Docker Repository Reverse Proxy Strategies
Introduction
The Docker client has strict requirements about how it can retrieve content from a repository (i.e., a registry). These requirements mainly center around the path at which it expects everything to be hosted.
While it is possible to tell the Docker client to use a chosen host from which to retrieve (or to which to upload) images, it is not possible to tell it to use an arbitrary base path where images are stored in a registry.
To further explain, the Docker client is given a registry to contact by specifying only the hostname + port. It's also given a specific path to an image in that registry. So, for example, it would be given "example:443/some/custom/image" to specify an image. You are not able to specify a registry application path.
Nexus Repository exposes its Docker registries with a repository path of /repository/<repo_name>/ and, by default, and application context path of /.
So, a full Docker image in the repository "docker-hosted" might be accessible at full URL example:443/nexus3/repository/docker-hosted/some/custom/image, which can be broken down as follows:
- example.com = host name
- 443 = port
- /nexus3 = application context path
- /repository/docker-hosted = base registry path
- /some/custom/image = specific image path in the registry
There is no way to give the Docker client the application context path or base registry path. Docker needs the registry exposed at the root of the host + port that it is accessing.
This is important because Nexus Repository uses request paths to separate content between different repositories. There are two potential ways to overcome this Docker limitation: using port connectors or using a reverse proxy. This page describes how to use a reverse proxy to access Docker registries hosted in Nexus Repository.
Note that SSL is required for Docker to work properly.
Intended Use Cases
Using a reverse proxy in front of Nexus Repository for Docker repositories is an option to consider for the following use cases:
- Multiple connectors inside of Eclipse Jetty or Nexus Repository would cause performance issues
- If more than 20 connectors are needed, you must use a reverse proxy
- You need to limit the number of open ports for infrastructure or security reasons
- Managing secure connectors/ports/hosts inside an external reverse proxy aligns with organizational goals and infrastructure
Implementation Overview
Once implemented as described in this topic, Nexus Repository listens at a non-https connector such as the default 8081.
You can add Docker repositories into Nexus Repository as normal, but do not configure them with any connector port values.
In the simple example, the following Docker repository hierarchy would exist inside Nexus per project team:
docker-group-project
docker-hosted-project
docker-hub
Based on the host name or port of the request to a reverse proxy, the reverse proxy decides where to reverse proxy the request into Nexus Repository.
Port Mapping Example
In the following Docker push port mapping example, the reverse proxy would direct Docker push
commands received at https://project.example.com:8086
to http://nexus.example.com:8081/repository/docker-hosted-project
.
docker login project.example.com:8086 docker push project.example.com:8086
In the following Docker pull port mapping example, the reverse proxy would direct Docker pull
commands received at https://project.example.com:8087
to http://nexus.example.com:8081/repository/docker-group-project
.
docker login project.example.com:8087 docker pull project.example.com:8087
The following example Apache HTTP Server configuration port maps https://project.example.com:8087
to http://nexus.example.com:8081/repository/docker-repo
.
Listen 8087 https <VirtualHost *:8087> ServerName project.example.com ServerAdmin admin@example.com AllowEncodedSlashes NoDecode ProxyRequests Off ProxyPreserveHost On SSLEngine ON SSLCertificateFile "/path/to/ssl/server.crt" SSLCertificateKeyFile "/path/to/ssl/server.key" ProxyTimeout 300 ProxyPass / http://nexus.example.com:8081/repository/docker-repo/ nocanon ProxyPassReverse / http://nexus.example.com:8081/repository/docker-repo/ RequestHeader set X-Forwarded-Proto "https" </VirtualHost>
Host Mapping Example
A wildcard TLS certificate is needed for the host name mapping scenario if you intend to have more than one project team specific host name.
In the following Docker push host mapping example, the reverse proxy would direct Docker push
commands received at https://project-push.example.com
to http://nexus.example.com:8081/repository/docker-hosted-project
.
docker login project-push.example.com docker push project-push.example.com
In the following Docker pull host mapping example, the reverse proxy would direct Docker pull
commands recieved at https://project.example.com
to http://nexus.example.com:8081/repository/docker-group-project
.
docker login project.example.com docker pull project.example.com
The following example Nginx configuration host maps https://project.example.com
to http://nexus.example.com:8081/repository/docker-repo
.
This Nginx host mapping example uses the same server for both Docker and regular Nexus Repository requests; it also uses location directives to proxy /v2 or /v1 requests to the Docker repository. Docker automatically adds these v1/v2 paths to the requests and does not require users to add them directly to the Docker commands.
http { proxy_send_timeout 120; proxy_read_timeout 300; proxy_buffering off; proxy_request_buffering off; keepalive_timeout 5 5; tcp_nodelay on; server { listen 443 ssl; server_name project.example.com ssl on; ssl_certificate /path/to/ssl/server.crt; ssl_certificate_key /path/to/ssl/server.key; # Docker /v2 and /v1 (for search) requests location /v2 { proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto "https"; proxy_pass http://nexus.example.com:8081/repository/docker-repo/$request_uri; } location /v1 { proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto "https"; proxy_pass http://nexus.example.com:8081/repository/docker-repo/$request_uri; } # Regular Nexus requests location / { proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto "https"; proxy_pass http://nexus.example.com:8081; } }}