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

Proxy cache #8082

Closed
michmike opened this issue Jun 20, 2019 · 36 comments
Closed

Proxy cache #8082

michmike opened this issue Jun 20, 2019 · 36 comments
Assignees
Labels
area/proxy-cache kind/requirement New feature or idea on top of harbor

Comments

@michmike
Copy link
Contributor

enable proxy cache for Harbor.

This is very useful in situations of intermittent network connectivity where you still need access to local images. the expectation is that the network will go off in ROBO or IoT scenarios

Of course, the standard use case of reducing redundant image pulls across the network applies here

@reasonerjt reasonerjt added the kind/requirement New feature or idea on top of harbor label Jun 21, 2019
@xaleeks
Copy link
Contributor

xaleeks commented Jul 11, 2019

@michmike Is this referring to deploying a worker instance close to the edge nodes functioning specifically as a local registry mirror for a central Harbor and have the edge daemons pointing to it for local pulls? or is Harbor being used as proxy cache to pull from other registries like Docker hub and such? I mean is this this linking Harbor instances or w/ rest of the registry ecosystem.

somethings to think about

  • once pulled, how does long is it kept in cache for other clients before cleaned, there should be some LRU (least recently used) algorithm for determination
  • 'subsequent' pulls of any particular image can be based on just the image manifest
  • user pulling image needs to be properly authenticated and authorization checked before redirected to the local cache
  • will this be able to work w/ rbac
  • when to use this vs deploying another harbor cluster and relying on regular replication

@salcinad
Copy link

salcinad commented Jul 17, 2019

@michmike
are you referring to the: https://github.com/goharbor/harbor/blob/master/contrib/Configure_mirror.md

our need is that we don't pull Images from "registry-1.docker.io" (from Docker hub in Internet) but from internal Harbor Register which will act as Proxy/Cache ( for example this Option is available in Nexus 3), which will Cache and if not cached pull Images from Docker hub and provide to user.

@michmike
Copy link
Contributor Author

proxy cache is a feature of registry v2. this is what i meant - https://mtijhof.wordpress.com/2018/07/23/using-nexus-oss-as-a-proxy-cache-for-docker-images/

@michmike
Copy link
Contributor Author

we of course don't have to restrict the Harbor capabilities, so it could pull images as a cache from any type of replication target we can support

@stonezdj
Copy link
Contributor

proxy cache is a feature of registry v2. this is what i meant - https://mtijhof.wordpress.com/2018/07/23/using-nexus-oss-as-a-proxy-cache-for-docker-images/

The pull through proxy only works for docker hub, it doesn't work for other registry repository.

@xaleeks
Copy link
Contributor

xaleeks commented Aug 13, 2019

@stonezdj yes it seems the docker registry's own proxy mechanism still only works for docker hub. There's a pr on supporting other self hosted registries but still unmerged after 2 years
moby/moby#34319

@xaleeks
Copy link
Contributor

xaleeks commented May 2, 2020

PRD:
https://docs.google.com/document/d/15CRHZuxfw9cm3booRVIToZrkc3Emqi-6Z26-AlmAWuE/edit
ping me for access

@sadoMasupilami
Copy link

As docker hub is soon enforcing strict image pull limits this feature is very important I think.

https://www.docker.com/blog/scaling-docker-to-serve-millions-more-developers-network-egress/

@gregsidelinger
Copy link
Contributor

Is there anyway to configure harbor 2.1 as a transparent docker hub mirror? I can only make 2.1 pull from the upstream by doing things like this docker pull mycache/library/tomcat:latest. I was hoping I could make it work like this docker pull tomcat:latest by pointing my registry-mirror setting to my harbor install.

@jgallucci32
Copy link

@hwchiu The first 2 lines look correct. You get HTTP/401 for /v2 which then redirects to get the bearer token which returns HTTP/200. The third request is HTTP/404 meaning URL is not found.

You will need to first verify your proxy cache is working by attempting a pull-through request like this

docker pull harbor.domain.local/docker.io/library/busybox:latest

If that is successful, then try to get the manifest directly using a web browser with the DNS alias you use for the mirror

https://harbor-mirror.domain.local/docker.io/library/busybox/manifests/latest

This will test if you can get to the image in the proxy cache using the DNS alias with the full path to the image. Unfortunately nginx logging in K8s is not very good (or at least I haven't figured out how to get good debugging yet)

@hwchiu
Copy link

hwchiu commented Nov 18, 2020

@jgallucci32
Thanks for your help, I follow your steps to verify the function, yes, I can pull the image in step (1) and I can see the image is created in the Harbor's UI.
However, when I open the URL on the second step, it shows the 404 Page not found.
but, at least I have a new way to test my nginx config.
Thanks for your help.

@hwchiu
Copy link

hwchiu commented Nov 18, 2020

@jgallucci32
Fixed, I put the config in the wrong server section.
Thanks for your help again.

@jgallucci32
Copy link

@hwchiu @bagel-dawg I did some refactoring to use map variables to make it more clear what you need to set and reduces the changes made. I also added an improvement where the DNS suffix can be used in the form of <harbor_project>.harbor-mirror.domain.local so you only have to modify nginx.conf once and can configure it for X number of projects (this is very helpful with deployments with containerd which can support multiple mirrors)

Harbor can act as both a transparent proxy and Private Registry for multiple repositories:

  1. Create a Proxy Cache project (i.e. docker.io) as pull-through cache to Docker Hub
  2. Create DNS alias (i.e. docker-mirror.domain.local) which points to the DNS name of Harbor
  3. Create wildcard DNS alias (i.e. *.docker-mirror.domainlocal) which points to the DNS name of Harbor
  4. Modify the nginx.conf file for the harbor-nginx pod to rewrite the URLs for /v2 and /service when using the DNS alias
  5. Configure Docker daemon to use DNS alias as registry-mirror (or use DNS subdomain of Harbor project) (NOTE: You must specify "insecure-registries" if your SSL cert is not configured for wildcard DNS subdomains)

Modifications to nginx.conf

  1. Put in http { block
  # Map for proxy cache
  map $host $harbor_project {
    default 0;
    ~^docker-mirror.domain.local$ docker.io;
    ~^(?<project>.+).docker-mirror.domain.local$ $project;
  }
  map $request_uri $new_uri {
    ~^/v2/(.+)$ /v2/$harbor_project/$1;
  }
  map $args $new_args {
    ~^(?<prefix2>.*scope=repository%3A)(?<suffix2>.*)$     ${prefix2}${harbor_project}%2F$suffix2;
  }
  map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$host$suffix1;
  }
  1. Put in second server { block
    # Rewrite for proxy cache
    if ($harbor_project != 0) {
      rewrite ^/v2/(.+)$ $new_uri;
      set $args $new_args;
    }
  1. Put in location /v2/ { block
      # Modify headers for proxy cache
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $new_header always;

Please test this out and let me know if you run into any issues.

@jgallucci32
Copy link

@hwchiu @bagel-dawg I created a new issue (feature request) to track this and plan to work this into a PR for review #13579

@hwchiu
Copy link

hwchiu commented Nov 23, 2020

@hwchiu @bagel-dawg I did some refactoring to use map variables to make it more clear what you need to set and reduces the changes made. I also added an improvement where the DNS suffix can be used in the form of <harbor_project>.harbor-mirror.domain.local so you only have to modify nginx.conf once and can configure it for X number of projects (this is very helpful with deployments with containerd which can support multiple mirrors)

Harbor can act as both a transparent proxy and Private Registry for multiple repositories:

  1. Create a Proxy Cache project (i.e. docker.io) as pull-through cache to Docker Hub
  2. Create DNS alias (i.e. docker-mirror.domain.local) which points to the DNS name of Harbor
  3. Create wildcard DNS alias (i.e. *.docker-mirror.domainlocal) which points to the DNS name of Harbor
  4. Modify the nginx.conf file for the harbor-nginx pod to rewrite the URLs for /v2 and /service when using the DNS alias
  5. Configure Docker daemon to use DNS alias as registry-mirror (or use DNS subdomain of Harbor project) (NOTE: You must specify "insecure-registries" if your SSL cert is not configured for wildcard DNS subdomains)

Modifications to nginx.conf

  1. Put in http { block
  # Map for proxy cache
  map $host $harbor_project {
    default 0;
    ~^docker-mirror.domain.local$ docker.io;
    ~^(?<project>.+).docker-mirror.domain.local$ $project;
  }
  map $request_uri $new_uri {
    ~^/v2/(.+)$ /v2/$harbor_project/$1;
  }
  map $args $new_args {
    ~^(?<prefix2>.*scope=repository%3A)(?<suffix2>.*)$     ${prefix2}${harbor_project}%2F$suffix2;
  }
  map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$host$suffix1;
  }
  1. Put in second server { block
    # Rewrite for proxy cache
    if ($harbor_project != 0) {
      rewrite ^/v2/(.+)$ $new_uri;
      set $args $new_args;
    }
  1. Put in location /v2/ { block
      # Modify headers for proxy cache
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $new_header always;

Please test this out and let me know if you run into any issues.

Thanks for your help, I will run it again when I have the time and let you know if I have any issues.

@ChristianCiach
Copy link

ChristianCiach commented Aug 12, 2021

As shown by @jgallucci32, you could rewrite the Www-authenticate header like this:

# Map for proxy cache
map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$host$suffix1;
}

But this could fail if your nginx reverse proxy runs on a different port than the docker client uses to connect to it. In my example, my nginx runs at port 443 inside the container, but the port is mapped to 5000 on the host (-p 5000:443). In this case, the port (5000 in my case) will be missing from the Www-authenticate header, which causes the docker client to connect to the /service-endpoint using the wrong port (443 by default).

To fix this, we can just use the variable $http_host instead of $host, which will include the port of the request:

map $upstream_http_www_authenticate $new_header {
    ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$http_host$suffix1;
}

@aSauerwein
Copy link

I've tried so setup a transparent registry with Harbor 2.3.2 and editing the nginx.conf but it does not work. I always get this error message

Error response from daemon: unauthorized: authorize header needed to send HEAD to repository: authorize header needed to send HEAD to repository

my modified nginx.conf: https://gist.github.com/aSauerwein/424ac58e13c7aadd619b3c75461e555f

debugging with curl shows me that the Www-Authenticateheader is missing when using my docker-mirror instead of harbor directly

@ricardojdsilva87
Copy link

ricardojdsilva87 commented Oct 6, 2021

Hello all, just an update with the code for nginx that enabled us to use Harbor as a dockerproxy with containerd and docker.

We are using NGINX helm chart, and added the following in the http:

    http-snippet: |
      # Map for docker.io proxy cache
      map $host $harbor_project {
        default 0;
        ~^dockerproxy(.*)xxxxxx$ proxy;
      }
      map $request_uri $new_harbor_uri {
        ~^/v2/(.+)$ /v2/$harbor_project/$1;
      }
      map $new_harbor_uri $new_containerd_uri {
        default $new_harbor_uri;
        ~^/v2/(.+)\?ns=docker\.io(.*)$ /v2/$1$2;
      }
      map $args $new_harbor_args {
        ~^(?<prefix2>.*scope=repository%3A)(?<suffix2>.*)$     ${prefix2}${harbor_project}%2F$suffix2;
      }
      map $upstream_http_www_authenticate $new_harbor_header {
        ~^(?<prefix1>.*https://).*(?<suffix1>/service/token.*)$     $prefix1$http_host$suffix1;
      }

Added after the following annotations on the proxy ingress:

  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
      # Rewrite for docker.io proxy cache
      if ($harbor_project != 0) {
        rewrite ^/v2/(.+)$ $new_harbor_uri;
        rewrite ^/v2/(.+)$ $new_containerd_uri;
        set $args $new_harbor_args;
      }
    nginx.ingress.kubernetes.io/configuration-snippet: |
      # Modify headers for proxy cache  
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $new_harbor_header always;

Hope it helps

@alexdepalex
Copy link

Ran into the authenticate issue with both go-containerregistry and containerd. Isn't this something that Harbor could support natively, instead of these nginx reverse proxy hacks?

@wolfganghuse
Copy link

I am also seeing 401 unauth when trying to use docker-io.docker-registry.mydomain.local like described above.
https is configured and Cert is valid.
Harbor is 2.4.1 offline install running with docker-compose.

@pedroosorio
Copy link

pedroosorio commented Aug 18, 2023

If anyone is still looking, here is an approach based of @jgallucci32 and @ricardojdsilva87 with no if's:

  1. Add to http{ block:
map $host $harbor_project {
  default 0;
  ~^(?<project>.+).harbor.local$ $project;
}

map "$harbor_project#$request_uri" $harbor_registry_uri {
  default $request_uri;
  "~^0#.*$" $request_uri;
  "~^.+#/v2/_catalog.*$" $request_uri;
  "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
}

map $args $harbor_args{
  default $args;
  ~^(?<arg_prefix>.*scope=repository%3A)(?<arg_suffix>.*)$ $arg_prefix$harbor_project%2F$arg_suffix;
}

map $upstream_http_www_authenticate $harbor_authenticate_header {
  default $upstream_http_www_authenticate;
  ~^(?<header_prefix>.*http.+//).*(?<header_suffix>/service/token.*)$ $header_prefix$http_host$header_suffix;
}
  1. Add to location /v2/ block:
rewrite ^/v2/(.+)$ $harbor_registry_uri break;
proxy_hide_header Www-Authenticate;
add_header Www-Authenticate $harbor_authenticate_header always;
  1. Add to location /service/ block:
set $args $harbor_args;

How to use:

  1. Do this changes to nginx.conf
  2. Create a registry to the given target registry
  3. Create a project (proxy-cache type) using the registry named 'xyz'
  4. In the machines using Harbor as the proxy, add an host 'xyz.harbor.local' pointing to the harbor IP
  5. Repeat 2,3 and 4 for each registry to be mirrored

If 'harbor.local' is not a good suffix for you, make sure you change it in the hosts file as well as in the nginx configuration at map $host $harbor_project

@hiddenmarten
Copy link

If anybody has issues with their non-nginx-based ingress controller, welcome to this simple chart.

Thanks for the inspiration:
@pedroosorio, @jgallucci32 and @ricardojdsilva87

@asdorsey
Copy link

asdorsey commented May 21, 2024

EDIT 20240528: Please see my comment below with an updated solution

@pedroosorio Thank you for your solution for this problem. I've made one slight modification to better handle images like python:3.12 (which is actually library/python:3.12, apparently). I've modified the $harbor_registry_uri map as follows:

  map "$harbor_project#$request_uri" $harbor_registry_uri {
    default $request_uri;
    "~^0#.*$" $request_uri;
    "~^.+#/v2/_catalog.*$" $request_uri;
    "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/([^/]+)(.*)$" /v2/$harbor_project/library/$1$2;    # added this line - MODIFIED 20240523
    "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
  }

Without the extra line that I added, I was having issues pulling "official images" that don't have a publisher name, like python:3.12.

$ docker pull docker.example.com/python:3.12
Error response from daemon: unknown: repository docker/docker/library/python not found

The line I added makes pulling official images work as expected:

$ docker pull docker.example.com/python:3.12
3.12: Pulling from python
Digest: sha256:3966b81808d864099f802080d897cef36c01550472ab3955fdd716d1c665acd6
Status: Image is up to date for docker.walkingwire.net/python:3.12
docker.walkingwire.net/python:3.12

@asdorsey
Copy link

The following is a further modification of @pedroosorio 's solution that adds the capability to bypass the mapping and rewriting process for certain domains.

I needed both transparent proxy caches and the ability to upload to other projects, but I had issues with the /blobs/uploads map pattern applying multiple times (probably because of a redirect somewhere), so I'd end up with the project name in the URI multiple times. With this change, I can instead exclude one of the domain names pointing to this Harbor instance, so when using that excluded domain, Harbor acts normally (without the rewrites and stuff for the transparent proxy cache).

Unfortunately I did have to use an if for the rewrite in the location /v2/ block to get this to work. Someone better at NGINX than me could probably eliminate that if.

  1. Add to http{ block:
  map $host $harbor_project {
    default 0;
    ~^harbor.example.com$ 0;        # this is how to specify a domain name to exclude from processing
    ~^(?<project>.+).example.com$ $project;
  }

  map "$harbor_project#$request_uri" $harbor_registry_uri {
    default $request_uri;
    "~^0#.*$" $request_uri;
    "~^.+#/v2/_catalog.*$" $request_uri;
    "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/([^/]+)(.*)$" /v2/$harbor_project/library/$1$2;
    "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
  }
  
  map "$harbor_project#$args" $harbor_args {
    default $args;
    "~^0#.*$" $args;
    "~^.+#(?<arg_prefix>.*scope=repository%3A)(?<arg_suffix>.*)$" $arg_prefix$harbor_project%2F$arg_suffix;
  }
  
  map "$harbor_project#$upstream_http_www_authenticate" $harbor_authenticate_header {
    default $upstream_http_www_authenticate;
    "~^0#.*$" $upstream_http_www_authenticate;
    "~^.+#(?<header_prefix>.*http.+//).*(?<header_suffix>/service/token.*)$" $header_prefix$http_host$header_suffix;
  }
  1. Add to location /v2/ block:
      if ($harbor_project) {
        rewrite ^/v2/(.+)$ $harbor_registry_uri break;
      }
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $harbor_authenticate_header always;

  1. Add to location /service/ block:
    set $args $harbor_args;

@kvaps
Copy link

kvaps commented Jul 30, 2024

Hi all, I experimented with this today, and found that this configuration working to me:

  1. Add to http{ block:
map $host $harbor_project {
  default 0;
  ~^harbor.example.com$ 0;
  ~^(?<project>.+).example.com$ $project;
}

map "$harbor_project#$request_uri" $harbor_registry_uri {
  default $request_uri;
  "~^0#.*$" $request_uri;
  "~^.+#/v2/_catalog.*$" $request_uri;
  "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/([^/]+)(.*)$" /v2/$harbor_project/library/$1$2;
  "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
}

map $args $harbor_args{
  default $args;
  ~^(?<arg_prefix>.*scope=repository%3A)(?<arg_suffix>.*)$ $arg_prefix$harbor_project%2F$arg_suffix;
}

map $upstream_http_www_authenticate $harbor_authenticate_header {
  default $upstream_http_www_authenticate;
  ~^(?<header_prefix>.*http.+//).*(?<header_suffix>/service/token.*)$ $header_prefix$http_host$header_suffix;
}
  1. Add to location /v2/ block in the bottom:
rewrite ^/v2/(.+)$ $harbor_registry_uri break;
proxy_hide_header Www-Authenticate;
add_header Www-Authenticate $harbor_authenticate_header always;
  1. Add to location /service/ block in the bottom:
set $args $harbor_args;

many thanks to you folks for provided snippets! <3

@upics
Copy link

upics commented Aug 29, 2024

The following is a further modification of @pedroosorio 's solution that adds the capability to bypass the mapping and rewriting process for certain domains.

I needed both transparent proxy caches and the ability to upload to other projects, but I had issues with the /blobs/uploads map pattern applying multiple times (probably because of a redirect somewhere), so I'd end up with the project name in the URI multiple times. With this change, I can instead exclude one of the domain names pointing to this Harbor instance, so when using that excluded domain, Harbor acts normally (without the rewrites and stuff for the transparent proxy cache).

Unfortunately I did have to use an if for the rewrite in the location /v2/ block to get this to work. Someone better at NGINX than me could probably eliminate that if.

  1. Add to http{ block:
  map $host $harbor_project {
    default 0;
    ~^harbor.example.com$ 0;        # this is how to specify a domain name to exclude from processing
    ~^(?<project>.+).example.com$ $project;
  }

  map "$harbor_project#$request_uri" $harbor_registry_uri {
    default $request_uri;
    "~^0#.*$" $request_uri;
    "~^.+#/v2/_catalog.*$" $request_uri;
    "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
    "~^.+#/v2/([^/]+)(.*)$" /v2/$harbor_project/library/$1$2;
    "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
  }
  
  map "$harbor_project#$args" $harbor_args {
    default $args;
    "~^0#.*$" $args;
    "~^.+#(?<arg_prefix>.*scope=repository%3A)(?<arg_suffix>.*)$" $arg_prefix$harbor_project%2F$arg_suffix;
  }
  
  map "$harbor_project#$upstream_http_www_authenticate" $harbor_authenticate_header {
    default $upstream_http_www_authenticate;
    "~^0#.*$" $upstream_http_www_authenticate;
    "~^.+#(?<header_prefix>.*http.+//).*(?<header_suffix>/service/token.*)$" $header_prefix$http_host$header_suffix;
  }
  1. Add to location /v2/ block:
      if ($harbor_project) {
        rewrite ^/v2/(.+)$ $harbor_registry_uri break;
      }
      proxy_hide_header Www-Authenticate;
      add_header Www-Authenticate $harbor_authenticate_header always;
  1. Add to location /service/ block:
    set $args $harbor_args;

I have encountered the same problem as him. If you want to push to a non-proxy cache project using project.domain.example, something with /blob/uploads dont work. Anyone has resolved this issue ?

@yashid-mohamed
Copy link

yashid-mohamed commented Jan 20, 2025

Hi All,

I am trying to setup NGINX reverse proxy which supports pulls for both types of docker images at the same time

  1. Images with the library prefix such as python, ubuntu etc.
  2. Images which do not have the library prefix i.e. that belong to other projects, such as grafana/loki .

The solution provided by @asdorsey and @kvaps works fine for docker images having the library prefix such as nginx, python, ubuntu etc.
However it fails for images which belong to other projects, for example - grafana/loki or kumahq/kuma-dp since the rules rewrite the uri with library/

$ docker pull dockerhub-proxy.example.com/grafana/loki
Using default tag: latest
Error response from daemon: unknown: repository dockerhub-proxy/library/grafana/loki not found

The solution provided by @pedroosorio , only works for images that do not have the library prefix such as grafana/loki but fails to work for images that do, like nginx,python etc.

$ docker pull dockerhub-proxy.example.com/nginx:latest
Error response from daemon: unknown: repository dockerhub-proxy/dockerhub-proxy/library/nginx not found

Has anyone managed to get a working solution which supports pulling of both these types of images or can provide any hints to update the rules to support this?

Thanks!

@yashid-mohamed
Copy link

Managed to get it working for both with @pedroosorio's solution by updating the second map as follows.

Note: replace dockerhub-proxy in the map with the name of your harbor project.

map "$harbor_project#$request_uri" $harbor_registry_uri {
  default $request_uri;
  "~^0#.*$" $request_uri;
  "~^.+#/v2/dockerhub-proxy/([^?]+)(.*)$" $request_uri;
  "~^.+#/v2/_catalog.*$" $request_uri;
  "~^.+#/v2/(.+)(/tags/list.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/(.+)(/blobs/uploads.*)$" /v2/$harbor_project/$1$2;
  "~^.+#/v2/([^?]+)(.*)$" /v2/$harbor_project/$1;
}

Hi All,

I am trying to setup NGINX reverse proxy which supports pulls for both types of docker images at the same time

  1. Images with the library prefix such as python, ubuntu etc.
  2. Images which do not have the library prefix i.e. that belong to other projects, such as grafana/loki .

The solution provided by @asdorsey and @kvaps works fine for docker images having the library prefix such as nginx, python, ubuntu etc. However it fails for images which belong to other projects, for example - grafana/loki or kumahq/kuma-dp since the rules rewrite the uri with library/

$ docker pull dockerhub-proxy.example.com/grafana/loki
Using default tag: latest
Error response from daemon: unknown: repository dockerhub-proxy/library/grafana/loki not found

The solution provided by @pedroosorio , only works for images that do not have the library prefix such as grafana/loki but fails to work for images that do, like nginx,python etc.

$ docker pull dockerhub-proxy.example.com/nginx:latest
Error response from daemon: unknown: repository dockerhub-proxy/dockerhub-proxy/library/nginx not found

Has anyone managed to get a working solution which supports pulling of both these types of images or can provide any hints to update the rules to support this?

Thanks!

@dhpagani
Copy link

Thank you all for your snippets!

I have several projects on Harbor, one of which is a DockerHub cache. This requires authentication for pulling and pushing private containers while allowing public pulls from dockerhub-cache.

The solution provided by @pedroosorio, along with @yashid-mohamed's consideration, made the registry mirror work. However, I still couldn't push and pull for other projects. Since the approach used here relies on two DNS entries, I duplicated the Nginx configuration—copying the entire server{} block. One block uses the server_name of the first domain, while the other is assigned to the second DNS entry.

From Nginx's perspective, this results in two separate services with distinct domains and configurations, preventing them from overlapping and eliminating the need for multiple conditional statements inside the Nginx config.

I'm sure that someone with a deeper understanding of Nginx could make it work with a single server{} entry! :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/proxy-cache kind/requirement New feature or idea on top of harbor
Projects
None yet
Development

No branches or pull requests