diff --git a/go.mod b/go.mod index 2ff16014a..6d3d159f9 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( k8s.io/client-go v0.35.1 k8s.io/klog/v2 v2.130.1 k8s.io/kubectl v0.35.1 - oras.land/oras-go/v2 v2.6.0 + oras.land/oras-go/v2 v2.6.1-0.20251010150221-ba36eb50d8f7 sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/kustomize/kyaml v0.21.1 sigs.k8s.io/yaml v1.6.0 diff --git a/go.sum b/go.sum index 81c537cc6..e05c87a1b 100644 --- a/go.sum +++ b/go.sum @@ -505,8 +505,8 @@ k8s.io/kubectl v0.35.1 h1:zP3Er8C5i1dcAFUMh9Eva0kVvZHptXIn/+8NtRWMxwg= k8s.io/kubectl v0.35.1/go.mod h1:cQ2uAPs5IO/kx8R5s5J3Ihv3VCYwrx0obCXum0CvnXo= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= -oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= +oras.land/oras-go/v2 v2.6.1-0.20251010150221-ba36eb50d8f7 h1:az9gwaHiKjkUKXE9ZwFP1kx4d24cfLllB39B7CsJIfE= +oras.land/oras-go/v2 v2.6.1-0.20251010150221-ba36eb50d8f7/go.mod h1:Oi6NpQ3ClWC5wsv18PUbGtLaMIdKHD5atudhGfRI//0= sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= diff --git a/pkg/registry/client.go b/pkg/registry/client.go index f2bfd13b4..2ac9ee1d5 100644 --- a/pkg/registry/client.go +++ b/pkg/registry/client.go @@ -252,18 +252,12 @@ func (c *Client) Login(host string, options ...LoginOption) error { } reg.PlainHTTP = c.plainHTTP cred := auth.Credential{Username: c.username, Password: c.password} - c.authorizer.ForceAttemptOAuth2 = true reg.Client = c.authorizer ctx := context.Background() if err := reg.Ping(ctx); err != nil { - c.authorizer.ForceAttemptOAuth2 = false - if err := reg.Ping(ctx); err != nil { - return fmt.Errorf("authenticating to %q: %w", host, err) - } + return fmt.Errorf("authenticating to %q: %w", host, err) } - // Always restore to false after probing, to avoid forcing POST to token endpoints like GHCR. - c.authorizer.ForceAttemptOAuth2 = false key := credentials.ServerAddressFromRegistry(host) key = credentials.ServerAddressFromHostname(key) diff --git a/pkg/registry/client_test.go b/pkg/registry/client_test.go index 702dfff69..477de9f94 100644 --- a/pkg/registry/client_test.go +++ b/pkg/registry/client_test.go @@ -18,10 +18,6 @@ package registry import ( "io" - "net/http" - "net/http/httptest" - "path/filepath" - "strings" "testing" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -56,71 +52,6 @@ func TestTagManifestTransformsReferences(t *testing.T) { require.Error(t, err, "Should NOT find the reference with the original +") } -// Verifies that Login always restores ForceAttemptOAuth2 to false on success. -func TestLogin_ResetsForceAttemptOAuth2_OnSuccess(t *testing.T) { - t.Parallel() - - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/v2/" { - // Accept either HEAD or GET - w.WriteHeader(http.StatusOK) - return - } - http.NotFound(w, r) - })) - defer srv.Close() - - host := strings.TrimPrefix(srv.URL, "http://") - - credFile := filepath.Join(t.TempDir(), "config.json") - c, err := NewClient( - ClientOptWriter(io.Discard), - ClientOptCredentialsFile(credFile), - ) - if err != nil { - t.Fatalf("NewClient error: %v", err) - } - - if c.authorizer == nil || c.authorizer.ForceAttemptOAuth2 { - t.Fatal("expected ForceAttemptOAuth2 default to be false") - } - - // Call Login with plain HTTP against our test server - if err := c.Login(host, LoginOptPlainText(true), LoginOptBasicAuth("u", "p")); err != nil { - t.Fatalf("Login error: %v", err) - } - - if c.authorizer.ForceAttemptOAuth2 { - t.Error("ForceAttemptOAuth2 should be false after successful Login") - } -} - -// Verifies that Login restores ForceAttemptOAuth2 to false even when ping fails. -func TestLogin_ResetsForceAttemptOAuth2_OnFailure(t *testing.T) { - t.Parallel() - - // Start and immediately close, so connections will fail - srv := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) - host := strings.TrimPrefix(srv.URL, "http://") - srv.Close() - - credFile := filepath.Join(t.TempDir(), "config.json") - c, err := NewClient( - ClientOptWriter(io.Discard), - ClientOptCredentialsFile(credFile), - ) - if err != nil { - t.Fatalf("NewClient error: %v", err) - } - - // Invoke Login, expect an error but ForceAttemptOAuth2 must end false - _ = c.Login(host, LoginOptPlainText(true), LoginOptBasicAuth("u", "p")) - - if c.authorizer.ForceAttemptOAuth2 { - t.Error("ForceAttemptOAuth2 should be false after failed Login") - } -} - // TestWarnIfHostHasPath verifies that warnIfHostHasPath correctly detects path components. func TestWarnIfHostHasPath(t *testing.T) { t.Parallel() diff --git a/pkg/registry/transport.go b/pkg/registry/transport.go index f039a8159..e283d428e 100644 --- a/pkg/registry/transport.go +++ b/pkg/registry/transport.go @@ -52,24 +52,8 @@ type LoggingTransport struct { // NewTransport creates and returns a new instance of LoggingTransport func NewTransport(debug bool) *retry.Transport { - type cloner[T any] interface { - Clone() T - } - - // try to copy (clone) the http.DefaultTransport so any mutations we - // perform on it (e.g. TLS config) are not reflected globally - // follow https://github.com/golang/go/issues/39299 for a more elegant - // solution in the future - transport := http.DefaultTransport - if t, ok := transport.(cloner[*http.Transport]); ok { - transport = t.Clone() - } else if t, ok := transport.(cloner[http.RoundTripper]); ok { - // this branch will not be used with go 1.20, it was added - // optimistically to try to clone if the http.DefaultTransport - // implementation changes, still the Clone method in that case - // might not return http.RoundTripper... - transport = t.Clone() - } + // clone http.DefaultTransport so TLS config mutations don't affect the global default + var transport http.RoundTripper = http.DefaultTransport.(*http.Transport).Clone() if debug { transport = &LoggingTransport{RoundTripper: transport} }