From 3c6c0bce1cacba9eeb9378230a63dfe0f34721c7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 26 Jun 2023 12:13:48 +0200 Subject: [PATCH] cli/registry/client: set actions when authn with token When using a personal access token, Docker Hub produces an error if actions are requested beyond the token's allowed actions. This resulted in errors when using a PAT with limited permissions to do a "docker manifest inspect". This patch sets actions to "pull" only by default, and requests "push" action for requests that need it. To verify: - create a PAT with limited access (read-only) - log in with your username and the PAT as password Before this patch: docker manifest inspect ubuntu:latest Get "https://registry-1.docker.io/v2/library/ubuntu/manifests/latest": unauthorized: access token has insufficient scopes With this patch applied: docker manifest inspect ubuntu:latest { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests": [ { "mediaType": "application/vnd.oci.image.manifest.v1+json", "size": 424, "digest": "sha256:56887c5194fddd8db7e36ced1c16b3569d89f74c801dc8a5adbf48236fb34564", "platform": { "architecture": "amd64", "os": "linux" } }, { "mediaType": "application/vnd.oci.image.manifest.v1+json", "size": 424, "digest": "sha256:c835a4f2a632bc91a2b494e871549f0dd83f2966c780e66435774e77e048ddf0", "platform": { "architecture": "arm", "os": "linux", "variant": "v7" } } ] } Signed-off-by: Sebastiaan van Stijn (cherry picked from commit d2047b954e63b1a6219c4d8891c357bfea9df5cc) Signed-off-by: Sebastiaan van Stijn --- cli/registry/client/client.go | 7 ++++++- cli/registry/client/endpoint.go | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/registry/client/client.go b/cli/registry/client/client.go index 86fc775684..1c33a836f0 100644 --- a/cli/registry/client/client.go +++ b/cli/registry/client/client.go @@ -7,6 +7,7 @@ import ( "strings" manifesttypes "github.com/docker/cli/cli/manifest/types" + "github.com/docker/cli/cli/trust" "github.com/docker/distribution" "github.com/docker/distribution/reference" distributionclient "github.com/docker/distribution/registry/client" @@ -77,6 +78,7 @@ func (c *client) MountBlob(ctx context.Context, sourceRef reference.Canonical, t if err != nil { return err } + repoEndpoint.actions = trust.ActionsPushAndPull repo, err := c.getRepositoryForReference(ctx, targetRef, repoEndpoint) if err != nil { return err @@ -102,6 +104,7 @@ func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest return digest.Digest(""), err } + repoEndpoint.actions = trust.ActionsPushAndPull repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint) if err != nil { return digest.Digest(""), err @@ -151,7 +154,9 @@ func (c *client) getHTTPTransportForRepoEndpoint(ctx context.Context, repoEndpoi c.authConfigResolver(ctx, repoEndpoint.info.Index), repoEndpoint.endpoint, repoEndpoint.Name(), - c.userAgent) + c.userAgent, + repoEndpoint.actions, + ) return httpTransport, errors.Wrap(err, "failed to configure transport") } diff --git a/cli/registry/client/endpoint.go b/cli/registry/client/endpoint.go index c6987badb3..6d0e920c5b 100644 --- a/cli/registry/client/endpoint.go +++ b/cli/registry/client/endpoint.go @@ -6,6 +6,7 @@ import ( "net/http" "time" + "github.com/docker/cli/cli/trust" "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/transport" @@ -17,6 +18,7 @@ import ( type repositoryEndpoint struct { info *registry.RepositoryInfo endpoint registry.APIEndpoint + actions []string } // Name returns the repository name @@ -74,7 +76,7 @@ func getDefaultEndpointFromRepoInfo(repoInfo *registry.RepositoryInfo) (registry } // getHTTPTransport builds a transport for use in communicating with a registry -func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.APIEndpoint, repoName string, userAgent string) (http.RoundTripper, error) { +func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.APIEndpoint, repoName, userAgent string, actions []string) (http.RoundTripper, error) { // get the http transport, this will be used in a client to upload manifest base := &http.Transport{ Proxy: http.ProxyFromEnvironment, @@ -98,8 +100,11 @@ func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.API passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) } else { + if len(actions) == 0 { + actions = trust.ActionsPullOnly + } creds := registry.NewStaticCredentialStore(&authConfig) - tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, "push", "pull") + tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...) basicHandler := auth.NewBasicHandler(creds) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) }