From bd2575cc4f16bdce1748e90bdef63823cff3a35d Mon Sep 17 00:00:00 2001 From: Matt Moore Date: Thu, 28 May 2015 20:46:20 -0700 Subject: [PATCH] Make the v2 logic fallback on v1 when v2 requests cannot be authorized. Signed-off-by: Matt Moore --- graph/pull.go | 4 ++++ graph/push.go | 4 ++++ registry/auth.go | 28 +++++++++++++++++++++++----- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/graph/pull.go b/graph/pull.go index b2a9ef1780..6cf61bdaa9 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -482,6 +482,10 @@ func (s *TagStore) pullV2Repository(r *registry.Session, out io.Writer, repoInfo if err != nil { return fmt.Errorf("error getting authorization: %s", err) } + if !auth.CanAuthorizeV2() { + return ErrV2RegistryUnavailable + } + var layersDownloaded bool if tag == "" { logrus.Debugf("Pulling tag list from V2 registry for %s", repoInfo.CanonicalName) diff --git a/graph/push.go b/graph/push.go index 817ef707fc..283af159a9 100644 --- a/graph/push.go +++ b/graph/push.go @@ -322,6 +322,9 @@ func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, o if err != nil { return fmt.Errorf("error getting authorization: %s", err) } + if !auth.CanAuthorizeV2() { + return ErrV2RegistryUnavailable + } for _, tag := range tags { logrus.Debugf("Pushing repository: %s:%s", repoInfo.CanonicalName, tag) @@ -549,6 +552,7 @@ func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) erro if err != ErrV2RegistryUnavailable { return fmt.Errorf("Error pushing to registry: %s", err) } + logrus.Debug("V2 registry is unavailable, falling back on V1") } if err := s.pushRepository(r, imagePushConfig.OutStream, repoInfo, localRepo, imagePushConfig.Tag, sf); err != nil { diff --git a/registry/auth.go b/registry/auth.go index 33f8fa0689..66b3438f22 100644 --- a/registry/auth.go +++ b/registry/auth.go @@ -74,6 +74,19 @@ func (auth *RequestAuthorization) getToken() (string, error) { return "", nil } +// Checks that requests to the v2 registry can be authorized. +func (auth *RequestAuthorization) CanAuthorizeV2() bool { + if len(auth.registryEndpoint.AuthChallenges) == 0 { + return true + } + scope := fmt.Sprintf("%s:%s:%s", auth.resource, auth.scope, strings.Join(auth.actions, ",")) + if _, err := loginV2(auth.authConfig, auth.registryEndpoint, scope); err != nil { + logrus.Debugf("Cannot authorize against V2 endpoint: %s", auth.registryEndpoint) + return false + } + return true +} + func (auth *RequestAuthorization) Authorize(req *http.Request) error { token, err := auth.getToken() if err != nil { @@ -91,7 +104,7 @@ func (auth *RequestAuthorization) Authorize(req *http.Request) error { func Login(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (string, error) { // Separates the v2 registry login logic from the v1 logic. if registryEndpoint.Version == APIVersion2 { - return loginV2(authConfig, registryEndpoint) + return loginV2(authConfig, registryEndpoint, "" /* scope */) } return loginV1(authConfig, registryEndpoint) } @@ -209,7 +222,7 @@ func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (stri // now, users should create their account through other means like directly from a web page // served by the v2 registry service provider. Whether this will be supported in the future // is to be determined. -func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (string, error) { +func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint, scope string) (string, error) { logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint) var ( err error @@ -217,13 +230,18 @@ func loginV2(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (stri ) for _, challenge := range registryEndpoint.AuthChallenges { - logrus.Debugf("trying %q auth challenge with params %s", challenge.Scheme, challenge.Parameters) + params := make(map[string]string, len(challenge.Parameters)+1) + for k, v := range challenge.Parameters { + params[k] = v + } + params["scope"] = scope + logrus.Debugf("trying %q auth challenge with params %v", challenge.Scheme, params) switch strings.ToLower(challenge.Scheme) { case "basic": - err = tryV2BasicAuthLogin(authConfig, challenge.Parameters, registryEndpoint) + err = tryV2BasicAuthLogin(authConfig, params, registryEndpoint) case "bearer": - err = tryV2TokenAuthLogin(authConfig, challenge.Parameters, registryEndpoint) + err = tryV2TokenAuthLogin(authConfig, params, registryEndpoint) default: // Unsupported challenge types are explicitly skipped. err = fmt.Errorf("unsupported auth scheme: %q", challenge.Scheme)