Skip to content

Commit 3f5d18f

Browse files
Merge pull request #396 from periklis/backport-pr-14752-6.0
[release-6.0] Backport PR grafana#14752
2 parents 7bff32d + 49da9ad commit 3f5d18f

File tree

13 files changed

+220
-21
lines changed

13 files changed

+220
-21
lines changed

‎operator/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## Main
22

3+
## Release 6.0.3
4+
5+
- [14752](https://github.com/grafana/loki/pull/14752) **periklis**: Add support for managed GCP WorkloadIdentity
6+
37
## Release 6.0.2
48

59
- [14613](https://github.com/grafana/loki/pull/14613) **xperimental**: fix(operator): Disable log level discovery for OpenShift tenancy modes

‎operator/bundle/community-openshift/manifests/loki-operator.clusterserviceversion.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ metadata:
150150
categories: OpenShift Optional, Logging & Tracing
151151
certified: "false"
152152
containerImage: docker.io/grafana/loki-operator:0.6.1
153-
createdAt: "2024-10-23T18:24:03Z"
153+
createdAt: "2024-11-18T12:58:47Z"
154154
description: The Community Loki Operator provides Kubernetes native deployment
155155
and management of Loki and related logging components.
156156
features.operators.openshift.io/disconnected: "true"
@@ -159,7 +159,7 @@ metadata:
159159
features.operators.openshift.io/tls-profiles: "true"
160160
features.operators.openshift.io/token-auth-aws: "true"
161161
features.operators.openshift.io/token-auth-azure: "true"
162-
features.operators.openshift.io/token-auth-gcp: "false"
162+
features.operators.openshift.io/token-auth-gcp: "true"
163163
operators.operatorframework.io/builder: operator-sdk-unknown
164164
operators.operatorframework.io/project_layout: go.kubebuilder.io/v4
165165
repository: https://github.com/grafana/loki/tree/main/operator

‎operator/bundle/community/manifests/loki-operator.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ metadata:
150150
categories: OpenShift Optional, Logging & Tracing
151151
certified: "false"
152152
containerImage: docker.io/grafana/loki-operator:0.6.1
153-
createdAt: "2024-10-23T18:24:01Z"
153+
createdAt: "2024-11-18T12:58:45Z"
154154
description: The Community Loki Operator provides Kubernetes native deployment
155155
and management of Loki and related logging components.
156156
operators.operatorframework.io/builder: operator-sdk-unknown

‎operator/bundle/openshift/manifests/loki-operator.clusterserviceversion.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ metadata:
150150
categories: OpenShift Optional, Logging & Tracing
151151
certified: "false"
152152
containerImage: quay.io/openshift-logging/loki-operator:0.1.0
153-
createdAt: "2024-10-23T18:24:05Z"
153+
createdAt: "2024-11-18T12:58:49Z"
154154
description: |
155155
The Loki Operator for OCP provides a means for configuring and managing a Loki stack for cluster logging.
156156
## Prerequisites and Requirements
@@ -166,7 +166,7 @@ metadata:
166166
features.operators.openshift.io/tls-profiles: "true"
167167
features.operators.openshift.io/token-auth-aws: "true"
168168
features.operators.openshift.io/token-auth-azure: "true"
169-
features.operators.openshift.io/token-auth-gcp: "false"
169+
features.operators.openshift.io/token-auth-gcp: "true"
170170
olm.skipRange: '>=5.8.0-0 <6.0.0'
171171
operatorframework.io/cluster-monitoring: "true"
172172
operatorframework.io/suggested-namespace: openshift-operators-redhat

‎operator/config/manifests/community-openshift/bases/loki-operator.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ metadata:
1616
features.operators.openshift.io/tls-profiles: "true"
1717
features.operators.openshift.io/token-auth-aws: "true"
1818
features.operators.openshift.io/token-auth-azure: "true"
19-
features.operators.openshift.io/token-auth-gcp: "false"
19+
features.operators.openshift.io/token-auth-gcp: "true"
2020
repository: https://github.com/grafana/loki/tree/main/operator
2121
support: Grafana Loki SIG Operator
2222
labels:

‎operator/config/manifests/openshift/bases/loki-operator.clusterserviceversion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ metadata:
2222
features.operators.openshift.io/tls-profiles: "true"
2323
features.operators.openshift.io/token-auth-aws: "true"
2424
features.operators.openshift.io/token-auth-azure: "true"
25-
features.operators.openshift.io/token-auth-gcp: "false"
25+
features.operators.openshift.io/token-auth-gcp: "true"
2626
olm.skipRange: '>=5.8.0-0 <6.0.0'
2727
operatorframework.io/cluster-monitoring: "true"
2828
operatorframework.io/suggested-namespace: openshift-operators-redhat

‎operator/internal/config/managed_auth.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package config
22

3-
import "os"
3+
import (
4+
"fmt"
5+
"os"
6+
)
47

58
type AWSEnvironment struct {
69
RoleARN string
@@ -13,9 +16,15 @@ type AzureEnvironment struct {
1316
Region string
1417
}
1518

19+
type GCPEnvironment struct {
20+
Audience string
21+
ServiceAccountEmail string
22+
}
23+
1624
type TokenCCOAuthConfig struct {
1725
AWS *AWSEnvironment
1826
Azure *AzureEnvironment
27+
GCP *GCPEnvironment
1928
}
2029

2130
func discoverTokenCCOAuthConfig() *TokenCCOAuthConfig {
@@ -28,6 +37,12 @@ func discoverTokenCCOAuthConfig() *TokenCCOAuthConfig {
2837
subscriptionID := os.Getenv("SUBSCRIPTIONID")
2938
region := os.Getenv("REGION")
3039

40+
// GCP
41+
projectNumber := os.Getenv("PROJECT_NUMBER")
42+
poolID := os.Getenv("POOL_ID")
43+
providerID := os.Getenv("PROVIDER_ID")
44+
serviceAccountEmail := os.Getenv("SERVICE_ACCOUNT_EMAIL")
45+
3146
switch {
3247
case roleARN != "":
3348
return &TokenCCOAuthConfig{
@@ -44,6 +59,20 @@ func discoverTokenCCOAuthConfig() *TokenCCOAuthConfig {
4459
Region: region,
4560
},
4661
}
62+
case projectNumber != "" && poolID != "" && providerID != "" && serviceAccountEmail != "":
63+
audience := fmt.Sprintf(
64+
"//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s",
65+
projectNumber,
66+
poolID,
67+
providerID,
68+
)
69+
70+
return &TokenCCOAuthConfig{
71+
GCP: &GCPEnvironment{
72+
Audience: audience,
73+
ServiceAccountEmail: serviceAccountEmail,
74+
},
75+
}
4776
}
4877

4978
return nil

‎operator/internal/handlers/internal/storage/secrets.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ var (
3333
errSecretUnknownSSEType = errors.New("unsupported SSE type (supported: SSE-KMS, SSE-S3)")
3434
errSecretHashError = errors.New("error calculating hash for secret")
3535

36-
errSecretUnknownCredentialMode = errors.New("unknown credential mode")
37-
errSecretUnsupportedCredentialMode = errors.New("combination of storage type and credential mode not supported")
36+
errSecretUnknownCredentialMode = errors.New("unknown credential mode")
3837

3938
errAzureManagedIdentityNoOverride = errors.New("when in managed mode, storage secret can not contain credentials")
4039
errAzureInvalidEnvironment = errors.New("azure environment invalid (valid values: AzureGlobal, AzureChinaCloud, AzureGermanCloud, AzureUSGovernment)")
@@ -47,6 +46,7 @@ var (
4746

4847
errGCPParseCredentialsFile = errors.New("gcp storage secret cannot be parsed from JSON content")
4948
errGCPWrongCredentialSourceFile = errors.New("credential source in secret needs to point to token file")
49+
errGCPInvalidCredentialsFile = errors.New("gcp credentials file contains invalid fields")
5050

5151
azureValidEnvironments = map[string]bool{
5252
"AzureGlobal": true,
@@ -355,6 +355,15 @@ func extractGCSConfigSecret(s *corev1.Secret, credentialMode lokiv1.CredentialMo
355355
}
356356

357357
switch credentialMode {
358+
case lokiv1.CredentialModeTokenCCO:
359+
if _, ok := s.Data[storage.KeyGCPServiceAccountKeyFilename]; ok {
360+
return nil, fmt.Errorf("%w: %s", errGCPInvalidCredentialsFile, "key.json must not be set for CredentialModeTokenCCO")
361+
}
362+
363+
return &storage.GCSStorageConfig{
364+
Bucket: string(bucket),
365+
WorkloadIdentity: true,
366+
}, nil
358367
case lokiv1.CredentialModeStatic:
359368
return &storage.GCSStorageConfig{
360369
Bucket: string(bucket),
@@ -380,12 +389,9 @@ func extractGCSConfigSecret(s *corev1.Secret, credentialMode lokiv1.CredentialMo
380389
WorkloadIdentity: true,
381390
Audience: audience,
382391
}, nil
383-
case lokiv1.CredentialModeTokenCCO:
384-
return nil, fmt.Errorf("%w: type: %s credentialMode: %s", errSecretUnsupportedCredentialMode, lokiv1.ObjectStorageSecretGCS, credentialMode)
385392
default:
393+
return nil, fmt.Errorf("%w: %s", errSecretUnknownCredentialMode, credentialMode)
386394
}
387-
388-
return nil, fmt.Errorf("%w: %s", errSecretUnknownCredentialMode, credentialMode)
389395
}
390396

391397
func extractS3ConfigSecret(s *corev1.Secret, credentialMode lokiv1.CredentialMode) (*storage.S3StorageConfig, error) {

‎operator/internal/handlers/internal/storage/secrets_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ func TestGCSExtract(t *testing.T) {
277277
type test struct {
278278
name string
279279
secret *corev1.Secret
280+
tokenAuth *corev1.Secret
281+
featureGates configv1.FeatureGates
280282
wantError string
281283
wantCredentialMode lokiv1.CredentialMode
282284
}
@@ -343,6 +345,45 @@ func TestGCSExtract(t *testing.T) {
343345
},
344346
wantCredentialMode: lokiv1.CredentialModeToken,
345347
},
348+
{
349+
name: "invalid for token CCO",
350+
featureGates: configv1.FeatureGates{
351+
OpenShift: configv1.OpenShiftFeatureGates{
352+
Enabled: true,
353+
TokenCCOAuthEnv: true,
354+
},
355+
},
356+
secret: &corev1.Secret{
357+
ObjectMeta: metav1.ObjectMeta{Name: "test"},
358+
Data: map[string][]byte{
359+
"bucketname": []byte("here"),
360+
"key.json": []byte("{\"type\": \"external_account\", \"audience\": \"\", \"service_account_id\": \"\"}"),
361+
},
362+
},
363+
wantError: "gcp credentials file contains invalid fields: key.json must not be set for CredentialModeTokenCCO",
364+
},
365+
{
366+
name: "valid for token CCO",
367+
featureGates: configv1.FeatureGates{
368+
OpenShift: configv1.OpenShiftFeatureGates{
369+
Enabled: true,
370+
TokenCCOAuthEnv: true,
371+
},
372+
},
373+
secret: &corev1.Secret{
374+
ObjectMeta: metav1.ObjectMeta{Name: "test"},
375+
Data: map[string][]byte{
376+
"bucketname": []byte("here"),
377+
},
378+
},
379+
tokenAuth: &corev1.Secret{
380+
ObjectMeta: metav1.ObjectMeta{Name: "token-auth-config"},
381+
Data: map[string][]byte{
382+
"service_account.json": []byte("{\"type\": \"external_account\", \"audience\": \"test\", \"service_account_id\": \"\"}"),
383+
},
384+
},
385+
wantCredentialMode: lokiv1.CredentialModeTokenCCO,
386+
},
346387
}
347388
for _, tst := range table {
348389
t.Run(tst.name, func(t *testing.T) {
@@ -352,7 +393,7 @@ func TestGCSExtract(t *testing.T) {
352393
Type: lokiv1.ObjectStorageSecretGCS,
353394
}
354395

355-
opts, err := extractSecrets(spec, tst.secret, nil, configv1.FeatureGates{})
396+
opts, err := extractSecrets(spec, tst.secret, tst.tokenAuth, tst.featureGates)
356397
if tst.wantError == "" {
357398
require.NoError(t, err)
358399
require.Equal(t, tst.wantCredentialMode, opts.CredentialMode)

‎operator/internal/manifests/openshift/credentialsrequest.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ func encodeProviderSpec(env *config.TokenCCOAuthConfig) (*runtime.RawExtension,
9898
AzureSubscriptionID: azure.SubscriptionID,
9999
AzureTenantID: azure.TenantID,
100100
}
101+
case env.GCP != nil:
102+
spec = &cloudcredentialv1.GCPProviderSpec{
103+
PredefinedRoles: []string{
104+
"roles/iam.workloadIdentityUser",
105+
"roles/storage.objectAdmin",
106+
},
107+
Audience: env.GCP.Audience,
108+
ServiceAccountEmail: env.GCP.ServiceAccountEmail,
109+
}
101110
}
102111

103112
encodedSpec, err := cloudcredentialv1.Codec.EncodeProviderSpec(spec.DeepCopyObject())

‎operator/internal/manifests/storage/configure.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ func ensureObjectStoreCredentials(p *corev1.PodSpec, opts Options) corev1.PodSpe
141141
volumes = append(volumes, saTokenVolume(opts))
142142
container.VolumeMounts = append(container.VolumeMounts, saTokenVolumeMount)
143143

144-
if opts.OpenShift.TokenCCOAuthEnabled() && opts.S3 != nil && opts.S3.STS {
144+
isSTS := opts.S3 != nil && opts.S3.STS
145+
isWIF := opts.GCS != nil && opts.GCS.WorkloadIdentity
146+
if opts.OpenShift.TokenCCOAuthEnabled() && (isSTS || isWIF) {
145147
volumes = append(volumes, tokenCCOAuthConfigVolume(opts))
146148
container.VolumeMounts = append(container.VolumeMounts, tokenCCOAuthConfigVolumeMount)
147149
}
@@ -223,8 +225,14 @@ func tokenAuthCredentials(opts Options) []corev1.EnvVar {
223225
envVarFromValue(EnvAzureFederatedTokenFile, ServiceAccountTokenFilePath),
224226
}
225227
case lokiv1.ObjectStorageSecretGCS:
226-
return []corev1.EnvVar{
227-
envVarFromValue(EnvGoogleApplicationCredentials, path.Join(secretDirectory, KeyGCPServiceAccountKeyFilename)),
228+
if opts.OpenShift.TokenCCOAuthEnabled() {
229+
return []corev1.EnvVar{
230+
envVarFromValue(EnvGoogleApplicationCredentials, path.Join(tokenAuthConfigDirectory, KeyGCPManagedServiceAccountKeyFilename)),
231+
}
232+
} else {
233+
return []corev1.EnvVar{
234+
envVarFromValue(EnvGoogleApplicationCredentials, path.Join(secretDirectory, KeyGCPServiceAccountKeyFilename)),
235+
}
228236
}
229237
default:
230238
return []corev1.EnvVar{}
@@ -326,7 +334,10 @@ func saTokenVolume(opts Options) corev1.Volume {
326334
audience = opts.Azure.Audience
327335
}
328336
case lokiv1.ObjectStorageSecretGCS:
329-
audience = opts.GCS.Audience
337+
audience = gcpDefaultAudience
338+
if opts.GCS.Audience != "" {
339+
audience = opts.GCS.Audience
340+
}
330341
}
331342
return corev1.Volume{
332343
Name: saTokenVolumeName,

0 commit comments

Comments
 (0)