Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Commit

Permalink
Add tasks for generating TLS certificates (#17)
Browse files Browse the repository at this point in the history
Add the remaining tasks from our existing docker role. These tasks
create CA, server, and client certificates.

- add a variable `docker_generate_certificates `. If `true`,
certificates will be generated. Defaults to `false`
- add separate task files for creating CA, server, and client
certificates
- a client certificate is created for each client listed in
`docker_client_hostnames `. The certificates are copied to the Ansible
controller
- add description of all new variables to the README
- add to the README a short discussion of when you would want to
generates the certificates

---------

Co-authored-by: Daniel Matthews <[email protected]>
  • Loading branch information
p-j-smith and drmatthews authored Oct 26, 2023
1 parent 20e2391 commit 7d6b491
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ jobs:
steps:
- uses: UCL-MIRSG/.github/actions/[email protected]
with:
ansible-roles-config: ./meta/requirements.yml
pre-commit-config: ./.pre-commit-config.yaml
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@ This role is for installing [docker-ce](https://docs.docker.com/engine/install/)
| `docker_repo_baseurl` | URL to the directory containing the repodata. Defaults to `https://download.docker.com/linux/centos` |
| `docker_yum_package` | The name of the Docker package. Defaults to `docker` |

If you would like to [configure](https://docs.docker.com/engine/security/protect-access/#use-tls-https-to-protect-the-docker-daemon-socket)
your Docker server such that clients can connect to it via TLS, you can also use this role to generate the necessary certificates.
The following variables can be used to configure certificate creation and signing:

| Name | Description |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `docker_generate_certificates` | If `true`, CA, server, and client certificates will be generated. Defaults to `false` |
| `docker_certificate_directory` | Directory in which to store the certificates. Defaults to `/home/docker/.docker` |
| `docker_config_dir` | Docker configuration directory. Defaults to `/etc/docker` |
| `docker_daemon_conf_file` | Docker daemon configuration filename. Defaults to `/etc/docker/daemon.json` |
| `docker_server_hostname` | Hostname of your Docker server. Used for the `commonName` field of the certificate signing request subject. Defaults to `"{{ ansible_host }}"` |
| `docker_server_ip` | IP address of your Docker server. Defaults to `0.0.0.0` |
| `docker_ca_key` | Filename for the CA certificate key. Defaults to `/home/docker/.docker/ca.key` |
| `docker_ca_csr` | Filename for the CA certificate signing request. Defaults to `/home/docker/.docker/ca.csr` |
| `docker_ca_cert` | Filename for the CA certificate. Defaults to `/home/docker/.docker/ca.pem` |
| `docker_server_key` | Filename for the server certificate key. Defaults to `/home/docker/.docker/server-key.pem` |
| `docker_server_csr` | Filename for the server certificate signing request. Defaults to `/home/docker/.docker/server.csr` |
| `docker_server_cert` | Filename for the server certificate. Defaults to `/home/docker/.docker/server-cert.pem` |
| `docker_client_hostnames` | List of hostnames of clients that will connect to the server. Defaults to `[]` |
| `docker_client_certificate_directory` | Directory in which to store the client certificates. Defaults to `/home/docker/.docker/client_certs` |
| `docker_client_certificate_cache_directory` | Directory in which to client certificates will be copied to. Defaults to `~/ansible_persistent_files/docker_certificates` |

If you have specified a list of clients in `docker_client_hostnames`, the certificate for each client will be stored locally on your Ansible
controller in the folder `docker_client_certificate_cache_directory`. You will then need to copy these certificates to the corresponding
client.

## Installation

Include in a requirements.yml file as follows:
Expand Down
32 changes: 30 additions & 2 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,38 @@
# defaults for mirsg.docker
docker_owner: "root"
docker_group: "root"
docker_config_dir: "/etc/docker"
docker_daemon_conf_file: "/etc/docker/daemon.json"

# mirsg.docker service
docker_service_directory: "/etc/systemd/system/docker.service.d"
docker_service_name: "docker"

# mirsg.docker install
docker_rpm_gpg_key_url: "https://download.docker.com/linux/centos/gpg"
docker_repo_baseurl: "https://download.docker.com/linux/centos/$releasever/$basearch/stable"
docker_yum_package: "docker"

# mirsg.docker certificates
docker_generate_certificates: false
docker_certificate_directory: "/home/docker/.docker"

# mirsg.docker configuration
docker_config_dir: "/etc/docker"
docker_daemon_conf_file: "/etc/docker/daemon.json"
docker_server_hostname: "{{ ansible_host }}"
docker_server_ip: "0.0.0.0"
docker_server_port: "2376"

# mirsg.docker CA certificate
docker_ca_key: "{{ docker_certificate_directory }}/ca.key"
docker_ca_csr: "{{ docker_certificate_directory }}/ca.csr"
docker_ca_cert: "{{ docker_certificate_directory }}/ca.pem"

# mirsg.docker server certificate
docker_server_key: "{{ docker_certificate_directory }}/server-key.pem"
docker_server_csr: "{{ docker_certificate_directory }}/server.csr"
docker_server_cert: "{{ docker_certificate_directory }}/server-cert.pem"

# mirsg.docker client certificates
docker_client_hostnames: [] # list of hostnames of clients that will connect to the server
docker_client_certificate_directory: "{{ docker_certificate_directory }}/client_certs"
docker_client_certificate_cache_directory: "{{ lookup('env', 'HOME') }}/ansible_persistent_files/docker_certificates"
3 changes: 3 additions & 0 deletions meta/requirements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
collections:
- community.crypto
9 changes: 8 additions & 1 deletion molecule/centos7/molecule.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
---
dependency:
name: galaxy
options:
role-file: meta/requirements.yml
force: true

driver:
name: docker

platforms:
- name: instance
- name: server
image: centos:7
dockerfile: ../resources/Dockerfile.j2
command: ""
Expand All @@ -19,7 +22,11 @@ provisioner:
config_options:
defaults:
callbacks_enabled: profile_tasks, timer, yaml
inventory:
links:
host_vars: ../resources/inventory/host_vars/
playbooks:
prepare: ./prepare.yml
converge: ../resources/converge.yml
env:
ANSIBLE_VERBOSITY: "1"
Expand Down
30 changes: 30 additions & 0 deletions molecule/centos7/prepare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
- name: Prepare
hosts: all
become: false
gather_facts: true
tasks:
- name: Install EPEL-release
ansible.builtin.yum:
name: "epel-release"
state: installed

- name: Install Python
ansible.builtin.package:
name: "{{ item }}"
update_cache: true
state: present
loop:
- python
- python-pip
- python-setuptools

- name: Update pip
ansible.builtin.pip:
name: pip
version: "20.3.4"

- name: Install cryptography with pip - needed to generate certificates
ansible.builtin.pip:
name:
- cryptography
3 changes: 3 additions & 0 deletions molecule/resources/inventory/host_vars/server.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
docker_generate_certificates: true
docker_client_hostnames: ["docker-client.com"]
9 changes: 8 additions & 1 deletion molecule/rocky8/molecule.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
---
dependency:
name: galaxy
options:
role-file: meta/requirements.yml
force: true

driver:
name: docker

platforms:
- name: instance
- name: server
image: rockylinux:8
dockerfile: ../resources/Dockerfile.j2
command: ""
Expand All @@ -21,7 +24,11 @@ provisioner:
config_options:
defaults:
callbacks_enabled: profile_tasks, timer, yaml
inventory:
links:
host_vars: ../resources/inventory/host_vars/
playbooks:
prepare: ./prepare.yml
converge: ../resources/converge.yml
env:
ANSIBLE_VERBOSITY: "1"
Expand Down
30 changes: 30 additions & 0 deletions molecule/rocky8/prepare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
- name: Prepare
hosts: all
become: false
gather_facts: true
tasks:
- name: Install EPEL-release
ansible.builtin.yum:
name: "epel-release"
state: installed

- name: Install Python
ansible.builtin.package:
name: "{{ item }}"
update_cache: true
state: present
loop:
- python3
- python3-pip
- python3-setuptools

- name: Update pip
ansible.builtin.pip:
name: pip
version: "21.3.1"

- name: Install cryptography with pip - needed to generate certificates
ansible.builtin.pip:
name:
- cryptography
36 changes: 36 additions & 0 deletions tasks/ca-cert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
- name: Ensure docker cert dir exists
ansible.builtin.file:
path: "{{ docker_certificate_directory }}"
state: directory
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0700"

- name: Generate CA private key
community.crypto.openssl_privatekey:
path: "{{ docker_ca_key }}"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0400"

- name: Generate CA CSR
community.crypto.openssl_csr:
path: "{{ docker_ca_csr }}"
privatekey_path: "{{ docker_ca_key }}"
common_name: "{{ docker_server_hostname }}"
subject_alt_name: "IP:{{ docker_server_ip }}"
basic_constraints_critical: true
basic_constraints: ["CA:TRUE"]

- name: Generate self-signed CA certificate
community.crypto.x509_certificate:
path: "{{ docker_ca_cert }}"
privatekey_path: "{{ docker_ca_key }}"
csr_path: "{{ docker_ca_csr }}"
provider: selfsigned
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0400"
notify:
- Restart docker
48 changes: 48 additions & 0 deletions tasks/client-certs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
- name: Ensure docker client cert dir exists on server
ansible.builtin.file:
path: "{{ docker_client_certificate_directory }}"
state: directory
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0700"

- name: Generate OpenSSL client private key
community.crypto.openssl_privatekey:
path: "{{ docker_client_certificate_directory }}/key.pem"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0400"

- name: Generate OpenSSL CSR for each client using private key
community.crypto.openssl_csr:
path: "{{ docker_client_certificate_directory }}/{{ item }}.csr"
privatekey_path: "{{ docker_client_certificate_directory }}/key.pem"
common_name: "{{ item }}"
register: new_docker_client_csr_generated
loop: "{{ docker_client_hostnames }}"

- name: Generate client certificates signed by server CA
community.crypto.x509_certificate:
path: "{{ docker_client_certificate_directory }}/{{ item }}.cert"
csr_path: "{{ docker_client_certificate_directory }}/{{ item }}.csr"
provider: ownca
ownca_path: "{{ docker_ca_cert }}"
ownca_privatekey_path: "{{ docker_ca_key }}"
mode: "0400"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
loop: "{{ docker_client_hostnames }}"

- name: Copy signed client certificates to temp dir on Ansible controller
ansible.builtin.fetch:
src: "{{ docker_client_certificate_directory }}/{{ item }}.cert"
dest: "{{ docker_client_certificate_cache_directory }}/{{ item }}.cert"
flat: true
loop: "{{ docker_client_hostnames }}"

- name: Copy private key to temp dir on Ansible controller
ansible.builtin.fetch:
src: "{{ docker_client_certificate_directory }}/key.pem"
dest: "{{ docker_client_certificate_cache_directory }}/key.pem"
flat: true
31 changes: 31 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,37 @@
mode: "0644"
notify: Reload docker

- name: Generate CA, server, and client certificates
when: docker_generate_certificates
notify:
- Restart docker
block:
- name: Ensure docker config directory exists - {{ docker_config_dir }}
ansible.builtin.file:
path: "{{ docker_config_dir }}"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
state: directory
mode: "0700"

- name: Write docker daemon configuration file
ansible.builtin.template:
src: daemon.json.j2
dest: "{{ docker_daemon_conf_file }}"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0640"

- name: Generate CA certificate
ansible.builtin.import_tasks: ca-cert.yml

- name: Generate server TLS certificate
ansible.builtin.import_tasks: server-cert.yml

- name: Generate TLS certificates for each client
ansible.builtin.import_tasks: client-certs.yml
when: docker_client_hostnames

- name: "Ensure docker service configuration is reloaded before restarting the service"
ansible.builtin.meta: flush_handlers

Expand Down
27 changes: 27 additions & 0 deletions tasks/server-cert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
- name: Generate server private key
community.crypto.openssl_privatekey:
path: "{{ docker_server_key }}"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0400"

- name: Generate server CSR
community.crypto.openssl_csr:
path: "{{ docker_server_csr }}"
privatekey_path: "{{ docker_server_key }}"
common_name: "{{ docker_server_hostname }}"
subject_alt_name: "IP:{{ docker_server_ip }}"

- name: Generate server certificate
community.crypto.x509_certificate:
path: "{{ docker_server_cert }}"
csr_path: "{{ docker_server_csr }}"
provider: ownca
ownca_path: "{{ docker_ca_cert }}"
ownca_privatekey_path: "{{ docker_ca_key }}"
owner: "{{ docker_owner }}"
group: "{{ docker_group }}"
mode: "0400"
notify:
- Restart docker
7 changes: 7 additions & 0 deletions templates/daemon.json.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"hosts": ["tcp://{{ docker_server_ip }}:{{ docker_server_port }}", "unix:///var/run/docker.sock"],
"tlsverify": true,
"tlscacert": "{{ docker_ca_cert }}",
"tlscert": "{{ docker_server_cert }}",
"tlskey": "{{ docker_server_key }}"
}

0 comments on commit 7d6b491

Please sign in to comment.