Run our own Docker registry with self-signed certificate with Ansible

ansible docker networking nginx vault

In this article we are going to install Docker registry and use it via HTTPS with our own certificate authority to manage our Docker images.

Prerequisites

We’ll need Ansible installed locally and Docker installed in the machine where the Docker registry will be installed. In this example, we will use certificates from Vault, more details in this article. NGINX will be used as a reverse proxy, to define NGINX sites with Ansible see this article or this Ansible collection.

Install Docker registry

We are going to install the Docker registry and a UI webapp, both will be running over HTTPS. Let’s say that our registry URL is https://registry.homelab.local and our UI webapp, https://registry-ui.homelab.local.

Create the registry configuration file (files/config.yml), allowing requests from the webapp:

version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
    Access-Control-Allow-Origin: ["https://registry-ui.homelab.local"]
    Access-Control-Allow-Methods: [HEAD, GET, OPTIONS, DELETE]
    Access-Control-Allow-Headers: [Authorization, Accept, Cache-Control]
    Access-Control-Max-Age: [1728000]
    Access-Control-Allow-Credentials: [true]
    Access-Control-Expose-Headers: [Docker-Content-Digest]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

Setup and run Docker registry:

- name: create data directory
  file:
    path: registry_data/registry
    state: directory

- name: config registry
  copy:
    src: files/config.yml
    dest: registry_data/config.yml

- name: run registry
  docker_container:
    name: registry
    state: started
    image: registry
    restart_policy: unless-stopped
    ports:
      - "5000:5000"
    volumes:
      - "{{ ansible_user_dir }}/registry_data/registry:/var/lib/registry"
      - "{{ ansible_user_dir }}/registry_data/config.yml:/etc/docker/registry/config.yml"

Run Docker registry UI:

- name: run registry ui
  docker_container:
    name: registry-ui
    state: started
    image: joxit/docker-registry-ui
    restart_policy: unless-stopped
    ports:
      - "8080:80"
    env:
      REGISTRY_URL: "https://registry.homelab.local"

Setup NGINX

Assume we have our SSL certificate and private key in /etc/ssl issued from our CA, create the server records for https://registry.homelab.local and https://registry-ui.homelab.local using the Ansible module mmas.nginx.nginx_site:

- name: define registry site
  mmas.nginx.nginx_site:
    name: registry.homelab.local
    log_name: regsitry
    proxy_pass: "http://{{ inventory_hostname }}:5000"
    sslcert: /etc/ssl/certs/homelab.local.crt
    sslkey: /etc/ssl/private/homelab.local.pem
  become: yes
  notify:
    - reload nginx

- name: define registry ui site
  mmas.nginx.nginx_site:
    name: registry-ui.homelab.local
    log_name: regsitry
    proxy_pass: "http://{{ inventory_hostname }}:8080"
    sslcert: /etc/ssl/certs/homelab.local.crt
    sslkey: /etc/ssl/private/homelab.local.pem
  become: yes
  notify:
    - reload nginx

If Docker registry and NGINX are not running in the same machine, the proxy_pass must point to the IP where registry is running.

The handler triggered can be defined as follows:

- name: reload nginx
  service:
    name: nginx
    state: reloaded
  become: yes

Import root certificate

Since we haven’t imported the root certificate into Docker, if we try to use the registry now, we’ll get the following error:

Get "https://registry.homelab.local/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority

We must import the certificate to the machine that will use the registry, so we’ll run the following tasks in hosts: localhost using the collection mmas.hashi_vault.vault_pki_root.

From the previous article, the subject common name of our root certificate is “Vault Root”, so:

- name: download root certificate
  set_fact:
    root_ca: "{{ lookup('mmas.hashi_vault.vault_pki_root', 'Vault Root').certificate }}"

- name: create docker certificates folder
  file:
    path: /etc/docker/certs.d/registry.homelab.local
    state: directory
  become: yes

- name: install root certificate in docker
  copy:
    dest: /etc/docker/certs.d/registry.homelab.local/root.crt
    content: "{{ root_ca }}"
  become: yes

To allow pulls from Kubernetes with containerd, we can install the certificate and restart containerd:

- name: install root certificate
  copy:
    dest: /usr/local/share/ca-certificates/root.crt
    content: "{{ root_ca }}"
  become: yes
  notify:
    - update ca certificates
    - restart containerd

With the handlers:

- name: update ca certificates
  command: update-ca-certificates
  become: yes

- name: restart containerd
  service:
    name: containerd
    state: restarted
  become: yes

Use the registry

Now, we’ll be able to use our Docker registry:

docker image tag myapp registry.homelab.local/myapp
docker push registry.homelab.local/myapp

If we get the error

Request Entity Too Large

we need to increase client_max_body_size in our NGINX configuration (/etc/nginx/nginx.conf):

http {
  [...]
  client_max_body_size 100M;
  [...]
}

To access the UI, we’ll need to import our root certificate in the browser.