In our last article we discussed how to generate a docker image. If we want to share an image we can either export the container image or provide the Dockerfile so that someone else can then rebult the container from scratch. Having someone rebuilt from scratch an image is not exactly the “agile way” and imagine if you have 5 engineers, would it not be better if they could just download a pre-built image? To solve this problem we have a docker Registry, some where we can upload and downlod images from.
The Docker project host a public registry at https://registry.hub.docker.com/. However it’s probable that your images are not for external consumption and for that we need a private registry. Even if your container images are going to be hosted in the Docker Hub you might want to setup an internal registry for development/testing before publishing externally, as part of your agile continous integration workflow.
In this post we will show you how to setup a docker registry (version 0.9.1) to store your local images. Whilst Docker recently released version 2.0, we do not consider it ready for production use as there are features, present in version 0.9.1, missing in the later version.
By default the docker registry runs over an un-encrypted HTTP connection. Since it is desirable to require users log-in to the registry we also want to encrypt all traffic to and from the registry.
Required components
- nginx docker image version 1.7
- registry docker image version 0.9.1
- docker-registry-frontend image version latest
- Valid SSL certificate associated with the fully-qualified domain name (FQDN) that we going to use to access our docker registry. In this example we going to use register.example.com
1. We going to use different containers to deploy our secured registry. So we need a docker host.
2. Run docker container from docker registry image version 0.9.1
docker run -t -d -v /opt/registry-v1:/tmp/registry-dev --name docker-registry-v1 registry:0.9.1
- With this command we start a new registry container called docker-registry-v1 version 0.9.1 (downloaded from docker public hub).
- The name of this container is important because we will use it later.
- You need an account in the public docker registry to be able to download docker public images.
- With “-v /opt/registry-v1:/tmp/registry-dev” we map a folder in the docker host where the docker registry store all their files. In case that we want to stop and start another registry container we can use the previous files.
Note: docker registry is normally listening on port 5000 but for secure reasons we are not going to expose this port.
3. Run nginx as reverse proxy.
- The goal is to have a reverse proxy pointing to our docker registry and also add basic authentication and SSL encryption. As requirement we need a file with user credentials for basic authentication, and the SSL certificate and SSL key that has been issued by a globally recognized certificate authority (CA).
Note: Docker daemon considers any private registry secure only if it uses transport layer security, and the Docker host is able to verify the validity of the certificate. If you use self-signed certificates you will need to run the Docker daemon with –insecure-registry flag.
- We can download the official Nginx docker image and do manual configuration or we can use the public image that I uploaded to the docker hub. You can examine the Dockerfile here. More info: https://registry.hub.docker.com/u/jmaciasportela/docker-registry-proxy-v1/
docker run -t -d -p 443:443 -e REGISTRY_HOST="docker-registry-v1" \ -e REGISTRY_PORT="5000" -e SERVER_NAME="register.example.com" \ --link docker-registry-v1:docker-registry-v1 \ -v /opt/.htpasswd:/etc/nginx/.htpasswd:ro \ -v /opt/certs:/etc/nginx/ssl:ro \ jmaciasportela/docker-registry-proxy-v1
- Map external, internet facing, port 443 to internal port 443
- Environment variable REGISTRY_HOST is the name that we use for the container on point 1
- Environment variable REGISTRY_PORT is the port where the registry container is listening
- Environment variable SERVER_NAME is the public server name of your registry. IMPORTANT: It has match the FQDN from the SSL certificate
- We link these two containers together using an alias ‘docker-registry-v1’, enabling the proxy container to connect to the registry container without knowing its IP address
- Map (in read-only mode) the password database, htpasswd, with credentials to connect to the registry. You can generate one with the command
htpasswd -c htpasswd userXXX
- Map (in read-only mode) from the docker host a folder that contains the relevant certificates (named docker-registry.crt and docker-registry.key) with internal nginx ssl folder
4. To access the registry we need to log in
docker login register.example.com Username: jmacias Password: ***** Email: Login Succeeded
5. Imagine that we have a Docker project called Hello
cd hello docker build -t "register.example.com/hello" . docker push register.example.com/hello
6. On other docker host
docker login register.example.com docker pull register.example.com/hello docker run -d hello
7. Docker Registry Frontend. A good way to manage your docker registry
docker run -d -e ENV_DOCKER_REGISTRY_HOST=docker-registry-v1 \ -e ENV_DOCKER_REGISTRY_PORT=5000 \ -e ENV_REGISTRY_PROXY_FQDN=register.example.com \ -e ENV_REGISTRY_PROXY_PORT=443 \ --link docker-registry-v1:docker-registry-v1 \ -e ENV_USE_SSL=yes \ -v /opt/certs/docker-registry.crt:/etc/apache2/server.crt:ro \ -v /opt/certs/docker-registry.key:/etc/apache2/server.key:ro \ -p 8443:443 konradkleine/docker-registry-frontend
- Environment variable ENV_DOCKER_REGISTRY_HOST is the name that we use for the container on point 1
- Environment variable ENV_DOCKER_REGISTRY_PORT is the port where the registry is listening
- Environment variable ENV_REGISTRY_PROXY_FQDN is the public server name of your registry
- Environment variable ENV_REGISTRY_PROXY_PORT is the port where the reverse proxy is listening from point 2
- We link this container with the previous to enable connectivity on port 5000
- Environment variable ENV_USE_SSL set to true. For SSL we use the same certificates that we have configured in point 2, so docker-registry.crt point to server.crt and and docker-registry.key point to server.key
- We map the external port 8443 to the internal port 443, so we can access the frontend from our browser
Note: The frontend allows us to maintain our docker registry without needing to directly use the API
8. Extra security
- You can add extra flag to have a read only frontend -e ENV_MODE_BROWSE_ONLY=true
- Add basic authenticacion to the frontend.
On a running frontend container:
docker exec -t -i DOCKERID bash -l root@e259f5121968:/# apt-get update root@e259f5121968:/# apt-get install apache2-utils vim root@e259f5121968:/# htpasswd -c /etc/apache2/users USERXX root@e259f5121968:/# vim /etc/apache2/sites-enabled/docker-site.conf
Add this lines inside <Directory /var/www/html> tag:
AuthType Basic AuthName "ownCloud docker Registry" AuthUserFile /etc/apache2/users Require valid-user
Restart apache service and exit container
root@e259f5121968:/# service apache2 reload root@e259f5121968:/# exit
REFERENCES
Docker – Shipping Software: http://solidgear.es/en/blog/docker-shipping-software
Docker II – Building A Container: http://solidgear.es/en/blog/docker-ii-building-container
Docker Registry 2.0: http://container-solutions.com/2015/04/running-secured-docker-registry-2-0/
Docker Registry Frontend: https://github.com/kwk/docker-registry-frontend
Docker cheat sheet: https://github.com/wsargent/docker-cheat-sheet#registry–repository