Introduction
Docker has become an essential tool for developers and DevOps teams, enabling the seamless deployment and management of containerized applications. When working with a private Docker registry, securing communication between Docker clients and the registry server is crucial for protecting your container images.
In this lab, you will learn how to set up a local Docker registry and secure it using a self-signed SSL certificate. This approach is perfect for development environments, testing, and learning scenarios where you need a secure registry without purchasing a certificate from a trusted authority.
By the end of this tutorial, you will have a functioning Docker registry that uses HTTPS for secure communication, allowing you to safely push and pull Docker images within your development environment.
Setting Up a Basic Docker Registry
Before securing our Docker registry with SSL, let's first understand what a Docker registry is and set up a basic registry to work with.
What is a Docker Registry?
A Docker registry is a storage and distribution system for Docker container images. It allows you to:
- Store your Docker images in a central location
- Share images with your team or organization
- Control access to your images
- Deploy containers from your images across different environments
Docker Hub is the most well-known public registry, but for many organizations, having a private registry is essential for security, performance, and control.
Setting Up a Basic Registry
Let's start by running a simple, insecure Docker registry:
Create a directory to store our registry data:
mkdir -p ~/project/registry-dataRun a basic Docker registry using the official image:
docker run -d -p 5000:5000 --restart=always --name registry -v ~/project/registry-data:/var/lib/registry registry:2This command:
- Runs the registry container in detached mode (
-d) - Maps port 5000 on your host to port 5000 in the container
- Sets the container to restart automatically if it stops
- Names the container "registry"
- Mounts the directory we created to store registry data
- Runs the registry container in detached mode (
Verify the registry is running:
docker psYou should see output similar to:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a7d8098de3a2 registry:2 "/entrypoint.sh /etc…" 5 seconds ago Up 4 seconds 0.0.0.0:5000->5000/tcp registryTest the registry by pushing a sample image:
First, pull a small image:
docker pull hello-worldTag it for your local registry:
docker tag hello-world localhost:5000/hello-worldPush it to your registry:
docker push localhost:5000/hello-worldYou should see output showing the image being pushed to your registry.
Stop the registry container before we secure it in the next steps:
docker stop registry docker rm registry
This basic registry works but has a significant limitation: it uses HTTP, which is insecure. Docker clients refuse to push or pull from insecure registries by default. In the next steps, we'll secure our registry with SSL.
Generating a Self-Signed SSL Certificate
Now that we understand the basics of Docker Registry, let's secure it by generating a self-signed SSL certificate. This certificate will enable HTTPS communication with our registry.
What is a Self-Signed Certificate?
A self-signed certificate is an SSL certificate that isn't signed by a trusted Certificate Authority (CA). While not suitable for production environments exposed to the public internet, self-signed certificates are perfect for development, testing, and internal applications.
Generate the Certificate and Key
We'll use OpenSSL, a widely-used cryptography toolkit, to create our certificate:
Create a directory to store our certificates:
mkdir -p ~/project/registry-certs cd ~/project/registry-certsGenerate a private key:
openssl genrsa -out registry.key 2048This command generates a 2048-bit RSA private key. You should see no output if successful.
Create a certificate signing request (CSR) using the private key:
openssl req -new -key registry.key -out registry.csrYou'll be prompted to enter information that will be included in your certificate:
Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California Locality Name (eg, city) []:San Francisco Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Company Organizational Unit Name (eg, section) []:IT Common Name (e.g. server FQDN or YOUR name) []:localhost Email Address []:admin@example.comNote: For
Common Name, enterlocalhostsince we'll be connecting to the registry on our local machine.You'll also be asked for:
Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:You can leave these blank by pressing Enter.
Generate the self-signed certificate using the CSR:
openssl x509 -req -days 365 -in registry.csr -signkey registry.key -out registry.crtThis command creates a self-signed certificate valid for 365 days.
You should see output similar to:
Signature ok subject=C = US, ST = California, L = San Francisco, O = Example Company, OU = IT, CN = localhost, emailAddress = admin@example.com Getting Private keyVerify that the certificate and key files have been created:
ls -lYou should see three files:
total 12 -rw-r--r-- 1 labex labex 1220 [date] registry.crt -rw-r--r-- 1 labex labex 1054 [date] registry.csr -rw-r--r-- 1 labex labex 1679 [date] registry.key
Now we have the necessary files to secure our Docker registry. In the next step, we'll configure our registry to use this certificate for HTTPS communication.
Configuring Docker Registry with SSL Certificate
Now that we have our self-signed certificate, we can configure our Docker registry to use SSL for secure communication.
Setting Up the Secure Registry
First, let's create a simple configuration file for our registry. This file will specify the HTTPS settings:
mkdir -p ~/project/registry-config cd ~/project/registry-config nano config.ymlAdd the following configuration to the file:
version: 0.1 storage: filesystem: rootdirectory: /var/lib/registry http: addr: 0.0.0.0:5000 tls: certificate: /certs/registry.crt key: /certs/registry.keyThis configuration tells the registry to:
- Use the filesystem for storage
- Listen on all interfaces on port 5000
- Use TLS (HTTPS) with our certificate and key
Save and exit by pressing
Ctrl+X, thenY, and thenEnter.Now let's run our registry with the SSL certificate:
docker run -d -p 5000:5000 --restart=always --name registry \ -v ~/project/registry-data:/var/lib/registry \ -v ~/project/registry-certs:/certs \ -v ~/project/registry-config/config.yml:/etc/docker/registry/config.yml \ registry:2This command:
- Mounts our certificate and key directory
- Mounts our configuration file
- Uses the same data directory we created earlier
Verify the registry is running:
docker psYou should see output showing the registry container is running.
Configuring Docker Client to Trust the Certificate
By default, Docker clients don't trust self-signed certificates. We need to tell Docker to trust our certificate:
Create a directory for Docker to store trusted certificates:
sudo mkdir -p /etc/docker/certs.d/localhost:5000Copy our certificate to this directory:
sudo cp ~/project/registry-certs/registry.crt /etc/docker/certs.d/localhost:5000/ca.crtRestart the Docker service to pick up the changes:
sudo systemctl restart dockerThis may take a few seconds to complete.
Since restarting Docker will stop our registry container, let's start it again:
docker start registryVerify the registry is running again:
docker ps
Now our Docker registry is configured to use HTTPS with our self-signed certificate, and our Docker client is configured to trust this certificate when connecting to localhost:5000.
Testing the Secure Docker Registry
Now that we have our Docker registry running with SSL, let's test it by pushing and pulling images. This will confirm that everything is working correctly.
Testing with a Sample Image
First, let's pull a sample image to use for testing:
docker pull alpine:latestYou should see output showing Docker downloading the Alpine Linux image.
Tag the image for our secure registry:
docker tag alpine:latest localhost:5000/alpine:latestThis command creates a new tag that points to our local registry.
Push the image to our secure registry:
docker push localhost:5000/alpine:latestYou should see output showing the image layers being pushed to your registry:
The push refers to repository [localhost:5000/alpine] 213ec9aee27d: Pushed latest: digest: sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f size: 528Remove the local image to ensure we're pulling from the registry:
docker rmi localhost:5000/alpine:latest docker rmi alpine:latestPull the image from our secure registry:
docker pull localhost:5000/alpine:latestYou should see output showing Docker downloading the image from your registry:
latest: Pulling from alpine 213ec9aee27d: Pull complete Digest: sha256:69e70a79f2d41ab5d637de98c1e0b055206ba40a8145e7bddb55ccc04e13cf8f Status: Downloaded newer image for localhost:5000/alpine:latest localhost:5000/alpine:latest
Verifying Registry Contents
Let's examine the contents of our registry using the Docker Registry API:
List all repositories in the registry:
curl -X GET https://localhost:5000/v2/_catalog --cacert ~/project/registry-certs/registry.crtYou should see output like:
{ "repositories": ["alpine", "hello-world"] }This shows all the images we've pushed to our registry.
List all tags for the alpine repository:
curl -X GET https://localhost:5000/v2/alpine/tags/list --cacert ~/project/registry-certs/registry.crtYou should see output like:
{ "name": "alpine", "tags": ["latest"] }
Understanding What We've Accomplished
Let's review what we've done:
- Set up a Docker registry with HTTPS using a self-signed SSL certificate
- Configured our Docker client to trust this certificate
- Successfully pushed and pulled images to and from our secure registry
This setup provides:
- Encrypted communication: All data transferred between Docker client and registry is encrypted.
- Authentication foundation: SSL is the first step toward implementing authentication.
- Docker client compatibility: Docker clients require HTTPS by default for non-localhost registries.
You can now use this secure registry for your development and testing needs. For production environments, you would typically use a certificate from a trusted certificate authority instead of a self-signed certificate.
Summary
Congratulations! You have successfully set up a secure Docker registry using a self-signed SSL certificate. Here's what you've accomplished:
- Set up a basic Docker registry and understood its purpose and functionality
- Generated a self-signed SSL certificate for securing your registry
- Configured the Docker registry to use HTTPS with your certificate
- Configured your Docker client to trust the self-signed certificate
- Successfully tested the secure registry by pushing and pulling images
These skills allow you to create secure private registries for your development and testing environments. Private registries give you control over where your Docker images are stored and who can access them, while SSL encryption ensures that your data remains secure during transmission.
For production environments, you would typically use certificates from a trusted certificate authority, but the configuration process would be similar to what you've learned in this lab.
Now you can confidently implement secure Docker registries in your own projects and development workflows, enhancing the security of your containerized applications.



