{"id":3113,"date":"2015-01-21T23:52:01","date_gmt":"2015-01-21T22:52:01","guid":{"rendered":"https:\/\/www.devco.net\/?p=3113"},"modified":"2015-01-22T00:49:53","modified_gmt":"2015-01-21T23:49:53","slug":"running-a-secure-docker-registry-behind-apache","status":"publish","type":"post","link":"https:\/\/www.devco.net\/archives\/2015\/01\/21\/running-a-secure-docker-registry-behind-apache.php","title":{"rendered":"Running a secure docker registry behind Apache"},"content":{"rendered":"

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.<\/p>\n

I start my registry pretty much as per the docs:<\/p>\n

\r\n% docker run --restart=always -d -p 5000:5000 -v \/srv\/docker-registry:\/tmp\/registry --name registry registry\r\n<\/pre>\n

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.<\/p>\n

The problem with this is there’s no SSL and so you need to configure docker specifically with:<\/p>\n

\r\ndocker -d --insecure-registry registry.devco.net:5000\r\n<\/pre>\n

At first I thought just fronting it with Apache will be as easy as:<\/p>\n

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

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\/<\/em> which then fails.<\/p>\n

Some digging into the registry code and I found it’s using the host<\/em> header<\/a> of the request to return a X-Docker-Endpoints<\/em> 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.<\/p>\n

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

So the final vhost looks like:<\/p>\n

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

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:<\/p>\n

\r\n% docker run --restart=always -d -p localhost:5000:5000 -v \/srv\/docker-registry:\/tmp\/registry --name registry registry\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"

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 […]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","footnotes":""},"categories":[7],"tags":[116],"_links":{"self":[{"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/posts\/3113"}],"collection":[{"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/comments?post=3113"}],"version-history":[{"count":7,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/posts\/3113\/revisions"}],"predecessor-version":[{"id":3120,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/posts\/3113\/revisions\/3120"}],"wp:attachment":[{"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/media?parent=3113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/categories?post=3113"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devco.net\/wp-json\/wp\/v2\/tags?post=3113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}