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 <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2023-06-26 12:13:48 +02:00
parent cdabfa2aa5
commit d2047b954e
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
2 changed files with 13 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import (
"strings" "strings"
manifesttypes "github.com/docker/cli/cli/manifest/types" manifesttypes "github.com/docker/cli/cli/manifest/types"
"github.com/docker/cli/cli/trust"
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
distributionclient "github.com/docker/distribution/registry/client" 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 { if err != nil {
return err return err
} }
repoEndpoint.actions = trust.ActionsPushAndPull
repo, err := c.getRepositoryForReference(ctx, targetRef, repoEndpoint) repo, err := c.getRepositoryForReference(ctx, targetRef, repoEndpoint)
if err != nil { if err != nil {
return err return err
@ -102,6 +104,7 @@ func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest
return digest.Digest(""), err return digest.Digest(""), err
} }
repoEndpoint.actions = trust.ActionsPushAndPull
repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint) repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint)
if err != nil { if err != nil {
return digest.Digest(""), err return digest.Digest(""), err
@ -151,7 +154,9 @@ func (c *client) getHTTPTransportForRepoEndpoint(ctx context.Context, repoEndpoi
c.authConfigResolver(ctx, repoEndpoint.info.Index), c.authConfigResolver(ctx, repoEndpoint.info.Index),
repoEndpoint.endpoint, repoEndpoint.endpoint,
repoEndpoint.Name(), repoEndpoint.Name(),
c.userAgent) c.userAgent,
repoEndpoint.actions,
)
return httpTransport, errors.Wrap(err, "failed to configure transport") return httpTransport, errors.Wrap(err, "failed to configure transport")
} }

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/docker/cli/cli/trust"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth"
"github.com/docker/distribution/registry/client/transport" "github.com/docker/distribution/registry/client/transport"
@ -17,6 +18,7 @@ import (
type repositoryEndpoint struct { type repositoryEndpoint struct {
info *registry.RepositoryInfo info *registry.RepositoryInfo
endpoint registry.APIEndpoint endpoint registry.APIEndpoint
actions []string
} }
// Name returns the repository name // 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 // 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 // get the http transport, this will be used in a client to upload manifest
base := &http.Transport{ base := &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
@ -98,8 +100,11 @@ func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.API
passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken} passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler)) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
} else { } else {
if len(actions) == 0 {
actions = trust.ActionsPullOnly
}
creds := registry.NewStaticCredentialStore(&authConfig) creds := registry.NewStaticCredentialStore(&authConfig)
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, "push", "pull") tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...)
basicHandler := auth.NewBasicHandler(creds) basicHandler := auth.NewBasicHandler(creds)
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
} }