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

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

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
root@learning-ocean:~# docker container ls
CONTAINER ID   IMAGE      COMMAND                  CREATED         STATUS         PORTS                    NAMES
9f4a4f911259   registry   "/ /etc…"   5 seconds ago   Up 4 seconds>5000/tcp   secure_registry

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 This gives an error message.

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

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

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 localhost ubuntuserver repo.docker.local

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

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