From ac8f1d2d662f1baa28a4f534b90965b7b0b14d47 Mon Sep 17 00:00:00 2001 From: Matej Vasek Date: Thu, 5 May 2022 20:42:28 +0200 Subject: [PATCH] Allow empty credentials registry (#1004) * Allow empty credentials registry Useful when using local unsecured registry. Signed-off-by: Matej Vasek * Added tests for empty credentials registries Signed-off-by: Matej Vasek --- docker/creds/credentials.go | 37 +++++++++-------- docker/creds/credentials_test.go | 68 +++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/docker/creds/credentials.go b/docker/creds/credentials.go index d7aa74255..f848ee214 100644 --- a/docker/creds/credentials.go +++ b/docker/creds/credentials.go @@ -36,13 +36,6 @@ type VerifyCredentialsCallback func(ctx context.Context, registry string, creden // CheckAuth verifies that credentials are correct func CheckAuth(ctx context.Context, registry string, credentials docker.Credentials, trans http.RoundTripper) error { - serverAddress := registry - if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { - serverAddress = "https://" + serverAddress - } - - url := fmt.Sprintf("%s/v2/", serverAddress) - authenticator := &authn.Basic{ Username: credentials.Username, Password: credentials.Password, @@ -53,6 +46,13 @@ func CheckAuth(ctx context.Context, registry string, credentials docker.Credenti return err } + serverAddress := registry + if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { + serverAddress = reg.Scheme() + "://" + serverAddress + } + + url := fmt.Sprintf("%s/v2/", serverAddress) + tr, err := transport.NewWithContext(ctx, reg, authenticator, trans, nil) if err != nil { var transportErr *transport.Error @@ -189,6 +189,9 @@ func NewCredentialsProvider(opts ...Opt) docker.CredentialsProvider { dockerConfigPath := filepath.Join(home, ".docker", "config.json") var defaultCredentialLoaders = []CredentialsCallback{ + func(registry string) (docker.Credentials, error) { // empty credentials provider for unsecured registries + return docker.Credentials{}, nil + }, func(registry string) (docker.Credentials, error) { creds, err := config.GetCredentials(sys, registry) if err != nil { @@ -220,20 +223,22 @@ func (c *credentialsProvider) getCredentials(ctx context.Context, registry strin result, err = load(registry) - if err != nil && !errors.Is(err, errCredentialsNotFound) { + if err != nil { + if errors.Is(err, errCredentialsNotFound) { + continue + } return docker.Credentials{}, err } - if result != (docker.Credentials{}) { - err = c.verifyCredentials(ctx, registry, result) - if err == nil { - return result, nil - } else { - if !errors.Is(err, ErrUnauthorized) { - return docker.Credentials{}, err - } + err = c.verifyCredentials(ctx, registry, result) + if err == nil { + return result, nil + } else { + if !errors.Is(err, ErrUnauthorized) { + return docker.Credentials{}, err } } + } if c.promptForCredentials == nil { diff --git a/docker/creds/credentials_test.go b/docker/creds/credentials_test.go index c3b2bbde6..bea3a94ff 100644 --- a/docker/creds/credentials_test.go +++ b/docker/creds/credentials_test.go @@ -68,8 +68,15 @@ func Test_registryEquals(t *testing.T) { } func TestCheckAuth(t *testing.T) { - localhost, localhostTLS, stopServer := startServer(t) - defer stopServer() + + const ( + uname = "testuser" + pwd = "testpwd" + incorrectPwd = "badpwd" + ) + + localhost, localhostTLS, stopServer := startServer(t, uname, pwd) + t.Cleanup(stopServer) _, portTLS, err := net.SplitHostPort(localhostTLS) if err != nil { @@ -93,8 +100,8 @@ func TestCheckAuth(t *testing.T) { name: "correct credentials localhost no-TLS", args: args{ ctx: context.Background(), - username: "testuser", - password: "testpwd", + username: uname, + password: pwd, registry: localhost, }, wantErr: false, @@ -103,8 +110,8 @@ func TestCheckAuth(t *testing.T) { name: "correct credentials localhost", args: args{ ctx: context.Background(), - username: "testuser", - password: "testpwd", + username: uname, + password: pwd, registry: localhostTLS, }, wantErr: false, @@ -114,8 +121,8 @@ func TestCheckAuth(t *testing.T) { name: "correct credentials non-localhost", args: args{ ctx: context.Background(), - username: "testuser", - password: "testpwd", + username: uname, + password: pwd, registry: nonLocalhostTLS, }, wantErr: false, @@ -124,8 +131,8 @@ func TestCheckAuth(t *testing.T) { name: "incorrect credentials localhost no-TLS", args: args{ ctx: context.Background(), - username: "testuser", - password: "badpwd", + username: uname, + password: incorrectPwd, registry: localhost, }, wantErr: true, @@ -134,8 +141,8 @@ func TestCheckAuth(t *testing.T) { name: "incorrect credentials localhost", args: args{ ctx: context.Background(), - username: "testuser", - password: "badpwd", + username: uname, + password: incorrectPwd, registry: localhostTLS, }, wantErr: true, @@ -154,7 +161,17 @@ func TestCheckAuth(t *testing.T) { } } -func startServer(t *testing.T) (addr, addrTLS string, stopServer func()) { +func TestCheckAuthEmptyCreds(t *testing.T) { + + localhost, _, stopServer := startServer(t, "", "") + t.Cleanup(stopServer) + err := creds.CheckAuth(context.Background(), localhost, docker.Credentials{}, http.DefaultTransport) + if err != nil { + t.Error(err) + } +} + +func startServer(t *testing.T, uname, pwd string) (addr, addrTLS string, stopServer func()) { // TODO: this should be refactored to use OS-chosen ports so as not to // fail when a user is running a Function on the default port.) listener, err := net.Listen("tcp", "localhost:0") @@ -170,10 +187,14 @@ func startServer(t *testing.T) (addr, addrTLS string, stopServer func()) { addrTLS = listenerTLS.Addr().String() handler := http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + if uname == "" || pwd == "" { + resp.WriteHeader(http.StatusOK) + return + } // TODO add also test for token based auth resp.Header().Add("WWW-Authenticate", "basic") - if user, pwd, ok := req.BasicAuth(); ok { - if user == "testuser" && pwd == "testpwd" { + if u, p, ok := req.BasicAuth(); ok { + if u == uname && p == pwd { resp.WriteHeader(http.StatusOK) return } @@ -401,6 +422,23 @@ func TestNewCredentialsProvider(t *testing.T) { } } +func TestNewCredentialsProviderEmptyCreds(t *testing.T) { + credentialsProvider := creds.NewCredentialsProvider(creds.WithVerifyCredentials(func(ctx context.Context, registry string, credentials docker.Credentials) error { + if registry == "localhost:5000" && credentials == (docker.Credentials{}) { + return nil + } + t.Fatal("unreachable") + return nil + })) + c, err := credentialsProvider(context.Background(), "localhost:5000") + if err != nil { + t.Error(err) + } + if c != (docker.Credentials{}) { + t.Error("unexpected credentials") + } +} + func TestCredentialsProviderSavingFromUserInput(t *testing.T) { defer withCleanHome(t)()