In this post I will go over the installation of Docker Registry with Basic Authentication and over SSL/TLS (self-signed cert for demonstration purposes). I will also do this on Docker for Mac, which has some interesting things to note.
Let me start by saying that you can use the Docker Hub which is a freely available Hosted registry, but there may be instances where you want to host your own registry to control where your images are being stored, you can also opt to use the commercial Docker Trusted Registry, but I will go over the free open-source Registry.
Create project structure
Password file to support Basic Auth
Certificates to support TLS
Location to store the images
Create Docker Registry container
Using the Registry
Registry API
Create project structure
mac$ mkdir -p registry/{volumes/{auth,certs,data},docker-registry} mac$ cd registry mac$ tree . └── registry ├── docker-registry // WhereI will store the docker-compose.yml file └── volumes ├── auth // Volume for Basic Auth password file ├── certs // Volume for TLS certs └── data // Volumes for images storage
Password file to support Basic Auth
Let’s create a username and password
mac$ htpasswd -Bbn> volumes/auth/htpasswd mac$ cat volumes/auth/htpasswd user1:$2y$05$xOuwA8oaflSZ5wpiJto2/OBOIMTSpZrH69wsaQOsnaKt07Vzu1sBW
Certificates to support TLS
Create a root CA key, which will sign the actual certificates
mac$ openssl genrsa -out volumes/certs/rootCA.key 2048
Create root CA certificate which will need to be installed on the systems that will use the Registry
mac$ openssl req -x509 -new -nodes -key volumes/certs/rootCA.key -days 365 -out volumes/certs/rootCA.crt You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:NY Locality Name (eg, city) []:NY Organization Name (eg, company) [Internet Widgits Pty Ltd]:Technologist Organizational Unit Name (eg, section) []:Tech Common Name (e.g. server FQDN or YOUR name) []:Technologist CA Email Address []:johnATtechnologist
Now that you have a CA, you can sign certificates.
Create a key for the Registry system
mac$ openssl genrsa -out volumes/certs/host.key 2048
Create a Certificate Signing Request (CSR) for the CA to sign
mac$ openssl req -new -key volumes/certs/host.key -out volumes/certs/host.csr You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:NY Locality Name (eg, city) []:NY Organization Name (eg, company) [Internet Widgits Pty Ltd]:Technologist Organizational Unit Name (eg, section) []:Tech Common Name (e.g. server FQDN or YOUR name) []:registry.example.com Email Address []:johnATtechnologist Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
Finally have the root CA sign the CSR
mac$ openssl x509 -req -in volumes/certs/host.csr -CA volumes/certs/rootCA.crt -CAkey volumes/certs/rootCA.key -CAcreateserial -out volumes/certs/host.crt -days 365 Signature ok subject=/C=US/ST=NY/L=NY/O=Technologist/OU=Tech/CN=registry.example.com/emailAddress=johnATtechnologist Getting CA Private Key
Location to store the images
The tree structure should look like below, having the data folder be the image repository
mac $ tree . ├── docker-registry │ └── docker-compose.yml └── volumes ├── auth │ └── htpasswd ├── certs │ ├── host.crt │ ├── host.csr │ ├── host.key │ ├── rootCA.crt │ ├── rootCA.key │ └── rootCA.srl └── data
Create Docker Registry container
Run your container using docker command
mac$ docker run -d -p 5000:5000 --restart=always --name registry \ -v /Users/john/docker/blog/registry/volumes/auth:/auth \ -e "REGISTRY_AUTH=htpasswd" \ -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ -v /Users/john/docker/blog/registry/volumes/certs:/certs \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/host.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/host.key \ registry:2
Or even better, use docker-compose:
mac$ cd docker-registry mac$ cat docker-compose.yml
registry: restart: always image: registry:2 ports: - 5000:5000 environment: REGISTRY_HTTP_TLS_CERTIFICATE: /certs/host.crt REGISTRY_HTTP_TLS_KEY: /certs/host.key REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm volumes: - /Users/john/docker/blog/registry/volumes/data:/var/lib/registry - /Users/john/docker/blog/registry/volumes/certs:/certs - /Users/john/docker/blog/registry/volumes/auth:/auth
Start up container based on docker-compose.yml
mac$ docker-compose up -d Creating dockerregistry_registry_1
See it running
mac$ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------------- dockerregistry_registry_1 /entrypoint.sh /etc/docker ... Up 0.0.0.0:5000->5000/tcp
At this point the Docker Registry is running
Using the Registry
Download an image from Docker Hub (or you can test with your own images built from Dockerfile)
mac$ docker pull ubuntu
Tag the image to point to your new Registry
mac$ docker tag ubuntu registry.example.com:5000/john/ubuntu
Push image to your Registry (see failure due to ‘unknow CA authority’)
mac$ docker push registry.example.com:5000/john/ubuntu The push refers to a repository [registry.example.com:5000/john/ubuntu] Get https://registry.example.com:5000/v1/_ping: x509: certificate signed by unknown authority
At this point, you need to add the root CA cert to your trusted certificates.
On the machine that will pull or push to the registry, you will need to install the rootCA.crt CA certificate created earlier:
docker-engine$ mkdir /etc/docker/certs.d/registry.example.com:5000/ docker-engine$ cp rootCA.crt /etc/docker/certs.d/registry.example.com:5000/rootCA.crt
On the Mac, you are probably using Docker for Mac, which actually runs a small hypervisor xhyve that virtualizes the docker engine. That means we need to place the rootCA.crt CA certificate inside the xhyve VM running the Docker Engine. There is no simple way of doing it and I am using the following method (which must be done everytime you restart the Docker service).
mac$ docker run -v /Users/john/docker/blog/registry/volumes/certs:/macdata -v /etc/docker:/hostdata ubuntu bash -c "mkdir -p /hostdata/certs.d/registry.example.com:5000/;cat /macdata/rootCA.crt > /hostdata/certs.d/registry.example.com:5000/ca.crt"
Now let’s try to Push the image to the Registry again
linux$ docker push registry.example.com:5000/john/ubuntu The push refers to a repository [registry.example.com:5000/john/ubuntu] 4f9d527ff23f: Preparing cc8df16ebbaf: Preparing c3c1fd75be70: Preparing 5c42a1311e7e: Preparing c8305edc6321: Preparing unauthorized: authentication required
Good, we are getting somewhere, TLS is working, and it now asks for Authentication
Authenticate to the Docker Registry
linux$ docker login registry.example.com:5000 Username (testuser): user1 Password: Login Succeeded
Now let’s successfully push our image
linux$ docker push registry.example.com:5000/john/ubuntu The push refers to a repository [registry.example.com:5000/john/ubuntu] 4f9d527ff23f: Pushed cc8df16ebbaf: Pushed c3c1fd75be70: Pushed 5c42a1311e7e: Pushed c8305edc6321: Pushed latest: digest: sha256:35dca5fc91c4890f787a266a057ad76b98ff3af43c75aa7e10962d06f06fd6e5 size: 1357
At this point you can share your Docker image, or incorporate into a development or integration workflow by pulling the image
From any Docker engine system:
linux$ docker login registry.example.com:5000 Username: user1 Password: Login Succeeded
linux$ docker pull registry.example.com:5000/john/ubuntu Using default tag: latest latest: Pulling from john/ubuntu Digest: sha256:35dca5fc91c4890f787a266a057ad76b98ff3af43c75aa7e10962d06f06fd6e5 Status: Downloaded newer image for registry.example.com:5000/john/ubuntu:latest
Registry API
You can use the Docker Registry API https://docs.docker.com/registry/spec/api/ to interact with your Registry.
For example to see a catalog of your images:
$ curl --user user1:password1 --cacert rootCA.crt -X GET https://registry.example.com:5000/v2/_catalog {"repositories":["john/ubuntu"]}