Running a secure docker registry behind Apache

01/21/2015

I host a local Docker registry and used to just have this on port 5000 over plain http. I wanted to put it behind SSL and on port 443 and it was annoying enough that I thought I’d write this up.

I start my registry pretty much as per the docs:

% docker run --restart=always -d -p 5000:5000 -v /srv/docker-registry:/tmp/registry --name registry registry

This starts it, ensure it stays running, makes it listen on port 5000 and also use a directory on my host for the file storage so I can remove and upgrade the registry without issues.

The problem with this is there’s no SSL and so you need to configure docker specifically with:

docker -d --insecure-registry registry.devco.net:5000

At first I thought just fronting it with Apache will be as easy as:

<VirtualHost *:443>
   ServerName registry.devco.net
   ServerAdmin webmaster@devco.net
 
   SSLEngine On
   SSLCertificateFile /etc/httpd/conf.d/ssl/registry.devco.net.cert
   SSLCertificateKeyFile /etc/httpd/conf.d/ssl/registry.devco.net.key
   SSLCertificateChainFile /etc/httpd/conf.d/ssl/registry.devco.net.chain
 
   ErrorLog /srv/www/registry.devco.net/logs/error_log
   CustomLog /srv/www/registry.devco.net/logs/access_log common
 
   ProxyPass / http://0.0.0.0:5000/
   ProxyPassReverse / http://0.0.0.0:5000/
</VirtualHost>

This worked on the basic level but soon as I tried to push to the registry I got errors, it seems after the initial handshake the docker daemon would get instruction from the registry to connect to http://0.0.0.0:5000/ which then fails.

Some digging into the registry code and I found it’s using the host header of the request to return a X-Docker-Endpoints header in the replies to the initial handshake with the registry service and future requests from the docker daemon will use the endpoints advertised here for communications.

By default Apache does not keep the host header in the proxy request, I had to add ProxyPreserveHost on to the vhost and after that it was all good, no more insecure registries or having to specify ugly ports in my image tags.

So the final vhost looks like:

<VirtualHost *:443>
   ServerName registry.devco.net
   ServerAdmin webmaster@devco.net
 
   SSLEngine On
   SSLCertificateFile /etc/httpd/conf.d/ssl/registry.devco.net.cert
   SSLCertificateKeyFile /etc/httpd/conf.d/ssl/registry.devco.net.key
   SSLCertificateChainFile /etc/httpd/conf.d/ssl/registry.devco.net.chain
 
   ErrorLog /srv/www/registry.devco.net/logs/error_log
   CustomLog /srv/www/registry.devco.net/logs/access_log common
 
   ProxyPreserveHost on
   ProxyPass / http://127.0.0.1:5000/
   ProxyPassReverse / http://127.0.0.1:5000/
</VirtualHost>

I also made sure it was using localhost for the port 5000 traffic and now I can start my registry like this ensuring I do not even have that port on the internet facing interfaces:

% docker run --restart=always -d -p localhost:5000:5000 -v /srv/docker-registry:/tmp/registry --name registry registry