Page MenuHomePhabricator
Paste P14384


Authored by Legoktm on Wed, Feb 17, 12:34 AM.
upstream registry {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
server {
listen 8080 default_server;
listen [::]:8080 default_server ipv6only=on;
# Images can be pretty large!
client_max_body_size 0;
# Avoids 411 errors!
chunked_transfer_encoding on;
# Tell everyone we're v2, not v1!
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
# And we vary response based on what you send in Accept
# See:
add_header 'Vary' 'Accept' always;
location / {
root /usr/share/nginx/html;
# Let me prefix by saying that the Docker v2 registry protocol apparently
# is much better than the v1 protocol. I have not read enough about both
# to make a comparative analysis, but I can tell that the auth design for
# v2 is just terribly done!
# So for a v2 to client to decide if it needs to authenticate or not, it
# first makes a GET request to /v2/, and based on wether it gets back a 2xx
# or a 401, decides to authenticate for all other requests in that session.
# This practically means it is really hard to do something like restrict
# write access to only a certain group while allowing read access to other
# groups - since it categorizes the whole registry based on the response
# code of one endpoint....
# I have hacked around this in the following manner:
# - Tell nginx that you can be whitelisted by IP or by specifying a password
# - Whitelist the whole world!
# - Blacklist the one IP that needs to specify password (to push)
# This causes nginx to return a 401 only to the IP that I want to push, and
# since this IP will have the password it'll be able to authenticate. The
# rest of the world will just be allowed via IP, and hence not be asked for
# passwords.
# This special case handling is only for the root endpoint - all other endpoints
# are handled in the location stanza below this, and require authentication for
# POST/PUT/DELETE etc. We're only doing this special hack to convince the
# docker client to send/not send basic auth credentials depending on what we
# want them to do.
# Fuck you docker.
location = /v2/ {
satisfy any;
allow all;
auth_basic "docker-registry (regular-push)";
auth_basic_user_file /etc/nginx/regular-push.htpasswd;
location /v2/restricted/ {
# Send GET/HEAD requests to @restricted_read location block below
# See <>
# which explains and recommends this
error_page 418 @restricted_read;
recursive_error_pages on;
if ($request_method ~ ^(GET|HEAD)$) {
return 418;
# Rest of this block applies to POST/PUT/DELETE/etc. methods
# Only "restricted-push" user can push images
auth_basic "docker-registry restricted (restricted-push)";
auth_basic_user_file /etc/nginx/restricted-push.htpasswd;
# Covers GET/HEAD requests to /v2/restricted/
location @restricted_read {
# Only "restricted-read" user can read images (includes "restricted-push")
auth_basic "docker-registry restricted (restricted-read)";
auth_basic_user_file /etc/nginx/restricted-read.htpasswd;
location /v2 {
# Require auth for POST, PUT, DELETE, ... requests
limit_except GET HEAD OPTIONS {
auth_basic "docker-registry (regular-push)";
auth_basic_user_file /etc/nginx/regular-push.htpasswd;