Create and Manage a secure private container registry for internal usage

A docker image registry is used to store docker images and maintain their versions. Container runtime engines such as Podman, Containerd, CRI-O, rkt, etc are typically used to the push and pull images using their respective commands. Most of these runtimes have a way to create and maintain a local registry for the machine on which they are installed. However when sharing images between different machines, we need a centralized repository where one can push and pull images. There are lot of PaaS registry services are available in the market already. However, sometimes you may want to have your own personal/private registry where you can control what images are available for your runtimes.

This article outlines the steps needed to implement a private registry as a container and store images in the same for internal use. 

Install Container Engine and httpd-tools

The steps to install container engines will vary depending upon the engine you want to run, its version and the OS that you want to use. Please follow the instructions related to your engine. For the purpose of this blog post, we’ll install and use docker engine which uses containerd as runtime, on the CentOS 7.

To install httpd-tools, we can use below command:

# yum install -y httpd-tools

Create Directories for Registry

We’ll be creating the registry in /opt/registry/. For this, we’ll need to create three directories – auth, certs and data inside this path. For this, we can use below command:

# mkdir -p /opt/registry/{auth,certs,data}

Out of these directories, the auth directory is used to store the authentication information, certs directory is used to stores certificates and data is used to store the image data.

If you are looking for a portable registry, its better to use removable storage for creating registries.

Create Credentials for accessing Registry

For this, we can use the htpasswd utility to generate a file containing the credentials for accessing the registry:

# htpasswd -bBc /opt/registry/auth/htpasswd registryuser registryuserpassword

In the above command, registryuser is the username and registryuserpassword is the associated password. This will create a Bcrypt Htpasswd file named htpasswd in the /opt/registry/auth/ directory.

Create Certificate to Secure Registry

To secure the registry with TLS, we need to use a certificate for it. You can either opt for a certificate provided by a trusted authority (internal or external) or by using a simple self-signed (not recommended) certificate. We can generate self-signed certificates using the OpenSSL utility, as below:

# openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/registry/certs/domain.key -x509 -days 365 -out /opt/registry/certs/domain.crt

Where:

  • req tells OpenSSL to generate and process certificate requests.
  • -newkey tells OpenSSL to create a new private key and matching certificate request.
  • rsa:4096 tells OpenSSL to generate an RSA key with 4096 bits.
  • -nodes tells OpenSSL there is no password requirement for the private key. The private key will not be encrypted.
  • -sha256 tells OpenSSL to use the sha256 to sign the request.
  • -keyout tells OpenSSL the name and location to store new key.
  • -x509 tells OpenSSL to generate a self-signed certificate.
  • -days tells OpenSSL the number of days the key pair is valid for.
  • -out tells OpenSSL where to store the certificate.

Enter the respective options for your certificate. The CN= value is the hostname of your host. The host’s hostname should be resolvable by DNS or the /etc/hosts file.

Since we are using a self-signed certificate, it will not be trusted by container engine runtimes by default. So, we’ll need to run below commands on client machines:

# cp /opt/registry/certs/domain.crt /etc/pki/ca-trust/source/anchors/
# update-ca-trust
# trust list | grep -i "hostname"

Start the Container Registry

For this, we can use the official registry image provided by Docker, Inc as docker.io/library/registry:latest. Since we are using docker engine in our case, we can run it using below command:

docker run --name myregistry \
 -p 5000:5000 \
 -v /opt/registry/data:/var/lib/registry:z \
 -v /opt/registry/auth:/auth:z \
 -e "REGISTRY_AUTH=htpasswd" \
 -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
 -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
 -v /opt/registry/certs:/certs:z \
 -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt" \
 -e "REGISTRY_HTTP_TLS_KEY=/certs/domain.key" \
 -e REGISTRY_COMPATIBILITY_SCHEMA1_ENABLED=true \
 -d \
 docker.io/library/registry:latest

In above command, we have mapped local port 5000 to the container image port 5000.

If we have firewall running on the machine, we need to create allow rules to permit traffic:

# firewall-cmd --add-port=5000/tcp --zone=internal --permanent 
# firewall-cmd --add-port=5000/tcp --zone=public --permanent
# firewall-cmd --reload

Verify if the access to Registry

The curl command can be used to access the registry:

# curl -u registryuser:registryuserpassword https://<hostname>:5000/v2/_catalog
{"repositories":[]}

The certificate can be verified using:

# openssl s_client -connect <hostname>:5000 -servername <hostname>

Be sure to trust the certificate from earlier or use curl’s -k switch to ignore certificate verification.

Verify Login/Logout to/from the Registry

We can use the docker login command to log into the registry:

# docker login <hostname>:5000
 Username: registryuser
 Password: 
 WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
 Configure a credential helper to remove this warning. See
 https://docs.docker.com/engine/reference/commandline/login/#credentials-store
 Login Succeeded

We can use docker logout command to logout of the registry.

Push/Pull Images from the Registry

At this point, the registry we created is empty. So we first need to push the image to the registry. To push to the registry, use docker tag to first tag the image and the registry location, and then push the image using docker push:

# docker tag <image id|<repo name/image name>> registry:5000/<repo>/<image>

We can verify the images present in the container registry using the curl command:

Same way, we can use docker pull command to pull the images from our private registry.

Managing Registry Lifecycle

Stop registry:

# docker stop myregistry

Remove registry:

# docker rm myregistry

Delete registry image:

# docker rmi registry:latest

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s