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 firstname.lastname@example.org 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 email@example.com 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
Hi Pienaar. Thanks for the tip. This is just what I was looking for to secure my docker registr in our environment. I have installed it with one vhost apache proxy without problem and it goes encrypted with ssl.
But now the problem that I find is that everybody can do one pull or push without login and password.
So is there any way to put some authentication into this?
Maybe some basic authentication with apache basic auth and htpasswd?…
Something like this:
AuthName “Private Docker Registry Repository”
Or is there any more “elegant or official” way of providing some authentication?
There are several issues on the github repo for the registry about auth, don’t think it works particularly well but check there
you need to add authentication to the virtualhost / location.
Thank you, I was missing the ProxyPreserveHost line and can’t imagine I would have found it myself.
You saved me a lot of time and I appreciate the post.
I recently set this up and found I also needed the following config fragment in my vhost:
Header set Host “registry.example.com”
RequestHeader set X-Forwarded-Proto “https”