From cb4fdc70d55c907c77a62bc6a7a3664abf84040f Mon Sep 17 00:00:00 2001 From: Sunny <darkowlzz@protonmail.com> Date: Wed, 14 Dec 2022 16:54:07 +0000 Subject: [PATCH] oci/auth: Fix ImageRegistryProvider for repo root When a repository root is provided, ImageRegistryProvider() fails to identify the registry host. The provided name.Reference for repository root is a Tag reference. name.ParseReference(), assumes that that given string is the name and registry host being empty defaults to index.docker.io. ImageRegistryProvider() now takes the full address, in addition to the name.Reference and uses the full address for analysis if there's no "/" in the address, which indicates that it's a repository root. This fixes login with repository root for all the providers, not just AWS. Signed-off-by: Sunny <darkowlzz@protonmail.com> --- oci/auth/login/login.go | 29 ++++++++++++++++++++--------- oci/auth/login/login_test.go | 13 +++++++++++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/oci/auth/login/login.go b/oci/auth/login/login.go index ae26ca37..7a72d531 100644 --- a/oci/auth/login/login.go +++ b/oci/auth/login/login.go @@ -18,6 +18,7 @@ package login import ( "context" + "strings" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" @@ -30,15 +31,25 @@ import ( // ImageRegistryProvider analyzes the provided registry and returns the identified // container image registry provider. -func ImageRegistryProvider(ref name.Reference) oci.Provider { - _, _, ok := aws.ParseRegistry(ref.Context().RegistryStr()) +func ImageRegistryProvider(url string, ref name.Reference) oci.Provider { + // If the url is a repository root address, use it to analyze. Else, derive + // the registry from the name reference. + // NOTE: This is because name.Reference of a repository root assumes that + // the reference is an image name and defaults to using index.docker.io as + // the registry host. + addr := strings.TrimSuffix(url, "/") + if strings.ContainsRune(addr, '/') { + addr = ref.Context().RegistryStr() + } + + _, _, ok := aws.ParseRegistry(addr) if ok { return oci.ProviderAWS } - if gcp.ValidHost(ref.Context().RegistryStr()) { + if gcp.ValidHost(addr) { return oci.ProviderGCP } - if azure.ValidHost(ref.Context().RegistryStr()) { + if azure.ValidHost(addr) { return oci.ProviderAzure } return oci.ProviderGeneric @@ -94,14 +105,14 @@ func (m *Manager) WithACRClient(c *azure.Client) *Manager { // Login performs authentication against a registry and returns the // authentication material. For generic registry provider, it is no-op. -func (m *Manager) Login(ctx context.Context, image string, ref name.Reference, opts ProviderOptions) (authn.Authenticator, error) { - switch ImageRegistryProvider(ref) { +func (m *Manager) Login(ctx context.Context, url string, ref name.Reference, opts ProviderOptions) (authn.Authenticator, error) { + switch ImageRegistryProvider(url, ref) { case oci.ProviderAWS: - return m.ecr.Login(ctx, opts.AwsAutoLogin, image) + return m.ecr.Login(ctx, opts.AwsAutoLogin, url) case oci.ProviderGCP: - return m.gcr.Login(ctx, opts.GcpAutoLogin, image, ref) + return m.gcr.Login(ctx, opts.GcpAutoLogin, url, ref) case oci.ProviderAzure: - return m.acr.Login(ctx, opts.AzureAutoLogin, image, ref) + return m.acr.Login(ctx, opts.AzureAutoLogin, url, ref) } return nil, nil } diff --git a/oci/auth/login/login_test.go b/oci/auth/login/login_test.go index aac1da5e..b23be1dc 100644 --- a/oci/auth/login/login_test.go +++ b/oci/auth/login/login_test.go @@ -20,6 +20,7 @@ import ( "context" "net/http" "net/http/httptest" + "strings" "testing" awssdk "github.com/aws/aws-sdk-go-v2/aws" @@ -40,18 +41,26 @@ func TestImageRegistryProvider(t *testing.T) { want oci.Provider }{ {"ecr", "012345678901.dkr.ecr.us-east-1.amazonaws.com/foo:v1", oci.ProviderAWS}, + {"ecr-root", "012345678901.dkr.ecr.us-east-1.amazonaws.com", oci.ProviderAWS}, + {"ecr-root with slash", "012345678901.dkr.ecr.us-east-1.amazonaws.com/", oci.ProviderAWS}, {"gcr", "gcr.io/foo/bar:v1", oci.ProviderGCP}, + {"gcr-root", "gcr.io", oci.ProviderGCP}, {"acr", "foo.azurecr.io/bar:v1", oci.ProviderAzure}, + {"acr-root", "foo.azurecr.io", oci.ProviderAzure}, {"docker.io", "foo/bar:v1", oci.ProviderGeneric}, + {"docker.io-root", "docker.io", oci.ProviderGeneric}, + {"library", "alpine", oci.ProviderGeneric}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - ref, err := name.ParseReference(tt.image) + // Trim suffix to allow parsing it as reference without modifying + // the given image address. + ref, err := name.ParseReference(strings.TrimSuffix(tt.image, "/")) g.Expect(err).ToNot(HaveOccurred()) - g.Expect(ImageRegistryProvider(ref)).To(Equal(tt.want)) + g.Expect(ImageRegistryProvider(tt.image, ref)).To(Equal(tt.want)) }) } }