From 18306e3401818cfd9723b6813987308f584c6ea3 Mon Sep 17 00:00:00 2001
From: Jan Chaloupka <jchaloup@redhat.com>
Date: Wed, 20 Sep 2017 11:56:19 +0200
Subject: consolidate etcd_common role

---
 .../tasks/certificates/backup_ca_certificates.yml  |  12 ++
 .../certificates/backup_generated_certificates.yml |  13 ++
 .../certificates/backup_server_certificates.yml    |  11 +
 roles/etcd/tasks/certificates/deploy_ca.yml        |  78 +++++++
 roles/etcd/tasks/certificates/distribute_ca.yml    |  47 +++++
 .../fetch_client_certificates_from_ca.yml          | 138 ++++++++++++
 .../fetch_server_certificates_from_ca.yml          | 234 +++++++++++++++++++++
 .../tasks/certificates/remove_ca_certificates.yml  |   5 +
 .../certificates/remove_generated_certificates.yml |   5 +
 .../certificates/retrieve_ca_certificates.yml      |   8 +
 10 files changed, 551 insertions(+)
 create mode 100644 roles/etcd/tasks/certificates/backup_ca_certificates.yml
 create mode 100644 roles/etcd/tasks/certificates/backup_generated_certificates.yml
 create mode 100644 roles/etcd/tasks/certificates/backup_server_certificates.yml
 create mode 100644 roles/etcd/tasks/certificates/deploy_ca.yml
 create mode 100644 roles/etcd/tasks/certificates/distribute_ca.yml
 create mode 100644 roles/etcd/tasks/certificates/fetch_client_certificates_from_ca.yml
 create mode 100644 roles/etcd/tasks/certificates/fetch_server_certificates_from_ca.yml
 create mode 100644 roles/etcd/tasks/certificates/remove_ca_certificates.yml
 create mode 100644 roles/etcd/tasks/certificates/remove_generated_certificates.yml
 create mode 100644 roles/etcd/tasks/certificates/retrieve_ca_certificates.yml

(limited to 'roles/etcd/tasks/certificates')

diff --git a/roles/etcd/tasks/certificates/backup_ca_certificates.yml b/roles/etcd/tasks/certificates/backup_ca_certificates.yml
new file mode 100644
index 000000000..f60eb82ef
--- /dev/null
+++ b/roles/etcd/tasks/certificates/backup_ca_certificates.yml
@@ -0,0 +1,12 @@
+---
+- name: Determine if CA certificate directory exists
+  stat:
+    path: "{{ etcd_ca_dir }}"
+  register: etcd_ca_certs_dir_stat
+- name: Backup generated etcd certificates
+  command: >
+    tar -czf {{ etcd_conf_dir }}/etcd-ca-certificate-backup-{{ ansible_date_time.epoch }}.tgz
+    {{ etcd_ca_dir }}
+  args:
+    warn: no
+  when: etcd_ca_certs_dir_stat.stat.exists | bool
diff --git a/roles/etcd/tasks/certificates/backup_generated_certificates.yml b/roles/etcd/tasks/certificates/backup_generated_certificates.yml
new file mode 100644
index 000000000..6a24cfcb3
--- /dev/null
+++ b/roles/etcd/tasks/certificates/backup_generated_certificates.yml
@@ -0,0 +1,13 @@
+---
+- name: Determine if generated etcd certificates exist
+  stat:
+    path: "{{ etcd_conf_dir }}/generated_certs"
+  register: etcd_generated_certs_dir_stat
+
+- name: Backup generated etcd certificates
+  command: >
+    tar -czf {{ etcd_conf_dir }}/etcd-generated-certificate-backup-{{ ansible_date_time.epoch }}.tgz
+    {{ etcd_conf_dir }}/generated_certs
+  args:
+    warn: no
+  when: etcd_generated_certs_dir_stat.stat.exists | bool
diff --git a/roles/etcd/tasks/certificates/backup_server_certificates.yml b/roles/etcd/tasks/certificates/backup_server_certificates.yml
new file mode 100644
index 000000000..8e6cc6965
--- /dev/null
+++ b/roles/etcd/tasks/certificates/backup_server_certificates.yml
@@ -0,0 +1,11 @@
+---
+- name: Backup etcd certificates
+  command: >
+    tar -czvf /etc/etcd/etcd-server-certificate-backup-{{ ansible_date_time.epoch }}.tgz
+    {{ etcd_conf_dir }}/ca.crt
+    {{ etcd_conf_dir }}/server.crt
+    {{ etcd_conf_dir }}/server.key
+    {{ etcd_conf_dir }}/peer.crt
+    {{ etcd_conf_dir }}/peer.key
+  args:
+    warn: no
diff --git a/roles/etcd/tasks/certificates/deploy_ca.yml b/roles/etcd/tasks/certificates/deploy_ca.yml
new file mode 100644
index 000000000..3d32290a2
--- /dev/null
+++ b/roles/etcd/tasks/certificates/deploy_ca.yml
@@ -0,0 +1,78 @@
+---
+- name: Install openssl
+  package:
+    name: openssl
+    state: present
+  when: not etcd_is_atomic | bool
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- file:
+    path: "{{ item }}"
+    state: directory
+    mode: 0700
+    owner: root
+    group: root
+  with_items:
+  - "{{ etcd_ca_new_certs_dir }}"
+  - "{{ etcd_ca_crl_dir }}"
+  - "{{ etcd_ca_dir }}/fragments"
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- command: cp /etc/pki/tls/openssl.cnf ./
+  args:
+    chdir: "{{ etcd_ca_dir }}/fragments"
+    creates: "{{ etcd_ca_dir }}/fragments/openssl.cnf"
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- template:
+    dest: "{{ etcd_ca_dir }}/fragments/openssl_append.cnf"
+    src: openssl_append.j2
+    backup: true
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- assemble:
+    src: "{{ etcd_ca_dir }}/fragments"
+    dest: "{{ etcd_openssl_conf }}"
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- name: Check etcd_ca_db exist
+  stat: path="{{ etcd_ca_db }}"
+  register: etcd_ca_db_check
+  changed_when: false
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- name: Touch etcd_ca_db file
+  file:
+    path: "{{ etcd_ca_db }}"
+    state: touch
+  when: etcd_ca_db_check.stat.isreg is not defined
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- copy:
+    dest: "{{ etcd_ca_serial }}"
+    content: "01"
+    force: no
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- name: Create etcd CA certificate
+  command: >
+    openssl req -config {{ etcd_openssl_conf }} -newkey rsa:4096
+    -keyout {{ etcd_ca_key }} -new -out {{ etcd_ca_cert }}
+    -x509 -extensions {{ etcd_ca_exts_self }} -batch -nodes
+    -days {{ etcd_ca_default_days }}
+    -subj /CN=etcd-signer@{{ ansible_date_time.epoch }}
+  args:
+    chdir: "{{ etcd_ca_dir }}"
+    creates: "{{ etcd_ca_cert }}"
+  environment:
+    SAN: 'etcd-signer'
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
diff --git a/roles/etcd/tasks/certificates/distribute_ca.yml b/roles/etcd/tasks/certificates/distribute_ca.yml
new file mode 100644
index 000000000..632ac15dd
--- /dev/null
+++ b/roles/etcd/tasks/certificates/distribute_ca.yml
@@ -0,0 +1,47 @@
+---
+- name: Create a tarball of the etcd ca certs
+  command: >
+    tar -czvf {{ etcd_conf_dir }}/{{ etcd_ca_name }}.tgz
+      -C {{ etcd_ca_dir }} .
+  args:
+    creates: "{{ etcd_conf_dir }}/{{ etcd_ca_name }}.tgz"
+    warn: no
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- name: Retrieve etcd ca cert tarball
+  fetch:
+    src: "{{ etcd_conf_dir }}/{{ etcd_ca_name }}.tgz"
+    dest: "{{ etcd_sync_cert_dir }}/"
+    flat: yes
+    fail_on_missing: yes
+    validate_checksum: yes
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- name: Ensure ca directory exists
+  file:
+    path: "{{ etcd_ca_dir }}"
+    state: directory
+
+- name: Unarchive etcd ca cert tarballs
+  unarchive:
+    src: "{{ etcd_sync_cert_dir }}/{{ etcd_ca_name }}.tgz"
+    dest: "{{ etcd_ca_dir }}"
+
+- name: Read current etcd CA
+  slurp:
+    src: "{{ etcd_conf_dir }}/ca.crt"
+  register: g_current_etcd_ca_output
+
+- name: Read new etcd CA
+  slurp:
+    src: "{{ etcd_ca_dir }}/ca.crt"
+  register: g_new_etcd_ca_output
+
+- copy:
+    content: "{{ (g_new_etcd_ca_output.content|b64decode) + (g_current_etcd_ca_output.content|b64decode) }}"
+    dest: "{{ item }}/ca.crt"
+  with_items:
+  - "{{ etcd_conf_dir }}"
+  - "{{ etcd_ca_dir }}"
diff --git a/roles/etcd/tasks/certificates/fetch_client_certificates_from_ca.yml b/roles/etcd/tasks/certificates/fetch_client_certificates_from_ca.yml
new file mode 100644
index 000000000..119071a72
--- /dev/null
+++ b/roles/etcd/tasks/certificates/fetch_client_certificates_from_ca.yml
@@ -0,0 +1,138 @@
+---
+- name: Ensure CA certificate exists on etcd_ca_host
+  stat:
+    path: "{{ etcd_ca_cert }}"
+  register: g_ca_cert_stat_result
+  delegate_to: "{{ etcd_ca_host }}"
+  run_once: true
+
+- fail:
+    msg: >
+      CA certificate {{ etcd_ca_cert }} doesn't exist on CA host
+      {{ etcd_ca_host }}. Apply 'etcd_ca' action from `etcd` role to
+      {{ etcd_ca_host }}.
+  when: not g_ca_cert_stat_result.stat.exists | bool
+  run_once: true
+
+- name: Check status of external etcd certificatees
+  stat:
+    path: "{{ etcd_cert_config_dir }}/{{ item }}"
+  with_items:
+  - "{{ etcd_cert_prefix }}client.crt"
+  - "{{ etcd_cert_prefix }}client.key"
+  - "{{ etcd_cert_prefix }}ca.crt"
+  register: g_external_etcd_cert_stat_result
+  when: not etcd_certificates_redeploy | default(false) | bool
+
+- set_fact:
+    etcd_client_certs_missing: "{{ true if etcd_certificates_redeploy | default(false) | bool
+                                   else (False in (g_external_etcd_cert_stat_result.results
+                                                   | default({})
+                                                   | oo_collect(attribute='stat.exists')
+                                                   | list)) }}"
+
+- name: Ensure generated_certs directory present
+  file:
+    path: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    state: directory
+    mode: 0700
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Create the client csr
+  command: >
+    openssl req -new -keyout {{ etcd_cert_prefix }}client.key
+    -config {{ etcd_openssl_conf }}
+    -out {{ etcd_cert_prefix }}client.csr
+    -reqexts {{ etcd_req_ext }} -batch -nodes
+    -subj /CN={{ etcd_hostname }}
+  args:
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'client.csr' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }},DNS:{{ etcd_hostname }}"
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+# Certificates must be signed serially in order to avoid competing
+# for the serial file.
+- name: Sign and create the client crt
+  delegated_serial_command:
+    command: >
+      openssl ca -name {{ etcd_ca_name }} -config {{ etcd_openssl_conf }}
+      -out {{ etcd_cert_prefix }}client.crt
+      -in {{ etcd_cert_prefix }}client.csr
+      -batch
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'client.crt' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }}"
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- file:
+    src: "{{ etcd_ca_cert }}"
+    dest: "{{ etcd_generated_certs_dir}}/{{ etcd_cert_subdir }}/{{ etcd_cert_prefix }}ca.crt"
+    state: hard
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Create local temp directory for syncing certs
+  local_action: command mktemp -d /tmp/etcd_certificates-XXXXXXX
+  register: g_etcd_client_mktemp
+  changed_when: False
+  when: etcd_client_certs_missing | bool
+  become: no
+
+- name: Create a tarball of the etcd certs
+  command: >
+    tar -czvf {{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz
+      -C {{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }} .
+  args:
+    creates: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz"
+    # Disables the following warning:
+    # Consider using unarchive module rather than running tar
+    warn: no
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Retrieve the etcd cert tarballs
+  fetch:
+    src: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz"
+    dest: "{{ g_etcd_client_mktemp.stdout }}/"
+    flat: yes
+    fail_on_missing: yes
+    validate_checksum: yes
+  when: etcd_client_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Ensure certificate directory exists
+  file:
+    path: "{{ etcd_cert_config_dir }}"
+    state: directory
+  when: etcd_client_certs_missing | bool
+
+- name: Unarchive etcd cert tarballs
+  unarchive:
+    src: "{{ g_etcd_client_mktemp.stdout }}/{{ etcd_cert_subdir }}.tgz"
+    dest: "{{ etcd_cert_config_dir }}"
+  when: etcd_client_certs_missing | bool
+
+- file:
+    path: "{{ etcd_cert_config_dir }}/{{ item }}"
+    owner: root
+    group: root
+    mode: 0600
+  with_items:
+  - "{{ etcd_cert_prefix }}client.crt"
+  - "{{ etcd_cert_prefix }}client.key"
+  - "{{ etcd_cert_prefix }}ca.crt"
+  when: etcd_client_certs_missing | bool
+
+- name: Delete temporary directory
+  local_action: file path="{{ g_etcd_client_mktemp.stdout }}" state=absent
+  changed_when: False
+  when: etcd_client_certs_missing | bool
+  become: no
diff --git a/roles/etcd/tasks/certificates/fetch_server_certificates_from_ca.yml b/roles/etcd/tasks/certificates/fetch_server_certificates_from_ca.yml
new file mode 100644
index 000000000..26492fb3c
--- /dev/null
+++ b/roles/etcd/tasks/certificates/fetch_server_certificates_from_ca.yml
@@ -0,0 +1,234 @@
+---
+- name: Install etcd
+  package:
+    name: "etcd{{ '-' + etcd_version if etcd_version is defined else '' }}"
+    state: present
+  when: not etcd_is_containerized | bool
+
+- name: Check status of etcd certificates
+  stat:
+    path: "{{ item }}"
+  with_items:
+  - "{{ etcd_cert_config_dir }}/{{ etcd_cert_prefix }}server.crt"
+  - "{{ etcd_cert_config_dir }}/{{ etcd_cert_prefix }}peer.crt"
+  - "{{ etcd_cert_config_dir }}/{{ etcd_cert_prefix }}ca.crt"
+  - "{{ etcd_system_container_cert_config_dir }}/{{ etcd_cert_prefix }}server.crt"
+  - "{{ etcd_system_container_cert_config_dir }}/{{ etcd_cert_prefix }}peer.crt"
+  - "{{ etcd_system_container_cert_config_dir }}/{{ etcd_cert_prefix }}ca.crt"
+  register: g_etcd_server_cert_stat_result
+  when: not etcd_certificates_redeploy | default(false) | bool
+
+- set_fact:
+    etcd_server_certs_missing: "{{ true if etcd_certificates_redeploy | default(false) | bool
+                                   else (False in (g_etcd_server_cert_stat_result.results
+                                                   | default({})
+                                                   | oo_collect(attribute='stat.exists')
+                                                   | list)) }}"
+
+- name: Ensure generated_certs directory present
+  file:
+    path: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    state: directory
+    mode: 0700
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Create the server csr
+  command: >
+    openssl req -new -keyout {{ etcd_cert_prefix }}server.key
+    -config {{ etcd_openssl_conf }}
+    -out {{ etcd_cert_prefix }}server.csr
+    -reqexts {{ etcd_req_ext }} -batch -nodes
+    -subj /CN={{ etcd_hostname }}
+  args:
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'server.csr' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }},DNS:{{ etcd_hostname }}"
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+# Certificates must be signed serially in order to avoid competing
+# for the serial file.
+- name: Sign and create the server crt
+  delegated_serial_command:
+    command: >
+      openssl ca -name {{ etcd_ca_name }} -config {{ etcd_openssl_conf }}
+      -out {{ etcd_cert_prefix }}server.crt
+      -in {{ etcd_cert_prefix }}server.csr
+      -extensions {{ etcd_ca_exts_server }} -batch
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'server.crt' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }}"
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Create the peer csr
+  command: >
+    openssl req -new -keyout {{ etcd_cert_prefix }}peer.key
+    -config {{ etcd_openssl_conf }}
+    -out {{ etcd_cert_prefix }}peer.csr
+    -reqexts {{ etcd_req_ext }} -batch -nodes
+    -subj /CN={{ etcd_hostname }}
+  args:
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'peer.csr' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }},DNS:{{ etcd_hostname }}"
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+# Certificates must be signed serially in order to avoid competing
+# for the serial file.
+- name: Sign and create the peer crt
+  delegated_serial_command:
+    command: >
+      openssl ca -name {{ etcd_ca_name }} -config {{ etcd_openssl_conf }}
+      -out {{ etcd_cert_prefix }}peer.crt
+      -in {{ etcd_cert_prefix }}peer.csr
+      -extensions {{ etcd_ca_exts_peer }} -batch
+    chdir: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}"
+    creates: "{{ etcd_generated_certs_dir ~ '/' ~  etcd_cert_subdir ~ '/'
+                 ~ etcd_cert_prefix ~ 'peer.crt' }}"
+  environment:
+    SAN: "IP:{{ etcd_ip }}"
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- file:
+    src: "{{ etcd_ca_cert }}"
+    dest: "{{ etcd_generated_certs_dir}}/{{ etcd_cert_subdir }}/{{ etcd_cert_prefix }}ca.crt"
+    state: hard
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Create local temp directory for syncing certs
+  local_action: command mktemp -d /tmp/etcd_certificates-XXXXXXX
+  become: no
+  register: g_etcd_server_mktemp
+  changed_when: False
+  when: etcd_server_certs_missing | bool
+
+- name: Create a tarball of the etcd certs
+  command: >
+    tar -czvf {{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz
+      -C {{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }} .
+  args:
+    creates: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz"
+    # Disables the following warning:
+    # Consider using unarchive module rather than running tar
+    warn: no
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Retrieve etcd cert tarball
+  fetch:
+    src: "{{ etcd_generated_certs_dir }}/{{ etcd_cert_subdir }}.tgz"
+    dest: "{{ g_etcd_server_mktemp.stdout }}/"
+    flat: yes
+    fail_on_missing: yes
+    validate_checksum: yes
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Ensure certificate directory exists
+  file:
+    path: "{{ item }}"
+    state: directory
+  with_items:
+  - "{{ etcd_cert_config_dir }}"
+  - "{{ etcd_system_container_cert_config_dir }}"
+  when: etcd_server_certs_missing | bool
+
+- name: Unarchive cert tarball
+  unarchive:
+    src: "{{ g_etcd_server_mktemp.stdout }}/{{ etcd_cert_subdir }}.tgz"
+    dest: "{{ etcd_cert_config_dir }}"
+  when: etcd_server_certs_missing | bool
+
+- name: Create a tarball of the etcd ca certs
+  command: >
+    tar -czvf {{ etcd_generated_certs_dir }}/{{ etcd_ca_name }}.tgz
+      -C {{ etcd_ca_dir }} .
+  args:
+    creates: "{{ etcd_generated_certs_dir }}/{{ etcd_ca_name }}.tgz"
+    warn: no
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Retrieve etcd ca cert tarball
+  fetch:
+    src: "{{ etcd_generated_certs_dir }}/{{ etcd_ca_name }}.tgz"
+    dest: "{{ g_etcd_server_mktemp.stdout }}/"
+    flat: yes
+    fail_on_missing: yes
+    validate_checksum: yes
+  when: etcd_server_certs_missing | bool
+  delegate_to: "{{ etcd_ca_host }}"
+
+- name: Ensure ca directory exists
+  file:
+    path: "{{ item }}"
+    state: directory
+  with_items:
+  - "{{ etcd_ca_dir }}"
+  - "{{ etcd_system_container_cert_config_dir }}/ca"
+  when: etcd_server_certs_missing | bool
+
+- name: Unarchive cert tarball for the system container
+  unarchive:
+    src: "{{ g_etcd_server_mktemp.stdout }}/{{ etcd_cert_subdir }}.tgz"
+    dest: "{{ etcd_system_container_cert_config_dir }}"
+  when:
+  - etcd_server_certs_missing | bool
+  - r_etcd_common_etcd_runtime == 'runc'
+
+- name: Unarchive etcd ca cert tarballs for the system container
+  unarchive:
+    src: "{{ g_etcd_server_mktemp.stdout }}/{{ etcd_ca_name }}.tgz"
+    dest: "{{ etcd_system_container_cert_config_dir }}/ca"
+  when:
+  - etcd_server_certs_missing | bool
+  - r_etcd_common_etcd_runtime == 'runc'
+
+- name: Delete temporary directory
+  local_action: file path="{{ g_etcd_server_mktemp.stdout }}" state=absent
+  become: no
+  changed_when: False
+  when: etcd_server_certs_missing | bool
+
+- name: Validate permissions on certificate files
+  file:
+    path: "{{ item }}"
+    mode: 0600
+    owner: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+    group: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+  when: etcd_url_scheme == 'https'
+  with_items:
+  - "{{ etcd_ca_file }}"
+  - "{{ etcd_cert_file }}"
+  - "{{ etcd_key_file }}"
+
+- name: Validate permissions on peer certificate files
+  file:
+    path: "{{ item }}"
+    mode: 0600
+    owner: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+    group: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+  when: etcd_peer_url_scheme == 'https'
+  with_items:
+  - "{{ etcd_peer_ca_file }}"
+  - "{{ etcd_peer_cert_file }}"
+  - "{{ etcd_peer_key_file }}"
+
+- name: Validate permissions on the config dir
+  file:
+    path: "{{ etcd_conf_dir }}"
+    state: directory
+    owner: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+    group: "{{ 'etcd' if not etcd_is_containerized | bool else omit }}"
+    mode: 0700
diff --git a/roles/etcd/tasks/certificates/remove_ca_certificates.yml b/roles/etcd/tasks/certificates/remove_ca_certificates.yml
new file mode 100644
index 000000000..4a86eb60d
--- /dev/null
+++ b/roles/etcd/tasks/certificates/remove_ca_certificates.yml
@@ -0,0 +1,5 @@
+---
+- name: Remove CA certificate directory
+  file:
+    path: "{{ etcd_ca_dir }}"
+    state: absent
diff --git a/roles/etcd/tasks/certificates/remove_generated_certificates.yml b/roles/etcd/tasks/certificates/remove_generated_certificates.yml
new file mode 100644
index 000000000..993b18de2
--- /dev/null
+++ b/roles/etcd/tasks/certificates/remove_generated_certificates.yml
@@ -0,0 +1,5 @@
+---
+- name: Remove generated etcd certificates
+  file:
+    path: "{{ etcd_conf_dir }}/generated_certs"
+    state: absent
diff --git a/roles/etcd/tasks/certificates/retrieve_ca_certificates.yml b/roles/etcd/tasks/certificates/retrieve_ca_certificates.yml
new file mode 100644
index 000000000..70b5c6523
--- /dev/null
+++ b/roles/etcd/tasks/certificates/retrieve_ca_certificates.yml
@@ -0,0 +1,8 @@
+---
+- name: Retrieve etcd CA certificate
+  fetch:
+    src: "{{ etcd_conf_dir }}/ca.crt"
+    dest: "{{ etcd_sync_cert_dir }}/"
+    flat: yes
+    fail_on_missing: yes
+    validate_checksum: yes
-- 
cgit v1.2.3