Merge pull request #1872 from cappyzawa/feat/default-service-account-flag

[RFC-0010] Add default-service-account for lockdown
This commit is contained in:
Matheus Pimenta 2025-08-17 17:49:20 +01:00 committed by GitHub
commit e5189f6791
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 40 additions and 27 deletions

2
go.mod
View File

@ -28,7 +28,7 @@ require (
github.com/fluxcd/cli-utils v0.36.0-flux.14 github.com/fluxcd/cli-utils v0.36.0-flux.14
github.com/fluxcd/pkg/apis/event v0.18.0 github.com/fluxcd/pkg/apis/event v0.18.0
github.com/fluxcd/pkg/apis/meta v1.18.0 github.com/fluxcd/pkg/apis/meta v1.18.0
github.com/fluxcd/pkg/auth v0.21.0 github.com/fluxcd/pkg/auth v0.27.0
github.com/fluxcd/pkg/cache v0.10.0 github.com/fluxcd/pkg/cache v0.10.0
github.com/fluxcd/pkg/git v0.35.0 github.com/fluxcd/pkg/git v0.35.0
github.com/fluxcd/pkg/git/gogit v0.38.0 github.com/fluxcd/pkg/git/gogit v0.38.0

4
go.sum
View File

@ -378,8 +378,8 @@ github.com/fluxcd/pkg/apis/event v0.18.0 h1:PNbWk9gvX8gMIi6VsJapnuDO+giLEeY+6olL
github.com/fluxcd/pkg/apis/event v0.18.0/go.mod h1:7S/DGboLolfbZ6stO6dcDhG1SfkPWQ9foCULvbiYpiA= github.com/fluxcd/pkg/apis/event v0.18.0/go.mod h1:7S/DGboLolfbZ6stO6dcDhG1SfkPWQ9foCULvbiYpiA=
github.com/fluxcd/pkg/apis/meta v1.18.0 h1:ACHrMIjlcioE9GKS7NGk62KX4NshqNewr8sBwMcXABs= github.com/fluxcd/pkg/apis/meta v1.18.0 h1:ACHrMIjlcioE9GKS7NGk62KX4NshqNewr8sBwMcXABs=
github.com/fluxcd/pkg/apis/meta v1.18.0/go.mod h1:97l3hTwBpJbXBY+wetNbqrUsvES8B1jGioKcBUxmqd8= github.com/fluxcd/pkg/apis/meta v1.18.0/go.mod h1:97l3hTwBpJbXBY+wetNbqrUsvES8B1jGioKcBUxmqd8=
github.com/fluxcd/pkg/auth v0.21.0 h1:ckAQqP12wuptXEkMY18SQKWEY09m9e6yI0mEMsDV15M= github.com/fluxcd/pkg/auth v0.27.0 h1:DFsizUxt9ZDAc+z7+o7jcbtfaxRH55MRD/wdU4CXNCQ=
github.com/fluxcd/pkg/auth v0.21.0/go.mod h1:MXmpsXT97c874HCw5hnfqFUP7TsG8/Ss1vFrk8JccfM= github.com/fluxcd/pkg/auth v0.27.0/go.mod h1:YEAHpBFuW5oLlH9ekuJaQdnJ2Q3A7Ny8kha3WY7QMnY=
github.com/fluxcd/pkg/cache v0.10.0 h1:M+OGDM4da1cnz7q+sZSBtkBJHpiJsLnKVmR9OdMWxEY= github.com/fluxcd/pkg/cache v0.10.0 h1:M+OGDM4da1cnz7q+sZSBtkBJHpiJsLnKVmR9OdMWxEY=
github.com/fluxcd/pkg/cache v0.10.0/go.mod h1:pPXRzQUDQagsCniuOolqVhnAkbNgYOg8d2cTliPs7ME= github.com/fluxcd/pkg/cache v0.10.0/go.mod h1:pPXRzQUDQagsCniuOolqVhnAkbNgYOg8d2cTliPs7ME=
github.com/fluxcd/pkg/git v0.35.0 h1:mAauhsdfxNW4yQdXviVlvcN/uCGGG0+6p5D1+HFZI9w= github.com/fluxcd/pkg/git v0.35.0 h1:mAauhsdfxNW4yQdXviVlvcN/uCGGG0+6p5D1+HFZI9w=

View File

@ -860,14 +860,13 @@ func (r *BucketReconciler) setupCredentials(ctx context.Context, obj *sourcev1.B
// createBucketProvider creates a provider-specific bucket client using the given credentials and configuration. // createBucketProvider creates a provider-specific bucket client using the given credentials and configuration.
// It handles different bucket providers (AWS, GCP, Azure, generic) and returns the appropriate client. // It handles different bucket providers (AWS, GCP, Azure, generic) and returns the appropriate client.
func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *sourcev1.Bucket, creds *bucketCredentials) (BucketProvider, error) { func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *sourcev1.Bucket, creds *bucketCredentials) (BucketProvider, error) {
var authOpts []auth.Option authOpts := []auth.Option{
auth.WithClient(r.Client),
auth.WithServiceAccountNamespace(obj.GetNamespace()),
}
if obj.Spec.ServiceAccountName != "" { if obj.Spec.ServiceAccountName != "" {
serviceAccount := client.ObjectKey{ authOpts = append(authOpts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName))
Name: obj.Spec.ServiceAccountName,
Namespace: obj.GetNamespace(),
}
authOpts = append(authOpts, auth.WithServiceAccount(serviceAccount, r.Client))
} }
if r.TokenCache != nil { if r.TokenCache != nil {

View File

@ -38,6 +38,7 @@ import (
kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status" kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth"
"github.com/fluxcd/pkg/runtime/conditions" "github.com/fluxcd/pkg/runtime/conditions"
conditionscheck "github.com/fluxcd/pkg/runtime/conditions/check" conditionscheck "github.com/fluxcd/pkg/runtime/conditions/check"
"github.com/fluxcd/pkg/runtime/jitter" "github.com/fluxcd/pkg/runtime/jitter"
@ -1390,11 +1391,10 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) {
patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"), patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"),
} }
// Handle ObjectLevelWorkloadIdentity feature gate environment variable // Handle ObjectLevelWorkloadIdentity feature gate
if tt.disableObjectLevelWorkloadIdentity { if !tt.disableObjectLevelWorkloadIdentity {
t.Setenv("ENABLE_OBJECT_LEVEL_WORKLOAD_IDENTITY", "false") auth.EnableObjectLevelWorkloadIdentity()
} else if tt.serviceAccount != nil { t.Cleanup(auth.DisableObjectLevelWorkloadIdentity)
t.Setenv("ENABLE_OBJECT_LEVEL_WORKLOAD_IDENTITY", "true")
} }
tmpDir := t.TempDir() tmpDir := t.TempDir()

View File

@ -661,7 +661,10 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
switch provider := obj.GetProvider(); provider { switch provider := obj.GetProvider(); provider {
case sourcev1.GitProviderAzure: // If AWS or GCP are added in the future they can be added here separated by a comma. case sourcev1.GitProviderAzure: // If AWS or GCP are added in the future they can be added here separated by a comma.
getCreds = func() (*authutils.GitCredentials, error) { getCreds = func() (*authutils.GitCredentials, error) {
var opts []auth.Option opts := []auth.Option{
auth.WithClient(r.Client),
auth.WithServiceAccountNamespace(obj.GetNamespace()),
}
if obj.Spec.ServiceAccountName != "" { if obj.Spec.ServiceAccountName != "" {
// Check object-level workload identity feature gate. // Check object-level workload identity feature gate.
@ -672,11 +675,8 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "%s", err) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "%s", err)
return nil, err return nil, err
} }
serviceAccount := client.ObjectKey{ // Set ServiceAccountName only if explicitly specified
Name: obj.Spec.ServiceAccountName, opts = append(opts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName))
Namespace: obj.GetNamespace(),
}
opts = append(opts, auth.WithServiceAccount(serviceAccount, r.Client))
} }
if r.TokenCache != nil { if r.TokenCache != nil {

View File

@ -373,7 +373,11 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
} }
if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != "" && obj.Spec.Provider != sourcev1.GenericOCIProvider && ok { if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != "" && obj.Spec.Provider != sourcev1.GenericOCIProvider && ok {
var opts []auth.Option opts := []auth.Option{
auth.WithClient(r.Client),
auth.WithServiceAccountNamespace(obj.GetNamespace()),
}
if obj.Spec.ServiceAccountName != "" { if obj.Spec.ServiceAccountName != "" {
// Check object-level workload identity feature gate. // Check object-level workload identity feature gate.
if !auth.IsObjectLevelWorkloadIdentityEnabled() { if !auth.IsObjectLevelWorkloadIdentityEnabled() {
@ -382,11 +386,8 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch
err := fmt.Errorf(msgFmt, gate) err := fmt.Errorf(msgFmt, gate)
return sreconcile.ResultEmpty, serror.NewStalling(err, meta.FeatureGateDisabledReason) return sreconcile.ResultEmpty, serror.NewStalling(err, meta.FeatureGateDisabledReason)
} }
serviceAccount := client.ObjectKey{ // Set ServiceAccountName only if explicitly specified
Name: obj.Spec.ServiceAccountName, opts = append(opts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName))
Namespace: obj.GetNamespace(),
}
opts = append(opts, auth.WithServiceAccount(serviceAccount, r.Client))
} }
if r.TokenCache != nil { if r.TokenCache != nil {
involvedObject := cache.InvolvedObject{ involvedObject := cache.InvolvedObject{

View File

@ -3059,7 +3059,8 @@ func TestOCIRepository_objectLevelWorkloadIdentityFeatureGate(t *testing.T) {
g.Expect(stalledCondition.Reason).Should(Equal(meta.FeatureGateDisabledReason)) g.Expect(stalledCondition.Reason).Should(Equal(meta.FeatureGateDisabledReason))
g.Expect(stalledCondition.Message).Should(Equal("to use spec.serviceAccountName for provider authentication please enable the ObjectLevelWorkloadIdentity feature gate in the controller")) g.Expect(stalledCondition.Message).Should(Equal("to use spec.serviceAccountName for provider authentication please enable the ObjectLevelWorkloadIdentity feature gate in the controller"))
t.Setenv(auth.EnvVarEnableObjectLevelWorkloadIdentity, "true") auth.EnableObjectLevelWorkloadIdentity()
t.Cleanup(auth.DisableObjectLevelWorkloadIdentity)
g.Eventually(func() bool { g.Eventually(func() bool {
if err := testEnv.Get(ctx, key, resultobj); err != nil { if err := testEnv.Get(ctx, key, resultobj); err != nil {

12
main.go
View File

@ -121,6 +121,7 @@ func main() {
artifactRetentionRecords int artifactRetentionRecords int
artifactDigestAlgo string artifactDigestAlgo string
tokenCacheOptions pkgcache.TokenFlags tokenCacheOptions pkgcache.TokenFlags
defaultServiceAccount string
) )
flag.StringVar(&metricsAddr, "metrics-addr", envOrDefault("METRICS_ADDR", ":8080"), flag.StringVar(&metricsAddr, "metrics-addr", envOrDefault("METRICS_ADDR", ":8080"),
@ -159,6 +160,8 @@ func main() {
"The maximum number of artifacts to be kept in storage after a garbage collection.") "The maximum number of artifacts to be kept in storage after a garbage collection.")
flag.StringVar(&artifactDigestAlgo, "artifact-digest-algo", intdigest.Canonical.String(), flag.StringVar(&artifactDigestAlgo, "artifact-digest-algo", intdigest.Canonical.String(),
"The algorithm to use to calculate the digest of artifacts.") "The algorithm to use to calculate the digest of artifacts.")
flag.StringVar(&defaultServiceAccount, auth.ControllerFlagDefaultServiceAccount,
"", "Default service account to use for workload identity when not specified in resources.")
clientOptions.BindFlags(flag.CommandLine) clientOptions.BindFlags(flag.CommandLine)
logOptions.BindFlags(flag.CommandLine) logOptions.BindFlags(flag.CommandLine)
@ -173,6 +176,10 @@ func main() {
logger.SetLogger(logger.NewLogger(logOptions)) logger.SetLogger(logger.NewLogger(logOptions))
if defaultServiceAccount != "" {
auth.SetDefaultServiceAccount(defaultServiceAccount)
}
if err := featureGates.WithLogger(setupLog).SupportedFeatures(features.FeatureGates()); err != nil { if err := featureGates.WithLogger(setupLog).SupportedFeatures(features.FeatureGates()); err != nil {
setupLog.Error(err, "unable to load feature gates") setupLog.Error(err, "unable to load feature gates")
os.Exit(1) os.Exit(1)
@ -186,6 +193,11 @@ func main() {
auth.EnableObjectLevelWorkloadIdentity() auth.EnableObjectLevelWorkloadIdentity()
} }
if auth.InconsistentObjectLevelConfiguration() {
setupLog.Error(auth.ErrInconsistentObjectLevelConfiguration, "invalid configuration")
os.Exit(1)
}
if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil { if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil {
setupLog.Error(err, "unable to set global jitter") setupLog.Error(err, "unable to set global jitter")
os.Exit(1) os.Exit(1)