From 411e148450fa267eaff6e243ad1f6411f173f982 Mon Sep 17 00:00:00 2001
From: Mark Goddard <mark@stackhpc.com>
Date: Wed, 1 Jul 2020 13:39:19 +0100
Subject: [PATCH] Docker registry TLS

Adds support for enabling TLS on the Docker registry.

Change-Id: Idac6a1dfb17f4a22b3043531d1181a5585cafe6a
Story: 2007952
Task: 40428
---
 ansible/docker-registry.yml                   |  1 +
 ansible/group_vars/all/docker-registry        |  9 ++
 .../roles/docker-registry/defaults/main.yml   | 29 ++++++-
 .../roles/docker-registry/handlers/main.yml   | 12 +++
 .../roles/docker-registry/tasks/config.yml    | 34 ++++++++
 .../roles/docker-registry/tasks/deploy.yml    |  4 +-
 .../roles/docker-registry/tasks/upgrade.yml   |  1 +
 doc/source/configuration/docker-registry.rst  | 82 +++++++++++++++++++
 doc/source/configuration/index.rst            |  1 +
 etc/kayobe/docker-registry.yml                | 19 ++++-
 .../docker-registry-tls-a823fc5717ef2b52.yaml |  4 +
 11 files changed, 189 insertions(+), 7 deletions(-)
 create mode 100644 ansible/roles/docker-registry/handlers/main.yml
 create mode 100644 ansible/roles/docker-registry/tasks/config.yml
 create mode 100644 doc/source/configuration/docker-registry.rst
 create mode 100644 releasenotes/notes/docker-registry-tls-a823fc5717ef2b52.yaml

diff --git a/ansible/docker-registry.yml b/ansible/docker-registry.yml
index f76ab78a..c721e023 100644
--- a/ansible/docker-registry.yml
+++ b/ansible/docker-registry.yml
@@ -13,3 +13,4 @@
   roles:
     - role: docker-registry
       docker_registry_action: "{{ kayobe_action }}"
+      docker_registry_config_path: "{{ config_path }}/docker-registry"
diff --git a/ansible/group_vars/all/docker-registry b/ansible/group_vars/all/docker-registry
index 5ee86f55..31999532 100644
--- a/ansible/group_vars/all/docker-registry
+++ b/ansible/group_vars/all/docker-registry
@@ -21,3 +21,12 @@ docker_registry_port: 4000
 
 # Name or path to use as the volume for the docker registry.
 docker_registry_datadir_volume: docker_registry
+
+# Whether to enable TLS for the registry.
+docker_registry_enable_tls: false
+
+# Path to a TLS certificate to use when TLS is enabled.
+docker_registry_cert_path:
+
+# Path to a TLS key to use when TLS is enabled.
+docker_registry_key_path:
diff --git a/ansible/roles/docker-registry/defaults/main.yml b/ansible/roles/docker-registry/defaults/main.yml
index 110a0e89..ba319fba 100644
--- a/ansible/roles/docker-registry/defaults/main.yml
+++ b/ansible/roles/docker-registry/defaults/main.yml
@@ -17,18 +17,23 @@ docker_registry_enabled: true
 # pull through cache.
 docker_registry_env: {}
 
+# Dict of environment variables to provide to the docker registry container
+# when TLS is enabled.
+docker_registry_env_tls:
+  REGISTRY_HTTP_TLS_CERTIFICATE: "{{ docker_registry_config_path }}/cert.pem"
+  REGISTRY_HTTP_TLS_KEY: "{{ docker_registry_config_path }}/key.pem"
+
 # Service deployment definition.
 docker_registry_services:
   docker_registry:
     container_name: docker_registry
     env: "{{ docker_registry_env }}"
+    env: "{{ (docker_registry_env_tls if docker_registry_enable_tls | bool else {}) | combine(docker_registry_env) }}"
     enabled: "{{ docker_registry_enabled }}"
     image: "{{ docker_registry_image_full }}"
     ports:
       - "{{ docker_registry_port }}:5000"
-    volumes:
-      - "/etc/localtime:/etc/localtime:ro"
-      - "{{ docker_registry_datadir_volume }}:/var/lib/registry"
+    volumes: "{{ docker_registry_volumes | select | list }}"
 
 # The port on which the docker registry server should listen.
 docker_registry_port: 5000
@@ -37,6 +42,18 @@ docker_registry_port: 5000
 # Defaults to ``docker_registry``.
 docker_registry_datadir_volume: docker_registry
 
+# Path in which to store docker registry configuration.
+docker_registry_config_path: "/etc/docker/registry"
+
+# Whether to enable TLS for the registry.
+docker_registry_enable_tls: false
+
+# Path to a TLS certificate to use when TLS is enabled.
+docker_registry_cert_path:
+
+# Path to a TLS key to use when TLS is enabled.
+docker_registry_key_path:
+
 ####################
 # Docker
 ####################
@@ -47,5 +64,11 @@ docker_registry_image: "{{ docker_registry_namespace ~ '/' if docker_registry_na
 docker_registry_tag: "latest"
 docker_registry_image_full: "{{ docker_registry_image }}:{{ docker_registry_tag }}"
 
+# List of volumes to be mounted to the docker registry container.
+docker_registry_volumes:
+  - "/etc/localtime:/etc/localtime:ro"
+  - "{{ docker_registry_datadir_volume }}:/var/lib/registry"
+  - "{% if docker_registry_enable_tls | bool %}{{ docker_registry_config_path }}:{{ docker_registry_config_path }}:ro{% endif %}"
+
 docker_registry_restart_policy: "unless-stopped"
 #docker_registry_restart_retries:
diff --git a/ansible/roles/docker-registry/handlers/main.yml b/ansible/roles/docker-registry/handlers/main.yml
new file mode 100644
index 00000000..a394557b
--- /dev/null
+++ b/ansible/roles/docker-registry/handlers/main.yml
@@ -0,0 +1,12 @@
+---
+- name: Restart docker-registry container
+  docker_container:
+    name: "{{ item.value.container_name }}"
+    state: started
+    restart: True
+    # NOTE: The image argument shouldn't be required, but without it this
+    # handler fails on Ansible 2.3. Related bug:
+    # https://github.com/ansible/ansible/issues/21188.
+    image: "{{ item.value.image }}"
+  with_dict: "{{ docker_registry_services }}"
+  when: item.value.enabled
diff --git a/ansible/roles/docker-registry/tasks/config.yml b/ansible/roles/docker-registry/tasks/config.yml
new file mode 100644
index 00000000..56825630
--- /dev/null
+++ b/ansible/roles/docker-registry/tasks/config.yml
@@ -0,0 +1,34 @@
+---
+- name: Ensure configuration path exists
+  file:
+    path: "{{ docker_registry_config_path }}"
+    state: directory
+    owner: "{{ ansible_user_uid }}"
+    group: "{{ ansible_user_gid }}"
+    mode: 0750
+  become: True
+  when: docker_registry_enable_tls | bool
+
+- name: Ensure TLS certificate exists
+  copy:
+    src: "{{ docker_registry_cert_path }}"
+    dest: "{{ docker_registry_config_path }}/cert.pem"
+    owner: "{{ ansible_user_uid }}"
+    group: "{{ ansible_user_gid }}"
+    mode: 0600
+  become: True
+  when: docker_registry_enable_tls | bool
+  notify:
+    - Restart docker-registry container
+
+- name: Ensure TLS key exists
+  copy:
+    src: "{{ docker_registry_key_path }}"
+    dest: "{{ docker_registry_config_path }}/key.pem"
+    owner: "{{ ansible_user_uid }}"
+    group: "{{ ansible_user_gid }}"
+    mode: 0600
+  become: True
+  when: docker_registry_enable_tls | bool
+  notify:
+    - Restart docker-registry container
diff --git a/ansible/roles/docker-registry/tasks/deploy.yml b/ansible/roles/docker-registry/tasks/deploy.yml
index caec5d3c..e8bb7f7f 100644
--- a/ansible/roles/docker-registry/tasks/deploy.yml
+++ b/ansible/roles/docker-registry/tasks/deploy.yml
@@ -1,4 +1,6 @@
 ---
+- import_tasks: config.yml
+
 - name: Ensure Docker registry container is running
   docker_container:
     env: "{{ item.value.env }}"
@@ -10,5 +12,5 @@
     restart_policy: "{{ docker_registry_restart_policy }}"
     restart_retries: "{{ docker_registry_restart_retries | default(omit) }}"
     state: "{{ item.value.enabled | ternary('started', 'absent') }}"
-    volumes: "{{ item.value.volumes }}"
+    volumes: "{{ item.value.volumes | select | list }}"
   with_dict: "{{ docker_registry_services }}"
diff --git a/ansible/roles/docker-registry/tasks/upgrade.yml b/ansible/roles/docker-registry/tasks/upgrade.yml
index 99348ae9..dca45c12 100644
--- a/ansible/roles/docker-registry/tasks/upgrade.yml
+++ b/ansible/roles/docker-registry/tasks/upgrade.yml
@@ -1,3 +1,4 @@
 ---
+- import_tasks: config.yml
 - include_tasks: pull.yml
 - include_tasks: deploy.yml
diff --git a/doc/source/configuration/docker-registry.rst b/doc/source/configuration/docker-registry.rst
new file mode 100644
index 00000000..58334a6f
--- /dev/null
+++ b/doc/source/configuration/docker-registry.rst
@@ -0,0 +1,82 @@
+.. _configuration-docker-registry:
+
+===============
+Docker registry
+===============
+
+This section covers configuration of the Docker registry that may be deployed,
+by default on the seed host. Docker registry configuration is typically applied
+in ``${KAYOBE_CONFIG_PATH}/docker-registry.yml``. Consult the `Docker registry
+documentation <https://docs.docker.com/registry/>`__ for further details of
+registry usage and configuration.
+
+The registry is deployed during the ``kayobe seed host configure`` command.
+
+Configuring the registry
+========================
+
+``docker_registry_enabled``
+    Whether a docker registry is enabled. Default is ``false``. When set to
+    ``true``, the Docker registry is deployed on all hosts in the
+    ``docker-registry`` group. By default this includes the seed host.
+``docker_registry_env``
+    Dict of environment variables to provide to the docker registry container.
+    This allows to configure the registry by overriding specific configuration
+    options, as described at https://docs.docker.com/registry/configuration/
+    For example, the registry can be configured as a pull through cache to
+    Docker Hub by setting REGISTRY_PROXY_REMOTEURL to
+    "https://registry-1.docker.io".  Note that it is not possible to push to a
+    registry configured as a pull through cache. Default is ``{}``.
+``docker_registry_port``
+    The port on which the docker registry server should listen. Default is
+    4000.
+``docker_registry_datadir_volume``
+    Name or path to use as the volume for the docker registry. Default is
+    ``docker_registry``.
+
+TLS
+---
+
+It is recommended to enable TLS for the registry.
+
+``docker_registry_enable_tls``
+    Whether to enable TLS for the registry. Default is ``false``.
+
+``docker_registry_cert_path``
+    Path to a TLS certificate to use when TLS is enabled. Default is none.
+
+``docker_registry_key_path``
+    Path to a TLS key to use when TLS is enabled. Default is none.
+
+For example, the certificate and key could be stored with the Kayobe
+configuration, under ``${KAYOBE_CONFIG_PATH}/docker-registry/``. These files
+may be encrypted via Ansible Vault.
+
+.. code-block:: yaml
+   :caption: ``docker-registry.yml``
+
+   docker_registry_enable_tls: true
+   docker_registry_cert_path: "{{ kayobe_config_path }}/docker-registry/cert.pem
+   docker_registry_key_path: "{{ kayobe_config_path }}/docker-registry/key.pem
+
+Using the registry
+==================
+
+Enabling the registry does not automatically set the configuration for Docker
+engine to use it. This should be done via the :ref:`docker_registry variable
+<configuration-hosts-docker>`.
+
+TLS
+---
+
+If the registry is using a privately signed TLS certificate, it is necessary to
+:ref:`configure Docker engine with the CA certificate
+<configuration-hosts-docker>`.
+
+If TLS is enabled, Docker engine should be configured to use HTTPS to
+communicate with it:
+
+.. code-block:: yaml
+   :caption: ``kolla/globals.yml``
+
+   docker_registry_insecure: false
diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst
index 89e58a4e..5ee94898 100644
--- a/doc/source/configuration/index.rst
+++ b/doc/source/configuration/index.rst
@@ -14,5 +14,6 @@ Configuration Guide
    kolla-ansible
    bifrost
    ironic-python-agent
+   docker-registry
    seed-custom-containers
    nova-cells
diff --git a/etc/kayobe/docker-registry.yml b/etc/kayobe/docker-registry.yml
index 85308a43..5be695f7 100644
--- a/etc/kayobe/docker-registry.yml
+++ b/etc/kayobe/docker-registry.yml
@@ -2,7 +2,7 @@
 ###############################################################################
 # Docker registry configuration.
 
-# Whether a docker registry is enabled.
+# Whether a docker registry is enabled. Default is false.
 #docker_registry_enabled:
 
 # Dict of environment variables to provide to the docker registry container.
@@ -11,12 +11,25 @@
 # For example, the registry can be configured as a pull through cache to Docker
 # Hub by setting REGISTRY_PROXY_REMOTEURL to "https://registry-1.docker.io".
 # Note that it is not possible to push to a registry configured as a
-# pull through cache.
+# pull through cache. Default is an empty dict.
 #docker_registry_env:
 
-# The port on which the docker registry server should listen.
+# The port on which the docker registry server should listen. Default is 4000.
 #docker_registry_port:
 
+# Name or path to use as the volume for the docker registry. Default is
+# 'docker_registry'.
+#docker_registry_datadir_volume:
+
+# Whether to enable TLS for the registry. Default is false.
+#docker_registry_enable_tls:
+
+# Path to a TLS certificate to use when TLS is enabled. Default is none.
+#docker_registry_cert_path:
+
+# Path to a TLS key to use when TLS is enabled. Default is none.
+#docker_registry_key_path:
+
 ###############################################################################
 # Dummy variable to allow Ansible to accept this file.
 workaround_ansible_issue_8743: yes
diff --git a/releasenotes/notes/docker-registry-tls-a823fc5717ef2b52.yaml b/releasenotes/notes/docker-registry-tls-a823fc5717ef2b52.yaml
new file mode 100644
index 00000000..294cfa59
--- /dev/null
+++ b/releasenotes/notes/docker-registry-tls-a823fc5717ef2b52.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Adds support for deploying a Docker registry with TLS.
-- 
GitLab