Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement private registry mirror support #34319

Closed
wants to merge 1 commit into from

Conversation

vrothberg
Copy link

@vrothberg vrothberg commented Jul 31, 2017

Add private-registry mirror support

Add support for mirroring private registries. The daemon.json config
can now be configured as exemplified below:

{
"registries": [
        {
	"Prefix": "docker.io/alpine",
	"Mirrors": [
		{
			"URL": "http://local-alpine-mirror.lan",
		}
	]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference). In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well. In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com". Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http. An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror. The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli fcastelli@suse.com
Signed-off-by: Valentin Rothberg vrothberg@suse.com

@vrothberg
Copy link
Author

Linking a related issue: distribution/distribution#1431

@thaJeztah
Copy link
Member

thaJeztah commented Jul 31, 2017

Looks like a duplicate of #32771, #21245, #19009, and #18915

Following earlier discussions / PR's on this subject, this needs a proper design first (see #19009 (comment)); I think this PR needs agreement on a design for this on the related issue: #18818

@thaJeztah thaJeztah marked this as a duplicate of #21245 Jul 31, 2017
@thaJeztah thaJeztah marked this as a duplicate of #32771 Jul 31, 2017
@vrothberg
Copy link
Author

vrothberg commented Aug 1, 2017

Hi @thaJeztah,

thanks for your comment.

Following earlier discussions / PR's on this subject, this needs a proper design first (see docker#19009 (comment)); I think this PR needs agreement on a design for this on the related issue: docker#18818

Shall I use this PR as a design proposal for #18818? We read through all related PRs before coming up with this solution, and believe that this PR matches the requirements. The most important part is that images can't be messed up as a mirror can't be used for more than one registry.

@cyphar
Copy link
Contributor

cyphar commented Aug 3, 2017

CI failure is caused by one of the test vectors not being updated with the new private-registry-mirrors member.

09:36:45 ----------------------------------------------------------------------
09:36:45 FAIL: docker_cli_events_unix_test.go:390: DockerDaemonSuite.TestDaemonEvents
09:36:45 
09:36:45 [dd85c3b245ad7] waiting for daemon to start
09:36:45 [dd85c3b245ad7] daemon started
09:36:45 
09:36:45 docker_cli_events_unix_test.go:431:
09:36:45     c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (allow-nondistributable-artifacts=[], cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
09:36:45 ... obtained string = "2017-07-31T09:36:42.951676711Z daemon reload 2JW5:JJ5Z:E3AL:VASX:7RZB:CVIG:YWKL:URZX:TD6V:2RXB:7OMD:BXWL (allow-nondistributable-artifacts=[], cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=02f072db8516, private-registry-mirrors={}, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)\n"
09:36:45 ... substring string = "daemon reload 2JW5:JJ5Z:E3AL:VASX:7RZB:CVIG:YWKL:URZX:TD6V:2RXB:7OMD:BXWL (allow-nondistributable-artifacts=[], cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=02f072db8516, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)"

@stevvooe
Copy link
Contributor

stevvooe commented Aug 8, 2017

@vrothberg Thanks for the great PR!

@thaJeztah As far as I remember, we still had concerns for this model due trust about mirrors vs origin, but I am not sure where we are on this today. Let's make sure the right people are involved to make a decision here.

@vrothberg Any reason we just don't accept these in the --registry-mirrors flag? What is the reasoning behind the separate flag?

@stevvooe
Copy link
Contributor

stevvooe commented Aug 8, 2017

@vikstrous PTAL

@vrothberg
Copy link
Author

@stevvooe Thanks a lot for your comments.

@vrothberg Any reason we just don't accept these in the --registry-mirrors flag? What is the reasoning behind the separate flag?

We figured it's more explicit and expressive to configure. But if you wish, we can change that and split private registry and mirror, for instance, with an '@' in --registry-mirror.

@stevvooe
Copy link
Contributor

stevvooe commented Aug 8, 2017

@vrothberg That is my question: is there a difference in behavior or policy, other than that these registries may need separate credentials?

@vrothberg
Copy link
Author

vrothberg commented Aug 9, 2017

@vrothberg That is my question: is there a difference in behavior or policy, other than that these registries may need separate credentials?

@stevvooe No, to my knowledge, there is no difference. From a mirror's perspective, it's just serving to a non-default and non-official registry URL.

@stevvooe
Copy link
Contributor

@vrothberg So, do you think a separate flag is really necessary?

@vrothberg
Copy link
Author

@vrothberg So, do you think a separate flag is really necessary?

@stevvooe No, not from a technical point of view. I prefer a separate flag because it is more explicit, but that's just matter of taste.

Do you want me to change the commit to use the --registry-mirror flag instead and split host and proxy with, for instance, an @?

@vrothberg
Copy link
Author

@stevvooe, @thaJeztah I'd love to push this PR forward. As far as I can see, the remaining open question is whether a dedicated flag for private-registry mirrors is really needed or not. There have been some concerns if it's actually possible to find a separator that wouldn't break a valid URL [1], which is indeed really tricky. However, using @ works as the daemon doesn't accept mirrors with http basic auth [2]. Keeping this in mind, shall I update this PR and remove the dedicated flag?

There is another open PR #34495 fixing the unfortunate fact that the daemon currently ignores invalid input, but there should be no conflict as soon as the error propagation and input validation work as expected.

[1] https://tools.ietf.org/html/rfc3986
[2] https://github.com/moby/moby/blob/master/registry/config.go#L329

@stevvooe
Copy link
Contributor

stevvooe commented Sep 1, 2017

@vrothberg I just LGTM'd #34495.

Do you have an example of the flag format?

@vikstrous Does this interfere with any other plans in this area?

@vrothberg
Copy link
Author

@vrothberg I just LGTM'd #34495.
Do you have an example of the flag format?

Thanks a lot, @stevvooe! In case we merge the semantics of private-registry mirrors with the --registry-mirror flag, we may split the mirror and the registry via mirror@registry. Currently, this should work as basic authentication (e.g., username:password@www.example.com) is not accepted (which is a good thing as passwords may be stored in clear text in the config.json). So if we go this path and decide to support basic authentication in the future, we will have a conflict with the mirror@registry syntax. Also if a user tries to use basic authentication now, it will be tricky to notify that this is not supported as we have to figure out if the specified string looks like registry@mirror or username:password@mirror.

So to the more I think about it, the safer I feel using a dedicated flag/config option for private-registry mirrors.

@vikstrous
Copy link
Contributor

There is one use case I was hoping to be able to solve that this proposal doesn't address. I think it would be valuable to be able to support mirroring hub within a sub-namespace on another registry. That way you can have a hub mirror AND local images without namespace conflicts. For, example, I would like to be able to pull someone/nginx and have that effectively rewritten to registry.example.com/hubmirror/someone/nginx so that I can configure registry.example.com as a pull-through cache for hub as well as a regular registry.

@flavio
Copy link
Contributor

flavio commented Sep 5, 2017

I'm against using separators because the final solution looks like a hack. I really appreciate the usage of a dictionary because it's more explicit.

A new flag was introduced to:

  • be able to use a dictionary
  • avoid breakage of the cli for existing users (do not force the existing flag to use a dictionary)
  • make the distinction between the central hub and third party registries explicit

@stevvooe
Copy link
Contributor

stevvooe commented Sep 5, 2017

@flavio @vrothberg Thanks for making the need for a new flag much clearer. I agree that this is fundamentally different than the existing registry-mirrors flag.

In practice, we are mapping a namespace, which was traditionally a registry host, to a registry host. Let's come up with at least a syntax/data structure that represents this problem.

Do we have a set of example use cases that we could build a table around?

@flavio
Copy link
Contributor

flavio commented Sep 7, 2017

@vrothberg and I are working on a concrete example with config files and such. We will come back with it by the beginning of next week.

@flavio
Copy link
Contributor

flavio commented Sep 13, 2017

Let's imagine the following scenario:

  • As a user I am consuming images built by the community that are hosted on different docker registries: the Docker Hub, Google Container Registry and Quay.
  • As a user I don't want to waste time and bandwitdh when running containers based on images that are not available on my docker hosts.
  • As a user I want all my docker hosts to pull the images from local docker registry mirrors running inside of my infrastructure.

This PR makes possible to satisfay this pretty common use case. The following sections will illustrate what the user has to do.

Run the local docker registry mirrors

The user has to run a vanilla docker distribution process per upstream registry that has to be mirrored.

In this case the user will have to run three local docker registry: one for the Docker Hub, one for Google Container Registry and one for Quay.

All the docker regitries have to be configured to act as pull through caches.

For the sake of this example let's assume the user will run these local mirrors:

  • hub-mirror.local.lan: mirror of Docker Hub
  • quay-mirror.local.lan: mirror of Quay
  • gcr-mirror.local.lan: mirror of Google Container Registry

This is the basic configuration the user has to provide to run the Quay mirror:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :443
  tls:
    certificate: /certs/domain.crt
    key: /certs/domain.key
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
proxy:
  remoteurl: https://quay.io

This is the basic configuration the user has to provide to run the Google Container Registry mirror:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :443
  tls:
    certificate: /certs/domain.crt
    key: /certs/domain.key
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
proxy:
  remoteurl: https://gcr.io

This is the basic configuration the user has to provide to run the Docker Hub mirror:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :443
  tls:
    certificate: /certs/domain.crt
    key: /certs/domain.key
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
proxy:
  remoteurl: https://registry-1.docker.io

Configure the docker hosts

On all the docker hosts the user has to run a version of the docker engine that includes this PR and create a /etc/docker/daemon.json like the following one:

{
  "registry-mirrors": [
    "https://hub-mirror.local.lan"
  ],
  "private-registry-mirrors": {
    "quay.io": "https://quay-mirror.local.lan",
    "gcr.io": "https://gcr-mirror.local.lan"
  }
}

Profit!

Once this is done all the docker pull requests against the Docker Hub, Google Container Registry and Quay will be directed against the local mirrors. These registries will serve the already cached images if they are still fresh, otherwise they will pull the images from "upstream" and serve them to the local docker hosts.

Some examples:

  • Get the latest busybox image from the Docker Hub using mirror-hub.local.lan as pull-through cache: docker pull busybox
  • Get the latest etcd image maintained by CoreOS and hosted on Quay using mirror-quay.local.lan as pull-through cache: docker pull quay.io/coreos/etcd
  • Get the v4 tag of the Kubernetes guestbook example hosted on Google Container Registry using mirror-gcr.local.lan as pull-through cache: docker pull gcr.io/google-samples/gb-frontend:v4

Final considerations

The code of this PR allows a pretty common scenario to be satisfied without:

  • Requiring changes to existing Dockerfile(s), compose files, kubernetes manifests,...
  • Breaking the existing command line interface and configuration format of the docker engine.
  • Requiring changes to the docker distribution project

The above example concentrates on a pretty common scenario made possible by this PR. Other use cases can now be achieved thanks to this PR.

For example users can now mirror docker registries operated by themselves: an organization running a central docker registry inside of a cloud region could then have multiple pull through caches inside of its regional offices.

@jgallucci32
Copy link

What about using DNS suffixes for the proxy cache to rewrite the URL using the subdomain as the prefix (similar to how Amazon S3 does it for buckets)? This would require a wildcard certificate and DNS entry, but that is typical for most K8s deployments these days.

For example:

  • Deploy Harbor using wildcard dns entry for *.harbor.domain to point to the ingress/loadbalancer
  • Create the project docker_proxy in Harbor
  • Have the ingress rewrite the URL docker_proxy.harbor.domain to harbor.domain/docker_proxy

This would allow Harbor to support both private registries and mirrors at the same time.

@gzm55
Copy link

gzm55 commented Nov 11, 2020

What about using DNS suffixes for the proxy cache to rewrite the URL using the subdomain as the prefix (similar to how Amazon S3 does it for buckets)? This would require a wildcard certificate and DNS entry, but that is typical for most K8s deployments these days.

For example:

  • Deploy Harbor using wildcard dns entry for *.harbor.domain to point to the ingress/loadbalancer
  • Create the project docker_proxy in Harbor
  • Have the ingress rewrite the URL docker_proxy.harbor.domain to harbor.domain/docker_proxy

This would allow Harbor to support both private registries and mirrors at the same time.

not only the url rewrite, but also require the correct rewrite of the bearer header.

@jgallucci32
Copy link

@gzm55 Here are some code samples I wrote and tested to get nginx to rewrite the bearer token for Harbor. This assumes the project name for proxy cache is called docker.io and proxy endpoint is docker-mirror.domain.local

# Put in `http {` block
----------------------
  # Map for proxy cache
  map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$host$suffix1;
  }
  map $args $new_args {
    ~^(?<prefix2>.*scope=repository%3A)(?<suffix2>(?!docker.io).*)$     ${prefix2}docker.io%2F$suffix2;
  }

# Put in second `server {` block
------------------------
    # Rewrite for proxy cache
    if ($host = 'docker-mirror-domain.local') {
      rewrite ^/v2/((?!docker.io).+)$ /v2/docker.io/$1 last;
      set $args $new_args;
    }
    
# Put in `location /v2/ {` block
--------------------------------
      # Modify headers for proxy cache
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $new_header always;

@gzm55
Copy link

gzm55 commented Nov 13, 2020

@gzm55 Here are some code samples I wrote and tested to get nginx to rewrite the bearer token for Harbor. This assumes the project name for proxy cache is called docker.io and proxy endpoint is docker-mirror.domain.local

# Put in `http {` block
----------------------
  # Map for proxy cache
  map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$host$suffix1;
  }
  map $args $new_args {
    ~^(?<prefix2>.*scope=repository%3A)(?<suffix2>(?!docker.io).*)$     ${prefix2}docker.io%2F$suffix2;
  }

# Put in second `server {` block
------------------------
    # Rewrite for proxy cache
    if ($host = 'docker-mirror-domain.local') {
      rewrite ^/v2/((?!docker.io).+)$ /v2/docker.io/$1 last;
      set $args $new_args;
    }
    
# Put in `location /v2/ {` block
--------------------------------
      # Modify headers for proxy cache
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $new_header always;

This should work, and the previous discussion gave some similar methods to play with url/header inside nginx.
Still hope this mr to be merged, then we do not need this kind of tricky work around.

cyphar pushed a commit to SUSE/docker that referenced this pull request Feb 10, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Feb 10, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Mar 3, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Apr 16, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Oct 6, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
robertgzr pushed a commit to balena-os/balena-engine that referenced this pull request Oct 11, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby/moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Nov 18, 2021
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Apr 14, 2022
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Jun 7, 2022
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
@ecki
Copy link

ecki commented Sep 11, 2022

Just setup a project in harbor as a pull through cache for your target instance and modify your docker pull command / pod specs to hit that proxy project instead.

this is useful (especially if you add some filtering/approval) but since it requires to change the container path it’s not the most transparent solution.

cyphar pushed a commit to SUSE/docker that referenced this pull request Dec 6, 2022
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
cyphar pushed a commit to SUSE/docker that referenced this pull request Dec 6, 2022
NOTE: This is a backport/downstream patch of the upstream pull-request
      for Moby, which is still subject to changes.  Please visit
      <moby#34319> for the current status.

Add support for mirroring private registries.  The daemon.json config
can now be configured as exemplified below:

```json
{
"registries": [
        {
        "Prefix": "docker.io/library/alpine",
        "Mirrors": [
                {
                        "URL": "http://local-alpine-mirror.lan"
                }
        ]
        },
        {
        "Prefix": "registry.suse.com",
        "Mirrors": [
                {
                        "URL": "https://remote.suse.mirror.com"
                }
        ]
        },
        {
        "Prefix": "http://insecure.registry.org:5000"
        }
],
"registry-mirrors": ["https://deprecated-mirror.com"]
}
```

With the new semantics, a mirror will be selected as an endpoint if the
specified prefix matches the prefix of the requested resource (e.g., an
image reference).  In the upper example, "local-alpine-mirror" will only
serve as a mirror for docker.io if the requested resource matches the
"alpine" prefix, such as "alpine:latest" or "alpine-foo/bar".

Furthermore, private registries can now be mirrored as well.  In the
example above, "remote.suse.mirror.com" will serve as a mirror for all
requests to "registry.suse.com".  Notice that if no http{s,} scheme is
specified, the URI will always default to https without fallback to
http.  An insecure registry can now be specified by adding the "http://"
scheme to the corresponding prefix.

Note that the configuration is sanity checked, so that a given mirror
can serve multiple prefixes if they all point to the same registry,
while a registry cannot simultaneously serve as a mirror.  The daemon
will warn in case the URI schemes of a registry and one of its mirrors
do not correspond.

This change deprecates the "insecure-regestries" and "registry-mirrors"
options, while the "insecure-registries" cannot be used simultaneously
with the new "registries", which doesn't allow a fallback from https to
http for security reasons.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Signed-off-by: Aleksa Sarai <asarai@suse.de>
@vrothberg
Copy link
Author

I think it's time to close the PR before it's hitting school age. There is a number of nice workarounds mentioned in the conversation above.

@vrothberg vrothberg closed this Feb 13, 2023
@cpuguy83
Copy link
Member

I actually have a branch I'm working on to add support for managing "streams" which we can use to implement auth negotiation with the client.

@ChristianCiach
Copy link

ChristianCiach commented Feb 13, 2023

@vrothberg I prefer an actual solution instead of having to use workarounds, no matter how nice they may be. As can be easily judged by the number of reactions to this issue, this issue is still valid. This PR (or a some variants) has been merged into numerous forks, and similar tools like podman properly support this use case, too. I still think docker should add support for mirrors other than those for docker hub.

Edit: Sorry. I am just noticing that you are the author of this PR, not a moby maintainer. I stand by what I said, but you are the wrong person to reply to :)

@joernott
Copy link

@ChristianCiach , thank you for your excellent solution.
Using your nginx default template with harbor v2.8.2, I ran into authentication issues which made docker ignore my proxy (unauthorized: authorize header needed to send HEAD to repository: authorize header needed to send HEAD to repository).

I solved this by creating a robot account in harbor and adding a new variable NGINX_UPSTREAM_AUTHORIZATION to the compose file. This variable contains the base64 encoded name of the robot and password like this.

echo 'robot$whatever:TokenGeneratedByHarbor'|base64

Note the single quotes to preserver the "$" in the robots name.

As I don't like removing passwords from ssl key files; I also added a variable NGINX_SSL_PASSWORD_FILE which I can use to provide the password to nginx.

My template looks like this now:

server {
    listen ${NGINX_PORT} ssl;
    server_name ${NGINX_SERVER_NAME};
    ssl_certificate ${NGINX_SSL_CERT_FILE};
    ssl_certificate_key ${NGINX_SSL_CERT_KEY_FILE};
    ssl_password_file ${NGINX_SSL_PASSWORD_FILE};

    # https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=intermediate&openssl=1.1.1d&guideline=5.6
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;


    resolver 127.0.0.11;

    location ~ ^/v2/(?<path>.*)$ {
        if ($path != "") {
            # inject project and source domain into uri
            set $path "${NGINX_UPSTREAM_DOCKER_PROJECT}/$path";
        }

        proxy_pass            ${NGINX_UPSTREAM_DOCKER_REGISTRY_URL}/v2/$path;
        proxy_cache           off;
        proxy_set_header      Host                            $http_host;   # required for docker client's sake
        proxy_set_header      X-Real-IP                       $remote_addr; # pass on real client's IP
        proxy_set_header      X-Forwarded-For                 $proxy_add_x_forwarded_for;
        proxy_set_header      X-Forwarded-Proto               $scheme;
        proxy_set_header      Authorization                   "Basic ${NGINX_UPSTREAM_AUTHORIZATION}";
        proxy_read_timeout    900;
        # prepare delegate auth
        proxy_hide_header     Www-Authenticate;
    }
    location ~ ^/service/ {
        if ($args ~* "^(.*)(scope=repository%3A)(.*)$") {
            # inject project and source domain into the auth scope
            set $args "$1$2${NGINX_UPSTREAM_DOCKER_PROJECT}%2F$3";
        }
        proxy_pass            ${NGINX_UPSTREAM_DOCKER_REGISTRY_URL}$uri$is_args$args;
        proxy_cache           off;
        proxy_set_header      Host                            $http_host;   # required for docker client's sake
        proxy_set_header      X-Real-IP                       $remote_addr; # pass on real client's IP
        proxy_set_header      X-Forwarded-For                 $proxy_add_x_forwarded_for;
        proxy_set_header      X-Forwarded-Proto               $scheme;
        proxy_set_header      Authorization                   "Basic ${NGINX_UPSTREAM_AUTHORIZATION}";
        proxy_read_timeout    900;
    }
}

My compose file looks like this now:

version: '3.8'

secrets:
  nginx_password_file:
    file: /etc/pki/tls/private/certificate.pass

services:
  dockerhub-proxy:
    image: 'nginx:latest'
    environment:
      NGINX_PORT: '443'
      NGINX_SERVER_NAME: 'harbor.company'
      NGINX_SSL_CERT_FILE: '/certs/certificate_joined.crt'
      NGINX_SSL_CERT_KEY_FILE: '/certs/certificate.key'
      NGINX_SSL_PASSWORD_FILE: '/run/secrets/nginx_password_file'
      NGINX_UPSTREAM_DOCKER_REGISTRY_URL: 'https://harbor.company'
      NGINX_UPSTREAM_DOCKER_PROJECT: 'dockerhub-proxy'
      NGINX_UPSTREAM_AUTHORIZATION: '<base64 encoded robot secret>'
    ports:
      - '4443:443'
    secrets:
      - nginx_password_file
    volumes:
      - '/etc/pki/tls/certs/certificate_joined.crt:/certs/certificate_joined.crt'
      - '/etc/pki/tls/private/certificate.key:/certs/certificate.key'
      - '/etc/nginx/templates/default.conf.template:/etc/nginx/templates/default.conf.template'

I didn't bother securing the robot credentials. Unlike the ssl certificate key, their use is quite limited.

@Becivells
Copy link

@joernott thanks

caddy config

echo 'robot$whatever:TokenGeneratedByHarbor'|base64
export HARBOR_AUTHORIZATION=cm9ib3Qkd2hhdGV2ZXI6VG9rZW5HZW5lcmF0ZWRCeUhhcmJvcgo=

or systemd

Environment=HARBOR_AUTHORIZATION=cm9ib3Qkd2hhdGV2ZXI6VG9rZW5HZW5lcmF0ZWRCeUhhcmJvcgo=
http://docker.company, https://docker.company {

@v2 path_regexp v2 ^/v2/(.*)$
    handle @v2 {
        rewrite * /v2/proxy/{http.regexp.v2.1}

        reverse_proxy http://harbor.company {
            header_up Host {http.request.host}   
            header_up X-Real-IP {http.request.remote}
            header_up Authorization "Basic {env.HARBOR_AUTHORIZATION}"
            header_down -Www-Authenticate
        }
    }


@service path_regexp service ^/service/
    handle @service {
        @modify_args {
            header_regexp Args Referer (.*)(scope=repository%3A)(.*)
        }
        handle @modify_args {
            rewrite * {path}?{http.regexp.Args.1}scope=repository%3Aproxy%2F{http.regexp.Args.3}
        }

        reverse_proxy http://harbor.company  {
            header_up Host {http.request.host} 
            header_up X-Real-IP {http.request.remote}
            header_up Authorization "Basic {env.HARBOR_AUTHORIZATION}"
            header_down -Www-Authenticate

        }
    }

}

@rmounce
Copy link

rmounce commented Jul 25, 2024

We had some success with this nginx template in front of Harbor (same compose config as Becivells).

  • The additional $path regex match avoids doubling up the project prefix when fetching images from the the Docker Hub library like docker pull docker-cache.internal.domain/ubuntu
  • The /service/ route isn't needed in conjunction with NGINX_UPSTREAM_AUTHORIZATION.
server {
    listen ${NGINX_PORT};
    server_name ${NGINX_SERVER_NAME};
    ssl_certificate ${NGINX_SSL_CERT_FILE};
    ssl_certificate_key ${NGINX_SSL_CERT_KEY_FILE};

    resolver 127.0.0.11;

    location ~ ^/v2/(?<path>.*)$ {
        if ($path !~ ^(${NGINX_UPSTREAM_DOCKER_PROJECT}(/.*)?)?$) {
            # inject project and source domain into uri
            set $path "${NGINX_UPSTREAM_DOCKER_PROJECT}/$path";
        }


        proxy_pass            ${NGINX_UPSTREAM_DOCKER_REGISTRY_URL}/v2/$path;
        proxy_cache           off;
        proxy_set_header      Host                            $http_host;   # required for docker client's sake
        proxy_set_header      X-Real-IP                       $remote_addr; # pass on real client's IP
        proxy_set_header      X-Forwarded-For                 $proxy_add_x_forwarded_for;
        proxy_set_header      X-Forwarded-Proto               $scheme;
        proxy_set_header      Authorization                   "Basic ${NGINX_UPSTREAM_AUTHORIZATION}";
        proxy_read_timeout    900;
        # prepare delegate auth
        proxy_hide_header     Www-Authenticate;
    }
}

Though it seems to be about 2x slower for cache hits than accessing Harbor directly. And then Harbor in turn seems to be about 2x slower than the official Docker 'distribution' registry's pull-through cache mode. We ended up keeping that as a cache and using Harbor only as a registry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/distribution status/failing-ci Indicates that the PR in its current state fails the test suite status/1-design-review
Projects
None yet
Development

Successfully merging this pull request may close these issues.