Gotchas / Tips Creating Your Own Private Docker Registry With Self Signed Certificate

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

Creating your own private Docker Registry using a Self Signed Certificate

Creating your own private Docker Registry without authentication, authorization or SSL can be a simple process, but creating a private Docker Registry with SSL support, authentication i.e. Tokens, LDAP, etc.. can be a bit more complicated.

Below I will show you in detail how to create your own private Docker Registry(v2), as well as a number of issues and workarounds. to note, all tests ware completed / tested o ubuntu 17.04(zesty) OS.

Creating your own Docker Registry, requiters addressing a number of issues outlined below. Most of them are addressed later in this article, and some will be addressed in an upcoming article.

Securing your Docker Private Registry Instance(s), includes

  • Option 1: Configure Your own CA with your SSL Certificate
  • Option 2: Buy an SSL Certificate from one of the public providers

Front end your Docker Private Registry with a secure device (can be a Docker instance)

  • Use Simple SSH port forwarding
  • Use pre-configured Nginx Containers to front end your Docker Registry
  • Create your own Nginx Container to protect your Docker Registry
  • Use a hardware Load Balancer such as F5 to front-end and protect your Docker Registry

Authentication and Authorization

  • Use a pre-configured Docker container with all the Docker Registry + LDAP configurations
  • Use a Docker Token service for Authentication and Authorization (works in newer Docker versions)

Other options are: Use a pre-configured GUI Manager to Manage your full Docker registry. Complete packages – include Docker Registry, Authentication and Authorization, Managed with a nice web UI(GUI).
Pre configured Docker Authentication GUI Managers are listed below

Even more options are, to use something like Rancher and Portainer more details is available here, they do much more then just managing a local registry.

Note: Before creating your own Docker Registry, you will need to install the Docker binary application(s). to do so follow the steps outlined here

Setting / configuring your Docker Proxy settings

Note: You can skip this step if you are not using a proxy i.e. if you are not using http_proxy, https_proxy, etc…

The below configuration is an example of my /etc/systemd/system/docker.service.d/http-proxy.conf file with the proxy configuration in it.

[Service]
Environment="HTTP_PROXY=http://proxy.domain.com:3127/"
Environment="HTTPS_PROXY=http://proxy.domain.com:3127/"
Environment="http_proxy=http://proxy.domain.com:3127/"
Environment="https_proxy=http://proxy.domain.com:3127/"
Environment="NO_PROXY=localhost,127.0.0.1,docker,docker.domain.com"
Environment="no_proxy=localhost,127.0.0.1,docker,docker.domain.com"

Note: Make sure to add your local hostname, in this case its docker to your no_proxy list.
I was struggling with this for a while by not having that set. if you do not add your hostname to the no_proxy list, your docker iptables related configuration will not get properly configured, the resutls will be, not being able to access your private docker repository. to note, your localhost:5000 might still work(if no SSL is enabled), but with SSL enabled using an FQDN (like docker.domian.com) is required, which will not work otherwise.

Below are the errors you might get while pushing an image to the local registry, without adding your hostname to the no_proxy list.

# Error while pushing an image to the local repository.
docker push docker.domain.com:5000/alpine
The push refers to a repository [docker.domain.com:5000/alpine]
Get https://docker.domain.com:5000/v1/_ping: Forbidden

# Docker push working as expected.
docker push docker.domain.com:5000/alpine
The push refers to a repository [docker.domain.com:5000/alpine]
e154057080f4: Layer already exists 
latest: digest: sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96 size: 528

Creating and Configuring your Certificates

Next, will move on creating your Certificates – this is needed for SSL communication.
There are a number of methods to use/create your own certificates, below is outlined two options.
Note You can of course use/get your SSL certificate signed by one of the public CA’s, you can then skip to the next step.

  • Manuel creating your certificate and move it in place
  • Automatically creating your certificate and move it in place, using the paulczar/omgwtfssl Docker image

For Docker to fully work using your own SSL certificate, you will also need to create your own CA.

Creating your own Certificates – The Manuel way

As mentioned above, I am using the FQDN i.e. hostname as docker I will use this for the rest of the article.

First create a CA key with a bit size of 2048 (or whatever bit length you like)
openssl genrsa -out ca-key.pem 2048
Sign your CA key
openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj '/C=US/ST=NY/L=NY/O=devtech101.com/CN=docker.domain.com'
Next, Create a server / client certificate and sign with ca

First lets Generate a new server or client key.

openssl genrsa -out key.pem 2048
Create the client certificate request – to be signed by CA

Note: If you like to use one of the public CA’s, the key.pem would be the file send to the public CA for signature.

openssl req -new -key key.pem \
-newkey rsa:2048 -nodes \
-subj '/C=US/ST=NY/L=NY/O=devtech101.com/CN=docker.domain.com' \
-outform pem -out key.csr -keyout key-req.pem
Self sign the client certificate by your own CA (or send to public CA)
openssl x509 -req -in key.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out cert.pem -days 1095 -extensions v3_req

Now, that the certificate(s) are generated(CA and server/client), we are ready to use them.

Copy the certificate(s) to the proper locations (outlined below), the docker registry will then use / trust the new certificate(s), more is explained below.

mkdir -p /docker-data/registry/{data,ssl,config}
cp ca-key.pem  ca.pem  ca.srl  cert.pem  key.csr  key.pem  /docker-data/registry/ssl/.

The process above shows you how to create your certificates manually.
Next will move on to show you how to to automate this process(its your choice which direction to go).

Creating your own Certificates by Using the paulczar/omgwtfsslby Docker image

The Docker image used in automating the Certificates – credit goes to this git repository

The process below will pull the Docker image and generated the certificate(s) and key(s) (CA and Server/Client).

mkdir -p /docker-data/registry/{data,ssl,config}
docker run --rm -v /docker-data/registry/ssl:/certs \
     -e SSL_IP=10.10.10.10 \
     -e SSL_DNS=docker.domain.com \
     -e SSL_SUBJECTdocker.domain.com \
     -e CA_SUBJECT=docker.domain..com \
     -e CA_EXPIRE=1095 \
     paulczar/omgwtfssl

Initial Registry Configuration

Next will move on to, Required Docker SSL settings

Below are the the settings required to get the Docker Registry working with SSL.

echo "# location of registry data
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/docker-data/registry/data

# location of TLK key/cert
REGISTRY_HTTP_TLS_KEY=/docker-data/registry/ssl/key.pem
REGISTRY_HTTP_TLS_CERTIFICATE=/docker-data/registry/ssl/cert.pem

# location of CA of trusted clients
REGISTRY_HTTP_TLS_CLIENTCAS_0=/docker-data/registry/ssl/ca.pem" > /docker-data/registry/config/registry.env

Note: Additional options can be set in the registry.env(depending in the Docker version, RedHat, Ubuntu, etc), I included a small subset sample below.

# The Docker registry configuration file (this file)
DOCKER_REGISTRY_CONFIG=/docker-data/registry/config/registry.env

# The configuration to use from DOCKER_REGISTRY_CONFIG file
SETTINGS_FLAVOR=local

# Address to bind the registry to
REGISTRY_ADDRESS=0.0.0.0

# Port to bind the registry to
REGISTRY_PORT=5000

# Number of workers to handle the connections
GUNICORN_WORKERS=4

Add and trust the self signed certificate(s) on your system

The next step is to add your own CA certificate to the systems trusted certificates store.

Lets create a Docker directory to store our Docker CA certificate, then lets add the certificate location to system CA trusted store.

if ! test -d /usr/share/ca-certificates/docker ; then mkdir -p /usr/share/ca-certificates/docker ;fi
if ! grep -w docker/ca.pem /etc/ca-certificates.conf  ; then echo "docker/ca.pem" >>/etc/ca-certificates.conf ;fi

Next, lets copy the CA certificate to the new trusted store location we just created, then run the system update, to let the system know about the new certificate.

cp /docker-data/registry/ssl/ca.pem /usr/share/ca-certificates/docker/ca.pem
update-ca-certificates

Last, restart the Docker demon for the changes to take effect.

/etc/init.d/docker restart

Note: Restarting Docker is absolutely needed, as the self signed certificate will not work without that.
Below are the errors you might get, if you do not restart the Docker demon even after you added your new SSL certificate(s).

# Error while trying to push an image
docker push docker.domain.com:5000/alpine
The push refers to a repository [docker.domain.com:5000/alpine]
Get https://docker.domain.com:5000/v1/_ping: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "docker.domain.com")

# Error while trying to pull an image
docker pull docker.domain.com:5000/alpine
Error response from daemon: Get https://docker.domain.com:5000/v1/_ping: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "docker.domain.com")

Complete the Docker registry installation and configuration

Now, finally complete docker initial registry installation and configuration.

docker run -d \
	--name registry \
	-p 5000:5000 --restart always \
	-v /docker-data/registry:/opt/registry \	
	--env-file /docker-data/registry/config/registry.env \
     	registry

You can also create a DockerFile with similar options like the one below.

FROM registry:latest

# Add the below for Docker v1 to be able to use the search uri.
ENV SEARCH_BACKEND sqlalchemy
ENV SQLALCHEMY_INDEX_DATABASE sqlite://///docker-data/registry/data/docker-registry.db
ENV STORAGE_PATH /docker-data/registry

CMD exec docker-registry

Then build and run the Docker registry container.

# Build the registry container.
docker build -t registry .

# Start the Docker registry.
docker run -d -p 5000:5000 \
      -v /docker-data/registry:/opt/registry \
      --env-file /docker-data/registry/config/registry.env

Begin using your own Docker registry

Now that the registry is up and running, we can start pushing and pulling images.

Lets pull the busybox image from the public repository.

docker pull busybox

Before we can publish/push the busybox image we now need to tag it.

# Tag the busybox image
docker tag alpine docker.domain.com:5000/busybox:devtech1

# Push the busybox image our private repository
docker push docker.domain.com:5000/busybox:devtech1

Last, lets try to pull the image from our own repository.

docker pull docker.domain.com:5000/busybox:devtech1

Authentication and Authorizations

Now that our private repository is up and running, its time to discuses Docker security options.
Note: The private Docker registry configured above has no security configurations configured, i.e. you can push/pull withany Authentication required.

There are a number of options around Docker Authentication and Authorizations, from using Docker itself, using tokens, LDAP, etc, front-ending Docker with an Nginx Docker image, or front-ending Docker by using a Load Balancer like an F5 handling the security.

Below I will discuss some of the options.

Using SSH for simple Authentication

The simplest configuration, i.e. just adding Authentication in the mix can be using ssh port-forwarding.

To use that, just run the below, change the port and server names to match your configuration.

ssh -N -L 5000:localhost:443 user@docker

Using a Docker pre configured image for Authentication

The next option would be, building an Nginx image, or using a pre-build Nginx Docker images.
By using Nginx as the proxy(front-end) for your Docker Registry, you gain all the befits that Nginx brings to table.
I hope to update this article once available, with the full details.

The next option would be(and especially if you’re looking to utilize your own LDAP), by using one of the Docker images available on github. they include an easy way to configure, point to your own LDAP for Authentication and Authorization.

There are a number of images available on github, each with their own benefits (and weaknesses). a partial list is below.

Private Docker registry GUI Managers

Another option is, to use one of the Docker GUI managers, there are a nice few options available, each have their own strengths and weaknesses.

Below is a partial Docker Web UI/GUI manager list.

Atomic Registry – a Red-hat sponsored project


Portus – a Suse sponsored project


Harbor – a VmWare sponsored project


Shipyard Build on Docker Swarm – Composable Docker Management


Other GUI option are, to use something like Rancher and Portainer more details is available here

Disabling SSL temporarily for testing purposes

For troubleshooting purposes, you might try to disable the SSL in your registry.
Modify your DOCKER_OPTS in the /etc/default/docker Docker file like below.
Note: A docker restart will be required for the changes to take effect.

# /etc/default/docker

# Disable SSL temporarily
DOCKER_OPTS="--dns 8.8.8.8 --ip-masq=true --insecure-registry docker.domain.com:5000"

# Enable SSL (the way it shuld normaly be)
DOCKER_OPTS="--dns 8.8.8.8 --ip-masq=true"

/etc/init.d/docker restart

Private Docker helpful tips

Docker registry v2 searching / tag listing

Docker private registry v2, has no search endpoint, I would love to find out otherwise – please let me know in the comments below. However, their are a few other end-points available to use listed below.
Using Docker for Search in v2 will not work

docker search docker.domain.com:5000/busybox
Error response from daemon: Unexpected status code 404

Using curl will partly work

Getting the full Docker private registry listing
curl -X GET https://docker.domain.com:5000/v2/_catalog
{"repositories":["alpine"]}
Getting the tag list for a specific image
curl -X GET https://docker.domain.com:5000/v2/busybox/tags/list
{"name":"busybox","tags":["latest"]}
Getting the manifest for a specific image
curl -X GET https://docker.domain.com:5000/v2/busybox/manifests/refrence
{"errors":[{"code":"MANIFEST_UNKNOWN","message":"manifest unknown","detail":{"Tag":"refrence"}}]}

Note: You can always create your own little Python program to pull the full catalog and tags, adding to a dictionary, then manipulating that.

What was your experience creating a private Docker registry, please let me know in the comments below.

Helpful Docker registry information script

The below script will proved details about your private Docker registry packages and disk space.

#!/bin/bash

REGISTRY_FILESYSTEM_ROOTDIRECTORY="/opt/registry"

dockeImgList=`find $REGISTRY_FILESYSTEM_ROOTDIRECTORY -print | \
    grep 'v2/repositories' | \
    grep 'current' | \
    grep -v 'link' | \
    sed -e 's/\/_manifests\/tags\//:/' | \
    sed -e 's/\/current//' | \
    sed -e 's/^.*repositories\//    /' | \
    sort`
echo "$dockeImgList"
echo "Number of images: `echo $dockeImgList |wc -l | awk {'print $1'}`"
echo "Disk space used:  `du -hs $REGISTRY_FILESYSTEM_ROOTDIRECTORY`"

A sample output is below.

/tmp/get_docker_info.sh
    busybox:latest
Number of images: 1
Disk space used:  2.1M	/docker-data/registry

You might also like
Managing docker on Ubuntu 17.04 using Rancher or Portainer
Using ZFS for Docker COW Storage layer(s) In Ubuntu 17-04

Leave a Reply

avatar
3000
  Subscribe  
Notify of