3
votes

I'm trying to deploy a generic registry instance (npm private registry + docker private registry) on an EC2 instance sitting behind an Elastic Load Balancer with nginx as a reverse proxy. Doing so I want to be able to push npm packages and docker images respectively to registry.mydomain.org and docker.mydomain.org through https and with authentication.

To do so, I followed the following steps:

  1. I created an EC2 instance from the AWS console opening an HTTP port (e.g. 8080) from the security groups option
  2. I created a new Load Balancer with HTTPS connecting it to the HTTP port of the instance.
  3. I created a docker-compose.yml file with this configuration (I'm using verdaccio as npm private registry and registry:2 as docker registry:

    version: '3'
    services:
      nginx:
        image: nginx:alpine
        container_name: nginx
        restart: always
        ports:
          - "80:80"
        volumes:
          - ./nginx:/etc/nginx/conf.d/
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
        links:
          - verdaccio:verdaccio
          - docker:docker

      verdaccio:
        image: verdaccio/verdaccio:latest
        container_name: verdaccio    
        restart: always
        ports:
          - "4873:4873"
        volumes:
          - ./registry:/verdaccio/conf
          - ./database/verdaccio:/verdaccio/storage 

      docker:
        image: registry:2
        container_name: docker
        restart: always
        ports:
          - "5000:5000"
        volumes:
          - ./database/docker:/var/lib/registry

  1. The nginx configuration (nginx.conf) is:

    events {
        worker_connections  1024;
    }

    http {
        upstream docker-registry {
            server docker:5000;
        }

        upstream npm-registry {
            server verdaccio:4873;
        }

        ## Set a variable to help us decide if we need to add the
        ## 'Docker-Distribution-Api-Version' header.
        ## The registry always sets this header.
        ## In the case of nginx performing auth, the header will be unset
        ## since nginx is auth-ing before proxying.
        map $upstream_http_docker_distribution_api_version     $docker_distribution_api_version {
            '' 'registry/2.0';
        }

        # Healtcheck
        server {
            listen 80;
            location /healthcheck {
                access_log off;
                return 200;
            }
        }

        server {
            # Server options
            listen 80;
            charset utf-8;
            client_max_body_size 0;

            server_name registry.mydomain.org;

            # Proxy settings
            location / {
                access_log /var/log/nginx/verdaccio.log;
                proxy_pass http://npm-registry;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-NginX-Proxy true;
                proxy_ssl_session_reuse off;
                proxy_redirect off;
            }
        }

        server {
            # Server options
            listen 80;
            charset utf-8;
            client_max_body_size 0;

            # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
            chunked_transfer_encoding on;

            server_name docker.mydomain.org;

            # Authentication
            auth_basic "Registry realm";
            auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;

            # Proxy settings
            location / {

                if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
                    return 404;
                }

                ## If $docker_distribution_api_version is empty, the header will not be added.
                ## See the map directive above where this variable is defined.
                add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;

                access_log /var/log/nginx/docker.log;
                proxy_pass http://docker-registry;
                proxy_read_timeout 900;
            }
        }
    }
  1. I created an nginx.htpasswd file with the encoded authentication infos.

Now, the registry is perfectly working. So if I visit https://registry.mydomain.org I can see it and I can push npm packages to it through npm login --registry=https://registry.mydomain.org --scope=@myscope

Concerning the docker registry, however, while I can definitely log into it with docker login -u user -p password but when I am trying to push an image to it the docker client enters in an infinite loop and continues trying uploading the images (with no success). On the server side, the docker registry's logs does not show an useful info about what is going on since all the requests end in 202 HTTP statuses.

Any hint about how to solve it?

1

1 Answers

4
votes

I figured it out. I was missing a proxy_set_header Host $host; in the nginx proxy configuration.

Doing this:

server {
        # Server options
        listen 80;
        charset utf-8;
        client_max_body_size 0;

        # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486)
        chunked_transfer_encoding on;

        server_name docker.cubbit.net;

        # Authentication
        auth_basic "Registry realm";
        auth_basic_user_file /etc/nginx/conf.d/nginx.htpasswd;

        # Proxy settings
        location / {
            # Do not allow connections from docker 1.5 and earlier
            # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
            if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
                return 404;
            }

            ## If $docker_distribution_api_version is empty, the header will not be added.
            ## See the map directive above where this variable is defined.
            add_header 'Docker-Distribution-Api-Version' $docker_distribution_api_version always;

            access_log /var/log/nginx/docker.log;
            proxy_pass http://docker-registry;
            proxy_set_header Host $host;
            proxy_read_timeout 900;
        }
    }

It started working perfectly. Hope this can help someone. I spent two days on it.