Permalink
Browse files

Authenticated registry mirrors: handle prefix correctly

Pulling from a prefixed and auuthenticated registry mirror
didn't work in the past.

Let's assume the following configuration is in place:

  * registry.kube.lan is an authenticated registry using
    bearer tokens as authentication method
  * A mirror of quay.io/coreos/etcd is available at
    registry.kube.lan/quay.io/coreos/etcd
  * The docker engine has the following configuration:
    ```json
      "registries": [
         {
           "Prefix" : "quay.io",
           "Mirrors": [
              {
                "URL": "https://registry.kube.lan/quay.io"
              }
           ]
         }
      ]
    ```

With this setup in place doing:

```
docker pull quay.io/coreos/etcd:v3.1
```

Should result in:

  * Have the docker engine authenticate against `registry.kube.lan`
  * Have the authenticated docker engine pull
    `registry.kube.lan/quay.io/coreos/etcd:v3.1` without the user
    being aware of it.

The docker engine checks if a registry is authenticated by doing a
`GET` request against the `/v2/` endpoint. Without this patch the docker
engine made a request against `/prefix/v2/`; in this case it would issue
the `GET` request against `https://registry.kube.lan/quay.io/v2/`.
That results in a `404` error code, which is interpreted by the engine
as "the remote registry is not using authentication, plus it doesn't
support v2".

After the docker engine is aware of the registry being authentcated, it
will contact the authentication endpoint specified by the registry.
While doing that the docker engine will provide the following
information to the authentication service:

  * `scope`: this is the name of the registry. This patch ensures the right
    name of the registry is sent. In this case it would send `registry.kube.lan`
    instead of `registry.kube.lan/quay.io`.
  * `service`: the scope of the operatation. This patch ensures the `scope`
    sent to the authentication service uses the right repository. In this case
    it would send `repository:quay.io/coreos/etcd:pull` instead of
    `repository:coreos/etcd:pull`.

Sending a wrong `service` and `scope` would cause the authentication
service to issue a wrong bearer token (something that doesn't grant any
kind of privileges or grants them against resources different from the
ones available on the registry). Without this patch the registry would
issue a `404` code to the engine, causing it to move to the next mirror.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
  • Loading branch information...
flavio committed Jul 17, 2018
1 parent b3fcbdd commit 9129064d6d7e6647f27685f7f52d0645bdf2b4ca
Showing with 14 additions and 3 deletions.
  1. +14 −3 components/engine/distribution/registry.go
@@ -4,6 +4,9 @@ import (
"fmt"
"net"
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/docker/distribution"
@@ -60,6 +63,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
if endpoint.TrimHostname {
repoName = reference.Path(repoInfo.Name)
}
repoName = strings.TrimPrefix(path.Join(endpoint.URL.RequestURI(), repoName), "/")
direct := &net.Dialer{
Timeout: 30 * time.Second,
@@ -85,7 +89,15 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders)
authTransport := transport.NewTransport(base, modifiers...)
challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
regURL, err := url.Parse(fmt.Sprintf("%s://%s", endpoint.URL.Scheme, endpoint.URL.Host))
if err != nil {
return nil, foundVersion, fallbackError{
err: err,
confirmedV2: foundVersion,
transportOK: false,
}
}
challengeManager, foundVersion, err := registry.PingV2Registry(regURL, authTransport)
if err != nil {
transportOK := false
if responseErr, ok := err.(registry.PingResponseError); ok {
@@ -108,7 +120,6 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
Actions: actions,
Class: repoInfo.Class,
}
creds := registry.NewStaticCredentialStore(authConfig)
tokenHandlerOptions := auth.TokenHandlerOptions{
Transport: authTransport,
@@ -131,7 +142,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
}
}
repo, err = client.NewRepository(ctx, repoNameRef, endpoint.URL.String(), tr)
repo, err = client.NewRepository(ctx, repoNameRef, regURL.String(), tr)
if err != nil {
err = fallbackError{
err: err,

0 comments on commit 9129064

Please sign in to comment.