diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 65bd2983..be1010e9 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -19,6 +19,12 @@ rules: - get - list - watch +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create - apiGroups: - source.toolkit.fluxcd.io resources: diff --git a/go.mod b/go.mod index 483d4649..6134ad00 100644 --- a/go.mod +++ b/go.mod @@ -24,15 +24,15 @@ require ( github.com/fluxcd/cli-utils v0.36.0-flux.13 github.com/fluxcd/pkg/apis/event v0.17.0 github.com/fluxcd/pkg/apis/meta v1.11.0 - github.com/fluxcd/pkg/auth v0.11.0 + github.com/fluxcd/pkg/auth v0.12.0 github.com/fluxcd/pkg/cache v0.9.0 - github.com/fluxcd/pkg/git v0.28.0 - github.com/fluxcd/pkg/git/gogit v0.30.0 + github.com/fluxcd/pkg/git v0.29.0 + github.com/fluxcd/pkg/git/gogit v0.31.0 github.com/fluxcd/pkg/gittestserver v0.17.0 github.com/fluxcd/pkg/helmtestserver v0.24.0 github.com/fluxcd/pkg/lockedfile v0.6.0 github.com/fluxcd/pkg/masktoken v0.7.0 - github.com/fluxcd/pkg/oci v0.47.0 + github.com/fluxcd/pkg/oci v0.48.0 github.com/fluxcd/pkg/runtime v0.59.0 github.com/fluxcd/pkg/sourceignore v0.12.0 github.com/fluxcd/pkg/ssh v0.18.0 diff --git a/go.sum b/go.sum index c0731334..4a832fbd 100644 --- a/go.sum +++ b/go.sum @@ -374,14 +374,14 @@ github.com/fluxcd/pkg/apis/event v0.17.0 h1:foEINE++pCJlWVhWjYDXfkVmGKu8mQ4BDBlb github.com/fluxcd/pkg/apis/event v0.17.0/go.mod h1:0fLhLFiHlRTDKPDXdRnv+tS7mCMIQ0fJxnEfmvGM/5A= github.com/fluxcd/pkg/apis/meta v1.11.0 h1:h8q95k6ZEK1HCfsLkt8Np3i6ktb6ZzcWJ6hg++oc9w0= github.com/fluxcd/pkg/apis/meta v1.11.0/go.mod h1:+son1Va60x2eiDcTwd7lcctbI6C+K3gM7R+ULmEq1SI= -github.com/fluxcd/pkg/auth v0.11.0 h1:1BC6fQ71lCLFKz7juGlvWq9ysR2HVl5JPOWoxy4RMWE= -github.com/fluxcd/pkg/auth v0.11.0/go.mod h1:BJVrbanLH0AoUBzOH7u016D21Zl3dvEd0AnAWVOo5Vs= +github.com/fluxcd/pkg/auth v0.12.0 h1:35o0ziYMLZVgJwNvJBGsv/wd903B2fMagcrnm1ptUjc= +github.com/fluxcd/pkg/auth v0.12.0/go.mod h1:gQD2VT5OhIR1E8ZTEsTaho3bDQZidr9P10smH/awcew= github.com/fluxcd/pkg/cache v0.9.0 h1:EGKfOLMG3fOwWnH/4Axl5xd425mxoQbZzlZoLfd8PDk= github.com/fluxcd/pkg/cache v0.9.0/go.mod h1:jMwabjWfsC5lW8hE7NM3wtGNwSJ38Javx6EKbEi7INU= -github.com/fluxcd/pkg/git v0.28.0 h1:by7XTOvj4ZUPH1alYMJtDCVryhHue+UfjhrnPuJt5vA= -github.com/fluxcd/pkg/git v0.28.0/go.mod h1:VPv6O3mYnYvn79LOdWAFCl4fE8o651cxW/p/yxBoq2g= -github.com/fluxcd/pkg/git/gogit v0.30.0 h1:tdKRT4EDV8Cc2tBX+bg4H4gdcND7M4OEl6DQy1jSJmo= -github.com/fluxcd/pkg/git/gogit v0.30.0/go.mod h1:UCm/fOBuvX43BNz7Rc61Sukp2gBG/qxlOASaBkwMFvc= +github.com/fluxcd/pkg/git v0.29.0 h1:MHQ4F53e6Xt8a/POkd/fiChgysnd/XqiuK7vOWXAXLk= +github.com/fluxcd/pkg/git v0.29.0/go.mod h1:Ygn+LfrK6Ok+85uiq6s3NWG5LcHS4KY7mzES2JDJsGY= +github.com/fluxcd/pkg/git/gogit v0.31.0 h1:A56cmtgJBkWAj+gXSOdhPMQVTx0VF91S0PUaqpMXN4g= +github.com/fluxcd/pkg/git/gogit v0.31.0/go.mod h1:ya8z22xTvAAdW12HycxKYv4S+G+lqu5Kx/LyO/jWz8Y= github.com/fluxcd/pkg/gittestserver v0.17.0 h1:JlBvWZQTDOI+np5Z+084m3DkeAH1hMusEybyRUDF63k= github.com/fluxcd/pkg/gittestserver v0.17.0/go.mod h1:E/40EmLoXcMqd6gLuLDC9F6KJxqHVGbBBeMNKk5XdxU= github.com/fluxcd/pkg/helmtestserver v0.24.0 h1:9sSfRG17GnDIup4sI8V+fdvKROtunU4JyIo34uvXq3Q= @@ -390,8 +390,8 @@ github.com/fluxcd/pkg/lockedfile v0.6.0 h1:64RRMiPv3ZK9Y4sjI8c78kZAdfEo+Sjr2iP8a github.com/fluxcd/pkg/lockedfile v0.6.0/go.mod h1:gpdUVm7+05NIT1ZvzuNnHfnT81OhZtIySlxxkZ68pXk= github.com/fluxcd/pkg/masktoken v0.7.0 h1:pitmyOg2pUVdW+nn2Lk/xqm2TaA08uxvOC0ns3sz6bM= github.com/fluxcd/pkg/masktoken v0.7.0/go.mod h1:Lc1uoDjO1GY6+YdkK+ZqqBIBWquyV58nlSJ5S1N1IYU= -github.com/fluxcd/pkg/oci v0.47.0 h1:eQ7syqy91Xcfd7Sgf64v5n+dfRAju/OBiXuOhZsgQAg= -github.com/fluxcd/pkg/oci v0.47.0/go.mod h1:XBnI8+T6YFnIW4uEFojg7iIgHjKH7LXMpZARXJ9qmZk= +github.com/fluxcd/pkg/oci v0.48.0 h1:iSK4JDM0nx9plSlOGx2aI4td6aQdV/awrfXK/bzI35I= +github.com/fluxcd/pkg/oci v0.48.0/go.mod h1:rnUC8EOpzQp4rugpmopYFMnG3+CR1wqEV3356gHUtSY= github.com/fluxcd/pkg/runtime v0.59.0 h1:3OrFkMJB39NcQ2vhhoxqls59sQVSn8U+thhyLbsQoA4= github.com/fluxcd/pkg/runtime v0.59.0/go.mod h1:MFbfyNyyoYRgPxpdwC9/dCOkzo7Yxhu/cQ9NKyhvqc0= github.com/fluxcd/pkg/sourceignore v0.12.0 h1:jCIe6d50rQ3wdXPF0+PhhqN0XrTRIq3upMomPelI8Mw= diff --git a/internal/controller/gitrepository_controller.go b/internal/controller/gitrepository_controller.go index 9e815fa2..045bb244 100644 --- a/internal/controller/gitrepository_controller.go +++ b/internal/controller/gitrepository_controller.go @@ -132,19 +132,17 @@ type GitRepositoryReconciler struct { Storage *Storage ControllerName string + TokenCache *cache.TokenCache requeueDependency time.Duration features map[string]bool patchOptions []patch.Option - - tokenCache *cache.TokenCache } type GitRepositoryReconcilerOptions struct { DependencyRequeueInterval time.Duration RateLimiter workqueue.TypedRateLimiter[reconcile.Request] - TokenCache *cache.TokenCache } // gitRepositoryReconcileFunc is the function type for all the @@ -164,8 +162,6 @@ func (r *GitRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, o r.features = features.FeatureGates() } - r.tokenCache = opts.TokenCache - return ctrl.NewControllerManagedBy(mgr). For(&sourcev1.GitRepository{}, builder.WithPredicates( predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}), @@ -689,14 +685,14 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 var authOpts []auth.Option - if r.tokenCache != nil { + if r.TokenCache != nil { involvedObject := cache.InvolvedObject{ Kind: sourcev1.GitRepositoryKind, Name: obj.GetName(), Namespace: obj.GetNamespace(), Operation: cache.OperationReconcile, } - authOpts = append(authOpts, auth.WithCache(*r.tokenCache, involvedObject)) + authOpts = append(authOpts, auth.WithCache(*r.TokenCache, involvedObject)) } if proxyURL != nil { @@ -726,7 +722,7 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 GitHubOpts: []github.OptFunc{ github.WithAppData(authData), github.WithProxyURL(proxyURL), - github.WithCache(r.tokenCache, sourcev1.GitRepositoryKind, + github.WithCache(r.TokenCache, sourcev1.GitRepositoryKind, obj.GetName(), obj.GetNamespace(), cache.OperationReconcile), }, } @@ -1150,7 +1146,7 @@ func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sour controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer) // Cleanup caches. - r.tokenCache.DeleteEventsForObject(sourcev1.GitRepositoryKind, + r.TokenCache.DeleteEventsForObject(sourcev1.GitRepositoryKind, obj.GetName(), obj.GetNamespace(), cache.OperationReconcile) // Stop reconciliation as the object is being deleted diff --git a/internal/controller/helmchart_controller.go b/internal/controller/helmchart_controller.go index 9e8f0b56..19d320ec 100644 --- a/internal/controller/helmchart_controller.go +++ b/internal/controller/helmchart_controller.go @@ -70,7 +70,6 @@ import ( "github.com/fluxcd/source-controller/internal/helm/chart" "github.com/fluxcd/source-controller/internal/helm/getter" "github.com/fluxcd/source-controller/internal/helm/repository" - "github.com/fluxcd/source-controller/internal/oci" soci "github.com/fluxcd/source-controller/internal/oci" scosign "github.com/fluxcd/source-controller/internal/oci/cosign" "github.com/fluxcd/source-controller/internal/oci/notation" @@ -1255,7 +1254,7 @@ func observeChartBuild(ctx context.Context, sp *patch.SerialPatcher, pOpts []pat if build.Complete() { conditions.Delete(obj, sourcev1.FetchFailedCondition) conditions.Delete(obj, sourcev1.BuildFailedCondition) - if build.VerifiedResult == oci.VerificationResultSuccess { + if build.VerifiedResult == soci.VerificationResultSuccess { conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of version %s", build.Version) } } diff --git a/internal/controller/ocirepository_controller.go b/internal/controller/ocirepository_controller.go index 2ae167bd..577cf863 100644 --- a/internal/controller/ocirepository_controller.go +++ b/internal/controller/ocirepository_controller.go @@ -51,6 +51,8 @@ import ( eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/auth" + "github.com/fluxcd/pkg/cache" "github.com/fluxcd/pkg/oci" "github.com/fluxcd/pkg/runtime/conditions" helper "github.com/fluxcd/pkg/runtime/controller" @@ -141,6 +143,7 @@ type OCIRepositoryReconciler struct { Storage *Storage ControllerName string + TokenCache *cache.TokenCache requeueDependency time.Duration patchOptions []patch.Option @@ -175,6 +178,7 @@ func (r *OCIRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, o // +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=ocirepositories/status,verbs=get;update;patch // +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=ocirepositories/finalizers,verbs=get;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// +kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create func (r *OCIRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) { start := time.Now() @@ -328,7 +332,7 @@ func (r *OCIRepositoryReconciler) reconcile(ctx context.Context, sp *patch.Seria // If this fails, it records v1beta2.FetchFailedCondition=True on the object and returns early. func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, obj *ociv1.OCIRepository, metadata *sourcev1.Artifact, dir string) (sreconcile.Result, error) { - var auth authn.Authenticator + var authenticator authn.Authenticator ctxTimeout, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration) defer cancel() @@ -363,9 +367,29 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch } if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != ociv1.GenericOCIProvider && ok { + var opts []auth.Option + if obj.Spec.ServiceAccountName != "" { + serviceAccount := client.ObjectKey{ + Name: obj.Spec.ServiceAccountName, + Namespace: obj.GetNamespace(), + } + opts = append(opts, auth.WithServiceAccount(serviceAccount, r.Client)) + } + if r.TokenCache != nil { + involvedObject := cache.InvolvedObject{ + Kind: ociv1.OCIRepositoryKind, + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + Operation: cache.OperationReconcile, + } + opts = append(opts, auth.WithCache(*r.TokenCache, involvedObject)) + } + if proxyURL != nil { + opts = append(opts, auth.WithProxyURL(*proxyURL)) + } var authErr error - auth, authErr = soci.OIDCAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider, proxyURL) - if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) { + authenticator, authErr = soci.OIDCAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider, opts...) + if authErr != nil { e := serror.NewGeneric( fmt.Errorf("failed to get credential from %s: %w", obj.Spec.Provider, authErr), sourcev1.AuthenticationFailedReason, @@ -386,7 +410,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch return sreconcile.ResultEmpty, e } - opts := makeRemoteOptions(ctx, transport, keychain, auth) + opts := makeRemoteOptions(ctx, transport, keychain, authenticator) // Determine which artifact revision to pull ref, err := r.getArtifactRef(obj, opts) @@ -446,7 +470,7 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch conditions.GetObservedGeneration(obj, sourcev1.SourceVerifiedCondition) != obj.Generation || conditions.IsFalse(obj, sourcev1.SourceVerifiedCondition) { - result, err := r.verifySignature(ctx, obj, ref, keychain, auth, transport, opts...) + result, err := r.verifySignature(ctx, obj, ref, keychain, authenticator, transport, opts...) if err != nil { provider := obj.Spec.Verify.Provider if obj.Spec.Verify.SecretRef == nil && obj.Spec.Verify.Provider == "cosign" { @@ -1225,6 +1249,10 @@ func (r *OCIRepositoryReconciler) reconcileDelete(ctx context.Context, obj *ociv // Remove our finalizer from the list controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer) + // Cleanup caches. + r.TokenCache.DeleteEventsForObject(ociv1.OCIRepositoryKind, + obj.GetName(), obj.GetNamespace(), cache.OperationReconcile) + // Stop reconciliation as the object is being deleted return sreconcile.ResultEmpty, nil } diff --git a/internal/controller/ocirepository_controller_test.go b/internal/controller/ocirepository_controller_test.go index ed511d42..93e34384 100644 --- a/internal/controller/ocirepository_controller_test.go +++ b/internal/controller/ocirepository_controller_test.go @@ -872,9 +872,9 @@ func TestOCIRepository_CertSecret(t *testing.T) { tlsSecretClientCert := corev1.Secret{ Data: map[string][]byte{ - oci.CACert: tlsCA, - oci.ClientCert: clientPublicKey, - oci.ClientKey: clientPrivateKey, + "caFile": tlsCA, + "certFile": clientPublicKey, + "keyFile": clientPrivateKey, }, } @@ -907,9 +907,9 @@ func TestOCIRepository_CertSecret(t *testing.T) { digest: pi.digest, certSecret: &corev1.Secret{ Data: map[string][]byte{ - oci.CACert: tlsCA, - oci.ClientCert: clientPublicKey, - oci.ClientKey: []byte("invalid-key"), + "caFile": tlsCA, + "certFile": clientPublicKey, + "keyFile": []byte("invalid-key"), }, }, expectreadyconition: false, diff --git a/internal/helm/getter/client_opts.go b/internal/helm/getter/client_opts.go index b586b41b..c08fb81d 100644 --- a/internal/helm/getter/client_opts.go +++ b/internal/helm/getter/client_opts.go @@ -24,7 +24,6 @@ import ( "os" "path" - "github.com/fluxcd/pkg/oci" "github.com/google/go-containerregistry/pkg/authn" helmgetter "helm.sh/helm/v3/pkg/getter" helmreg "helm.sh/helm/v3/pkg/registry" @@ -137,8 +136,8 @@ func GetClientOpts(ctx context.Context, c client.Client, obj *sourcev1.HelmRepos } } } else if obj.Spec.Provider != sourcev1beta2.GenericOCIProvider && obj.Spec.Type == sourcev1.HelmRepositoryTypeOCI && ociRepo { - authenticator, authErr := soci.OIDCAuth(ctx, obj.Spec.URL, obj.Spec.Provider, nil) - if authErr != nil && !errors.Is(authErr, oci.ErrUnconfiguredProvider) { + authenticator, authErr := soci.OIDCAuth(ctx, obj.Spec.URL, obj.Spec.Provider) + if authErr != nil { return nil, "", fmt.Errorf("failed to get credential from '%s': %w", obj.Spec.Provider, authErr) } if authenticator != nil { diff --git a/internal/oci/auth.go b/internal/oci/auth.go index cfbc684e..c917a0d9 100644 --- a/internal/oci/auth.go +++ b/internal/oci/auth.go @@ -18,13 +18,12 @@ package oci import ( "context" - "fmt" - "net/url" "strings" - "github.com/fluxcd/pkg/oci/auth/login" "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/name" + + "github.com/fluxcd/pkg/auth" + authutils "github.com/fluxcd/pkg/auth/utils" sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" ) @@ -41,22 +40,7 @@ func (a Anonymous) Resolve(_ authn.Resource) (authn.Authenticator, error) { } // OIDCAuth generates the OIDC credential authenticator based on the specified cloud provider. -func OIDCAuth(ctx context.Context, url, provider string, proxyURL *url.URL) (authn.Authenticator, error) { +func OIDCAuth(ctx context.Context, url, provider string, opts ...auth.Option) (authn.Authenticator, error) { u := strings.TrimPrefix(url, sourcev1.OCIRepositoryPrefix) - ref, err := name.ParseReference(u) - if err != nil { - return nil, fmt.Errorf("failed to parse URL '%s': %w", u, err) - } - - opts := login.ProviderOptions{} - switch provider { - case sourcev1.AmazonOCIProvider: - opts.AwsAutoLogin = true - case sourcev1.AzureOCIProvider: - opts.AzureAutoLogin = true - case sourcev1.GoogleOCIProvider: - opts.GcpAutoLogin = true - } - - return login.NewManager(login.WithProxyURL(proxyURL)).Login(ctx, u, ref, opts) + return authutils.GetArtifactRegistryCredentials(ctx, provider, u, opts...) } diff --git a/main.go b/main.go index 8d684285..a8c0f518 100644 --- a/main.go +++ b/main.go @@ -216,10 +216,10 @@ func main() { Metrics: metrics, Storage: storage, ControllerName: controllerName, + TokenCache: tokenCache, }).SetupWithManagerAndOptions(mgr, controller.GitRepositoryReconcilerOptions{ DependencyRequeueInterval: requeueDependency, RateLimiter: helper.GetRateLimiter(rateLimiterOptions), - TokenCache: tokenCache, }); err != nil { setupLog.Error(err, "unable to create controller", "controller", v1.GitRepositoryKind) os.Exit(1) @@ -278,6 +278,7 @@ func main() { Storage: storage, EventRecorder: eventRecorder, ControllerName: controllerName, + TokenCache: tokenCache, Metrics: metrics, }).SetupWithManagerAndOptions(mgr, controller.OCIRepositoryReconcilerOptions{ RateLimiter: helper.GetRateLimiter(rateLimiterOptions),