Docker Registry/Repository (Secure)

In this blog, we will learn how to make our repository/registry secure.

Let's see how it can be done.

For making our registry secure we will need certificates. So let's create a directory first to store those certificates. And then we will generate self-signed certificates using OpenSSL.

gaurav@learning-ocean:~$ mkdir certs
gaurav@learning-ocean:~$ openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt
Cant load /home/gaurav/.rnd into RNG
140393696207296:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/gaurav/.rnd
Generating a RSA private key
.................++++
# few lines removed from outut
Common Name (e.g. server FQDN or YOUR name) []:repo.docker.local
# few lines removed from output
gaurav@learning-ocean:~/certs$

The above image shows how we can create a self-signed certificate using OpenSSL. OpenSSL will generate domain.crt and domain.key files in the certs directory that we just created. While generating this certificate we have provided the hostname which will be used for accessing our registry.

Now, we need to copy the generated self signed certificate to the path - /etc/docker/certs.d/repo.docker.local:5000/ca.crt

For copying the certificate we needed to create a directory certs.d at path /etc/docker and inside certs.d we created a directory with the hostname that we provided while generating the certificate. Then we copied the certificate as ca.crt.

gaurav@learning-ocean:~$ cd certs/
gaurav@learning-ocean:~/certs$ ls
domain.crt  domain.key
gaurav@learning-ocean:~/certs$ cd /etc/docker/
gaurav@learning-ocean:/etc/docker$ sudo su
[sudo] password for gaurav:
root@learning-ocean:/etc/docker# mkdir certs.d
root@learning-ocean:/etc/docker# ls
certs.d  daemon.json  key.json
root@learning-ocean:/etc/docker# rm daemon.json
root@learning-ocean:/etc/docker# cd certs.d/
root@learning-ocean:/etc/docker/certs.d# ls
root@learning-ocean:/etc/docker/certs.d# mkdir repo.docker.local:5000
root@learning-ocean:/etc/docker/certs.d# cd /home/gaurav/certs/
root@learning-ocean:/home/gaurav/certs# cp domain.crt /etc/docker/certs.d/repo.docker.local\:5000/ca.crt

After copying the certificate we need to restart the docker service.

root@learning-ocean:~# service docker restart
root@learning-ocean:~#

And we also need to run the registry again using the command in the below image.

root@learning-ocean:~# docker container run -d -p 5000:5000 --name secure_registry -v /home/gaurav/certs/:/certs -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key registry
9f4a4f911259672c1f1fe31195e17914f355c383d496c79ec68e2d79ef4d0922
root@learning-ocean:~# docker container ls
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS                    NAMES
9f4a4f911259   registry   "/entrypoint.sh /etc…"   5 seconds ago   Up 4 seconds   0.0.0.0:5000->5000/tcp   secure_registry
root@learning-ocean:~#

In the above command we are running the container named secure_registry along with that we have provided parameters to map the port, using -v flag we are mounting the certificate present in /home/gaurav/certs directory to the certs folder inside the container and we are passing environment variables using -e flag for passing the path to the self-signed certificate and the generated key present inside the container.

So our registry is up and running now.

Let's try to push an image to the registry again to the insecure registry 10.0.2.15:5000. This gives an error message.

root@learning-ocean:~# docker image push 192.168.1.11:5000/redis
Using default tag: latest
The push refers to repository [192.168.1.11:5000/redis]
Get https://192.168.1.11:5000/v2/: x509: cannot validate certificate for 192.168.1.11 because it doesnt contain any IP SANs
root@learning-ocean:~#

Now let's try to push an image to the domain name 'repo.docker.local' for which we generated the self-signed certificate.

Using the below command we tag an image to the new address.

root@learning-ocean:~# docker image tag redis repo.docker.local:5000/redis
root@learning-ocean:~# docker image push  repo.docker.local:5000/redis
Using default tag: latest
The push refers to repository [repo.docker.local:5000/redis]
Get https://repo.docker.local:5000/v2/: dial tcp: lookup repo.docker.local: Temporary failure in name resolution
root@learning-ocean:~#

But again on trying to push this image we get an error which says docker is unable to resolve this domain name.

For fixing this issue we need to do the following step. Edit the hosts file and add an entry for our domain name.

root@learning-ocean:~# cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 ubuntuserver
192.168.1.11 repo.docker.local
root@learning-ocean:~#

Now let's again try to push the image.

root@learning-ocean:~# docker image push  repo.docker.local:5000/redis
Using default tag: latest
The push refers to repository [repo.docker.local:5000/redis]
262de04acb7e: Pushed
45f6df634253: Pushed
e46136075591: Pushed
11f991845040: Pushed
dd1ebb1f5319: Pushed
814bff734324: Pushed
latest: digest: sha256:1bd57e1a42b99ae53412b582784d0362fa8205243ce5f289cb4f76de2907cb97 size: 1574
root@learning-ocean:~#

The image is pushed and we have set up our secure register successfully!