github.com/npaton/distribution@v2.3.1-rc.0+incompatible/docs/nginx.md (about) 1 <!--[metadata]> 2 +++ 3 title = "Authenticating proxy with nginx" 4 description = "Restricting access to your registry using a nginx proxy" 5 keywords = ["registry, on-prem, images, tags, repository, distribution, nginx, proxy, authentication, TLS, recipe, advanced"] 6 +++ 7 <![end-metadata]--> 8 9 # Authenticating proxy with nginx 10 11 12 ## Use-case 13 14 People already relying on a nginx proxy to authenticate their users to other services might want to leverage it and have Registry communications tunneled through the same pipeline. 15 16 Usually, that includes enterprise setups using LDAP/AD on the backend and a SSO mechanism fronting their internal http portal. 17 18 ### Alternatives 19 20 If you just want authentication for your registry, and are happy maintaining users access separately, you should really consider sticking with the native [basic auth registry feature](deploying.md#native-basic-auth). 21 22 ### Solution 23 24 With the method presented here, you implement basic authentication for docker engines in a reverse proxy that sits in front of your registry. 25 26 While we use a simple htpasswd file as an example, any other nginx authentication backend should be fairly easy to implement once you are done with the example. 27 28 We also implement push restriction (to a limited user group) for the sake of the example. Again, you should modify this to fit your mileage. 29 30 ### Gotchas 31 32 While this model gives you the ability to use whatever authentication backend you want through the secondary authentication mechanism implemented inside your proxy, it also requires that you move TLS termination from the Registry to the proxy itself. 33 34 Furthermore, introducing an extra http layer in your communication pipeline will make it more complex to deploy, maintain, and debug, and will possibly create issues. Make sure the extra complexity is required. 35 36 For instance, Amazon's Elastic Load Balancer (ELB) in HTTPS mode already sets the following client header: 37 38 ``` 39 X-Real-IP 40 X-Forwarded-For 41 X-Forwarded-Proto 42 ``` 43 44 So if you have an nginx sitting behind it, should remove these lines from the example config below: 45 46 ``` 47 X-Real-IP $remote_addr; # pass on real client's IP 48 X-Forwarded-For $proxy_add_x_forwarded_for; 49 X-Forwarded-Proto $scheme; 50 ``` 51 52 Otherwise nginx will reset the ELB's values, and the requests will not be routed properly. For more information, see [#970](https://github.com/docker/distribution/issues/970). 53 54 ## Setting things up 55 56 Read again [the requirements](recipes.md#requirements). 57 58 Ready? 59 60 -- 61 62 Create the required directories 63 64 ``` 65 mkdir -p auth 66 mkdir -p data 67 ``` 68 69 Create the main nginx configuration you will use. 70 71 ``` 72 73 cat <<EOF > auth/nginx.conf 74 events { 75 worker_connections 1024; 76 } 77 78 http { 79 80 upstream docker-registry { 81 server registry:5000; 82 } 83 84 ## Set a variable to help us decide if we need to add the 85 ## 'Docker-Distribution-Api-Version' header. 86 ## The registry always sets this header. 87 ## In the case of nginx performing auth, the header will be unset 88 ## since nginx is auth-ing before proxying. 89 map \$upstream_http_docker_distribution_api_version \$docker_distribution_api_version { 90 'registry/2.0' ''; 91 default registry/2.0; 92 } 93 94 server { 95 listen 443 ssl; 96 server_name myregistrydomain.com; 97 98 # SSL 99 ssl_certificate /etc/nginx/conf.d/domain.crt; 100 ssl_certificate_key /etc/nginx/conf.d/domain.key; 101 102 # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html 103 ssl_protocols TLSv1.1 TLSv1.2; 104 ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; 105 ssl_prefer_server_ciphers on; 106 ssl_session_cache shared:SSL:10m; 107 108 # disable any limits to avoid HTTP 413 for large image uploads 109 client_max_body_size 0; 110 111 # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486) 112 chunked_transfer_encoding on; 113 114 location /v2/ { 115 # Do not allow connections from docker 1.5 and earlier 116 # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents 117 if (\$http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*\$" ) { 118 return 404; 119 } 120 121 # To add basic authentication to v2 use auth_basic setting. 122 auth_basic "Registry realm"; 123 auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd; 124 125 ## If $docker_distribution_api_version is empty, the header will not be added. 126 ## See the map directive above where this variable is defined. 127 add_header 'Docker-Distribution-Api-Version' \$docker_distribution_api_version always; 128 129 proxy_pass http://docker-registry; 130 proxy_set_header Host \$http_host; # required for docker client's sake 131 proxy_set_header X-Real-IP \$remote_addr; # pass on real client's IP 132 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; 133 proxy_set_header X-Forwarded-Proto \$scheme; 134 proxy_read_timeout 900; 135 } 136 } 137 } 138 EOF 139 ``` 140 141 Now create a password file for "testuser" and "testpassword" 142 143 ``` 144 docker run --rm --entrypoint htpasswd registry:2 -bn testuser testpassword > auth/nginx.htpasswd 145 ``` 146 147 Copy over your certificate files 148 149 ``` 150 cp domain.crt auth 151 cp domain.key auth 152 ``` 153 154 Now create your compose file 155 156 ``` 157 cat <<EOF > docker-compose.yml 158 nginx: 159 image: "nginx:1.9" 160 ports: 161 - 5043:443 162 links: 163 - registry:registry 164 volumes: 165 - ./auth:/etc/nginx/conf.d 166 - ./auth/nginx.conf:/etc/nginx/nginx.conf:ro 167 168 registry: 169 image: registry:2 170 ports: 171 - 127.0.0.1:5000:5000 172 volumes: 173 - `pwd`./data:/var/lib/registry 174 EOF 175 ``` 176 177 ## Starting and stopping 178 179 Now, start your stack: 180 181 docker-compose up -d 182 183 Login with a "push" authorized user (using `testuser` and `testpassword`), then tag and push your first image: 184 185 docker login -p=testuser -u=testpassword -e=root@example.ch myregistrydomain.com:5043 186 docker tag ubuntu myregistrydomain.com:5043/test 187 docker push myregistrydomain.com:5043/test 188 docker pull myregistrydomain.com:5043/test