From 11c1fe494ffea6f500d81232d12e03100371f713 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 20:27:40 +0300 Subject: [PATCH 01/30] add support of different key algorithm --- defaults/main.yml | 4 +- molecule/ca-renew/converge.yml | 12 +- molecule/ca-renew/verify.yml | 214 ++--------------------------- molecule/ca-renew/verify_tasks.yml | 197 ++++++++++++++++++++++++++ molecule/default/converge.yml | 12 +- molecule/default/verify.yml | 126 ++--------------- molecule/default/verify_tasks.yml | 109 +++++++++++++++ molecule/renew/converge.yml | 13 +- molecule/renew/prepare.yml | 1 - molecule/renew/verify.yml | 213 ++-------------------------- molecule/renew/verify_tasks.yml | 196 ++++++++++++++++++++++++++ tasks/ca.yml | 45 +++--- tasks/main.yml | 18 +-- 13 files changed, 599 insertions(+), 561 deletions(-) create mode 100644 molecule/ca-renew/verify_tasks.yml create mode 100644 molecule/default/verify_tasks.yml create mode 100644 molecule/renew/verify_tasks.yml diff --git a/defaults/main.yml b/defaults/main.yml index f00a5c0..1ac75cb 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,15 +1,17 @@ --- - ca_manage_openssl: true ca_ca_dir: /opt/ca ca_client_ca_dir: /opt/ca ca_client_ca_dir_owner: root ca_client_ca_dir_group: root ca_client_ca_dir_mode: 0700 +ca_client_key_algorithm: "{{ ca_ca_signing_key_algorithm }}" ca_ca_password: ChangeMe ca_localdir: /tmp/ca ca_local_become: false ca_ca_host: localhost +ca_ca_signing_key_algorithm: RSA +ca_ca_signing_key_params: "" ca_server_cert: true ca_logstash: false diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 7ee9462..973eeaa 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -13,7 +13,11 @@ ca_ca_days: 3650 ca_valid_time: +365d ca_renew: true - tasks: - - name: "Include CA role" - include_role: - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + roles: + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index beb2c9b..98a7b92 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -1,206 +1,16 @@ --- - - name: Verify hosts: all - vars: - ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca tasks: - - - name: Verify signature on certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - - - name: Verify signature on server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - - - name: Check if instance key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - register: instance_key_stat - - - name: Fail if instance key is missing - fail: - msg: "Instance key is missing" - when: - - not instance_key_stat.stat.exists | bool - - - name: Check if Logstash key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" - register: logstash_key_stat - - - name: Fail if Logstash key is missing - fail: - msg: "Logstash key is missing" - when: - - not logstash_key_stat.stat.exists | bool - - - name: Verify signature on etcd peer certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Register SAN of etcd peer certificate - command: > - openssl x509 - -text - -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - register: etcd_san_peer_stat - - - name: Register SAN of etcd server certificate - command: > - openssl x509 - -text - -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - - register: etcd_san_server_stat - - - name: Fail if SAN of etcd peer certificate is missing IP addresses - fail: - msg: "Default IPv4 address in etcd peer certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_peer_stat.stdout - - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - - - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: - msg: "Default IPv4 address in etcd server certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_server_stat.stdout - - '"127.0.0.1" not in etcd_san_server_stat.stdout' - - - name: Get next year - set_fact: - next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" - - - name: Register notAfter of client certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - | grep notAfter - register: client_crt_stat - - - name: Fail if notAfter of client certificate is not next year - fail: - msg: "Failed: notAfter of client certificate is not next year" - when: next_year | string not in client_crt_stat.stdout - - - name: Register notAfter of server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - | grep notAfter - register: server_crt_stat - - - name: Fail if notAfter of server certificate is not next year - fail: - msg: "Failed: notAfter of server certificate is not next year" - when: next_year | string not in server_crt_stat.stdout - - - name: Register notAfter of etcd certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - | grep notAfter - register: etcd_crt_stat - - - name: Fail if notAfter of etcd certificate is not next year - fail: - msg: "Failed: notAfter of etcd certificate is not next year" - when: next_year | string not in etcd_crt_stat.stdout - - - name: Register notAfter of etcd server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - | grep notAfter - register: etcd_server_crt_stat - - - name: Fail if notAfter of etcd server certificate is not next year - fail: - msg: "Failed: notAfter of etcd server certificate is not next year" - when: next_year | string not in etcd_server_crt_stat.stdout - - - name: Get year of next decade to check CA - set_fact: - next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" - - - name: Register notAfter of CA certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/ca.crt - | grep notAfter - register: ca_crt_stat - - # Caution: - # Can fail at end or beginning of year, since - # leap year days can be +- 3 - - name: Fail if notAfter of CA certificate is not next decade - fail: - msg: "Failed: notAfter of CA certificate is not next decade" - when: next_decade | string not in ca_crt_stat.stdout + - name: Verify RSA + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_dir: /opt/ca + ca_client_ca_dir: /opt/ca + - name: Verify Ed25519 + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/ca-renew/verify_tasks.yml b/molecule/ca-renew/verify_tasks.yml new file mode 100644 index 0000000..f733d96 --- /dev/null +++ b/molecule/ca-renew/verify_tasks.yml @@ -0,0 +1,197 @@ +- name: Verify signature on certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + +- name: Verify signature on server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + +- name: Check if instance key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + register: instance_key_stat + +- name: Fail if instance key is missing + fail: + msg: "Instance key is missing" + when: + - not instance_key_stat.stat.exists | bool + +- name: Check if Logstash key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + register: logstash_key_stat + +- name: Fail if Logstash key is missing + fail: + msg: "Logstash key is missing" + when: + - not logstash_key_stat.stat.exists | bool + +- name: Verify signature on etcd peer certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Register SAN of etcd peer certificate + command: > + openssl x509 + -text + -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + register: etcd_san_peer_stat + +- name: Register SAN of etcd server certificate + command: > + openssl x509 + -text + -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + + register: etcd_san_server_stat + +- name: Fail if SAN of etcd peer certificate is missing IP addresses + fail: + msg: "Default IPv4 address in etcd peer certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_peer_stat.stdout + - '"127.0.0.1" not in etcd_san_peer_stat.stdout' + +- name: Fail if SAN of etcd server certificate is missing IP addresses + fail: + msg: "Default IPv4 address in etcd server certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_server_stat.stdout + - '"127.0.0.1" not in etcd_san_server_stat.stdout' + +- name: Get next year + set_fact: + next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" + +- name: Register notAfter of client certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + | grep notAfter + register: client_crt_stat + +- name: Fail if notAfter of client certificate is not next year + fail: + msg: "Failed: notAfter of client certificate is not next year" + when: next_year | string not in client_crt_stat.stdout + +- name: Register notAfter of server certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + | grep notAfter + register: server_crt_stat + +- name: Fail if notAfter of server certificate is not next year + fail: + msg: "Failed: notAfter of server certificate is not next year" + when: next_year | string not in server_crt_stat.stdout + +- name: Register notAfter of etcd certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + | grep notAfter + register: etcd_crt_stat + +- name: Fail if notAfter of etcd certificate is not next year + fail: + msg: "Failed: notAfter of etcd certificate is not next year" + when: next_year | string not in etcd_crt_stat.stdout + +- name: Register notAfter of etcd server certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + | grep notAfter + register: etcd_server_crt_stat + +- name: Fail if notAfter of etcd server certificate is not next year + fail: + msg: "Failed: notAfter of etcd server certificate is not next year" + when: next_year | string not in etcd_server_crt_stat.stdout + +- name: Get year of next decade to check CA + set_fact: + next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" + +- name: Register notAfter of CA certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/ca.crt + | grep notAfter + register: ca_crt_stat + +# Caution: +# Can fail at end or beginning of year, since +# leap year days can be +- 3 +- name: Fail if notAfter of CA certificate is not next decade + fail: + msg: "Failed: notAfter of CA certificate is not next decade" + when: next_decade | string not in ca_crt_stat.stdout diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index fddc1cd..7784b1e 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -10,7 +10,11 @@ ca_logstash: true ca_etcd: true ca_etcd_group: molecule - tasks: - - name: "Include CA role" - include_role: - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + roles: + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 789fab0..98a7b92 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -1,118 +1,16 @@ --- - - name: Verify hosts: all - vars: - ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca tasks: - - - name: Verify signature on certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - - - name: Verify signature on server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - - - name: Check if instance key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - register: instance_key_stat - - - name: Fail if instance key is missing - fail: - msg: "Instance key is missing" - when: - - not instance_key_stat.stat.exists | bool - - - name: Check if Logstash key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" - register: logstash_key_stat - - - name: Fail if Logstash key is missing - fail: - msg: "Logstash key is missing" - when: - - not logstash_key_stat.stat.exists | bool - - - name: Verify signature on etcd peer certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Register SAN of etcd peer certificate - command: > - openssl x509 - -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - register: etcd_san_peer_stat - - - name: Register SAN of etcd server certificate - command: > - openssl x509 - -text - -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - register: etcd_san_server_stat - - - name: Fail if SAN of etcd peer certificate is missing IP addresses - fail: - msg: "Default IPv4 address in etcd peer certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_peer_stat.stdout - - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - - - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: - msg: "Default IPv4 address in etcd server certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_server_stat.stdout - - '"127.0.0.1" not in etcd_san_server_stat.stdout' + - name: Verify RSA + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_dir: /opt/ca + ca_client_ca_dir: /opt/ca + - name: Verify Ed25519 + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/default/verify_tasks.yml b/molecule/default/verify_tasks.yml new file mode 100644 index 0000000..8767496 --- /dev/null +++ b/molecule/default/verify_tasks.yml @@ -0,0 +1,109 @@ +- name: Verify signature on certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + +- name: Verify signature on server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + +- name: Check if instance key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + register: instance_key_stat + +- name: Fail if instance key is missing + fail: + msg: "Instance key is missing" + when: + - not instance_key_stat.stat.exists | bool + +- name: Check if Logstash key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + register: logstash_key_stat + +- name: Fail if Logstash key is missing + fail: + msg: "Logstash key is missing" + when: + - not logstash_key_stat.stat.exists | bool + +- name: Verify signature on etcd peer certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Register SAN of etcd peer certificate + command: > + openssl x509 + -text -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + register: etcd_san_peer_stat + +- name: Register SAN of etcd server certificate + command: > + openssl x509 + -text + -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + register: etcd_san_server_stat + +- name: Fail if SAN of etcd peer certificate is missing IP addresses + fail: + msg: "Default IPv4 address in etcd peer certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_peer_stat.stdout + - '"127.0.0.1" not in etcd_san_peer_stat.stdout' + +- name: Fail if SAN of etcd server certificate is missing IP addresses + fail: + msg: "Default IPv4 address in etcd server certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_server_stat.stdout + - '"127.0.0.1" not in etcd_san_server_stat.stdout' diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 4290d53..6540a00 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -14,7 +14,12 @@ ca_valid_time: +365d ca_check_valid_time: +1w ca_renew: true - tasks: - - name: "Include CA role" - include_role: - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + + roles: + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 55f3ae4..f251046 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -9,7 +9,6 @@ ca_valid_time: "+5d" ca_renew: true tasks: - - name: Install Python libraries pip: name: cryptography>= 1.2.3 diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index 147a8a8..98a7b92 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -1,205 +1,16 @@ --- - - name: Verify hosts: all - vars: - ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca tasks: - - - name: Verify signature on certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - - - name: Verify signature on server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - - - name: Check if instance key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - register: instance_key_stat - - - name: Fail if instance key is missing - fail: - msg: "Instance key is missing" - when: - - not instance_key_stat.stat.exists | bool - - - name: Check if Logstash key is present - stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" - register: logstash_key_stat - - - name: Fail if Logstash key is missing - fail: - msg: "Logstash key is missing" - when: - - not logstash_key_stat.stat.exists | bool - - - name: Verify signature on etcd peer certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Verify signature on etcd server certificate - command: > - openssl verify - -verbose - -CAfile {{ ca_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - - - name: Register SAN of etcd peer certificate - command: > - openssl x509 - -text - -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - register: etcd_san_peer_stat - - - name: Register SAN of etcd server certificate - command: > - openssl x509 - -text - -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - -certopt " - no_subject, - no_header, - no_version, - no_serial, - no_signame, - no_validity, - no_issuer, - no_pubkey, - no_sigdump, - no_aux" - register: etcd_san_server_stat - - - name: Fail if SAN of etcd peer certificate is missing addresses - fail: - msg: "Default IPv4 address in etcd peer certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_peer_stat.stdout - - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - - - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: - msg: "Default IPv4 address in etcd server certifcate are missing" - when: - - hostvars['ca_default_client']['ansible_default_ipv4']['address'] - not in etcd_san_server_stat.stdout - - '"127.0.0.1" not in etcd_san_server_stat.stdout' - - - name: Get next year - set_fact: - next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" - - - name: Register notAfter of client certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - | grep notAfter - register: client_crt_stat - - - name: Fail if notAfter of client certificate is not next year - fail: - msg: "Failed: notAfter of client certificate is not next year" - when: next_year | string not in client_crt_stat.stdout - - - name: Register notAfter of server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - | grep notAfter - register: server_crt_stat - - - name: Fail if notAfter of server certificate is not next year - fail: - msg: "Failed: notAfter of server certificate is not next year" - when: next_year | string not in server_crt_stat.stdout - - - name: Register notAfter of etcd certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - | grep notAfter - register: etcd_crt_stat - - - name: Fail if notAfter of etcd certificate is not next year - fail: - msg: "Failed: notAfter of etcd certificate is not next year" - when: next_year | string not in etcd_crt_stat.stdout - - - name: Register notAfter of etcd server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - | grep notAfter - register: etcd_server_crt_stat - - - name: Fail if notAfter of etcd server certificate is not next year - fail: - msg: "Failed: notAfter of etcd server certificate isn't next year" - when: next_year | string not in etcd_server_crt_stat.stdout - - - name: Get year of next decade to check CA - set_fact: - next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" - - - name: Register notAfter of CA certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; - openssl x509 - -noout - -dates - -in {{ ca_client_ca_dir }}/ca.crt - | grep notAfter - register: ca_crt_stat - - # Caution: - # Can fail at end or beginning of year, since - # leap year days can be +- 3 - - name: Fail if notAfter of CA certificate is not next decade - fail: - msg: "Failed because notAfter of CA certificate is not next decade" - when: next_decade | string not in ca_crt_stat.stdout + - name: Verify RSA + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_dir: /opt/ca + ca_client_ca_dir: /opt/ca + - name: Verify Ed25519 + ansible.builtin.include_tasks: verify_tasks.yml + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/renew/verify_tasks.yml b/molecule/renew/verify_tasks.yml new file mode 100644 index 0000000..66df987 --- /dev/null +++ b/molecule/renew/verify_tasks.yml @@ -0,0 +1,196 @@ +- name: Verify signature on certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + +- name: Verify signature on server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + +- name: Check if instance key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + register: instance_key_stat + +- name: Fail if instance key is missing + fail: + msg: "Instance key is missing" + when: + - not instance_key_stat.stat.exists | bool + +- name: Check if Logstash key is present + stat: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + register: logstash_key_stat + +- name: Fail if Logstash key is missing + fail: + msg: "Logstash key is missing" + when: + - not logstash_key_stat.stat.exists | bool + +- name: Verify signature on etcd peer certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Verify signature on etcd server certificate + command: > + openssl verify + -verbose + -CAfile {{ ca_ca_dir }}/ca.crt + {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + +- name: Register SAN of etcd peer certificate + command: > + openssl x509 + -text + -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + register: etcd_san_peer_stat + +- name: Register SAN of etcd server certificate + command: > + openssl x509 + -text + -noout + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -certopt " + no_subject, + no_header, + no_version, + no_serial, + no_signame, + no_validity, + no_issuer, + no_pubkey, + no_sigdump, + no_aux" + register: etcd_san_server_stat + +- name: Fail if SAN of etcd peer certificate is missing addresses + fail: + msg: "Default IPv4 address in etcd peer certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_peer_stat.stdout + - '"127.0.0.1" not in etcd_san_peer_stat.stdout' + +- name: Fail if SAN of etcd server certificate is missing IP addresses + fail: + msg: "Default IPv4 address in etcd server certifcate are missing" + when: + - hostvars['ca_default_client']['ansible_default_ipv4']['address'] + not in etcd_san_server_stat.stdout + - '"127.0.0.1" not in etcd_san_server_stat.stdout' + +- name: Get next year + set_fact: + next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" + +- name: Register notAfter of client certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + | grep notAfter + register: client_crt_stat + +- name: Fail if notAfter of client certificate is not next year + fail: + msg: "Failed: notAfter of client certificate is not next year" + when: next_year | string not in client_crt_stat.stdout + +- name: Register notAfter of server certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + | grep notAfter + register: server_crt_stat + +- name: Fail if notAfter of server certificate is not next year + fail: + msg: "Failed: notAfter of server certificate is not next year" + when: next_year | string not in server_crt_stat.stdout + +- name: Register notAfter of etcd certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + | grep notAfter + register: etcd_crt_stat + +- name: Fail if notAfter of etcd certificate is not next year + fail: + msg: "Failed: notAfter of etcd certificate is not next year" + when: next_year | string not in etcd_crt_stat.stdout + +- name: Register notAfter of etcd server certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + | grep notAfter + register: etcd_server_crt_stat + +- name: Fail if notAfter of etcd server certificate is not next year + fail: + msg: "Failed: notAfter of etcd server certificate isn't next year" + when: next_year | string not in etcd_server_crt_stat.stdout + +- name: Get year of next decade to check CA + set_fact: + next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" + +- name: Register notAfter of CA certificate + shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + openssl x509 + -noout + -dates + -in {{ ca_client_ca_dir }}/ca.crt + | grep notAfter + register: ca_crt_stat + +# Caution: +# Can fail at end or beginning of year, since +# leap year days can be +- 3 +- name: Fail if notAfter of CA certificate is not next decade + fail: + msg: "Failed because notAfter of CA certificate is not next decade" + when: next_decade | string not in ca_crt_stat.stdout diff --git a/tasks/ca.yml b/tasks/ca.yml index a0da62d..20a67c5 100644 --- a/tasks/ca.yml +++ b/tasks/ca.yml @@ -1,5 +1,4 @@ --- - - name: Place CA configuration file ansible.builtin.template: src: ca.conf.j2 @@ -16,7 +15,6 @@ - name: Test and prepare key when: cakey.stat.exists block: - - name: Test CA key if present community.crypto.openssl_privatekey_info: path: "{{ ca_ca_dir }}/ca.key" @@ -28,7 +26,6 @@ when: - not cakeyinfo.can_parse_key | bool block: - - name: Move old key ansible.builtin.command: > mv @@ -45,7 +42,7 @@ args: creates: "{{ ca_ca_dir }}/ca.key.{{ ansible_date_time.iso8601 }}" -- name: Generate CA key +- name: Generate RSA CA key ansible.builtin.command: > openssl genrsa -aes256 @@ -54,11 +51,25 @@ {{ ca_ca_keylength }} args: creates: "{{ ca_ca_dir }}/ca.key" + when: ca_ca_signing_key_algorithm == 'RSA' + +- name: Generate CA key + ansible.builtin.command: + cmd: > + openssl genpkey + -algorithm {{ ca_ca_signing_key_algorithm }} + -pass stdin + -aes256 + -out {{ ca_ca_dir }}/ca.key + {{ ca_ca_signing_key_params }} + stdin: "{{ ca_ca_password }}" + args: + creates: "{{ ca_ca_dir }}/ca.key" + when: ca_ca_signing_key_algorithm != 'RSA' - name: Renew CA certificate when: ca_renew | bool block: - - name: Check if CA certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: @@ -80,17 +91,17 @@ when: crt_exists.stat.exists | bool - name: Generate CA certificate - ansible.builtin.command: > - openssl req - -x509 - -new - -nodes - -key {{ ca_ca_dir }}/ca.key - -passin pass:{{ ca_ca_password }} - -sha256 - {{ _ca_openssl_ca_ext_opts }} - -days {{ ca_ca_days }} - -out {{ ca_ca_dir }}/ca.crt - -config {{ ca_ca_dir }}/ca.conf + ansible.builtin.command: + cmd: > + openssl req + -x509 + -new + -key {{ ca_ca_dir }}/ca.key + -passin stdin + -sha256 + -days {{ ca_ca_days }} + -out {{ ca_ca_dir }}/ca.crt + -config {{ ca_ca_dir }}/ca.conf + stdin: "{{ ca_ca_password }}" args: creates: "{{ ca_ca_dir }}/ca.crt" diff --git a/tasks/main.yml b/tasks/main.yml index f7e5435..6dca271 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,5 +1,4 @@ --- - - name: Ensure CA directory exists on clients ansible.builtin.file: path: "{{ ca_client_ca_dir }}" @@ -54,6 +53,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" + type: "{{ ca_client_key_algorithm }}" register: key - name: Create CSR @@ -97,8 +97,7 @@ ownca_privatekey_passphrase: "{{ ca_ca_password }}" ownca_not_after: "{{ ca_valid_time }}" provider: ownca - force: - "{{ not crt_info.valid_at.check_period | default(omit) or + force: "{{ not crt_info.valid_at.check_period | default(omit) or hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" backup: true delegate_to: "{{ ca_ca_host }}" @@ -138,7 +137,6 @@ - name: Create server certificate when: ca_server_cert | bool block: - - name: Create server CSR community.crypto.openssl_csr: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.csr" @@ -180,8 +178,7 @@ ownca_privatekey_passphrase: "{{ ca_ca_password }}" ownca_not_after: "{{ ca_valid_time }}" provider: ownca - force: - "{{ not crt_info.valid_at.check_period | default(omit) or + force: "{{ not crt_info.valid_at.check_period | default(omit) or hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" backup: true delegate_to: "{{ ca_ca_host }}" @@ -204,7 +201,6 @@ - name: Handle Logstash compatible key when: ca_logstash | bool block: - - name: Check if Logstash key is present ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" @@ -248,7 +244,6 @@ - name: Create etcd peer certificate when: ca_etcd | bool block: - - name: Add IP addresses for etcd to ca_san_etcd ansible.builtin.set_fact: ca_san_etcd: >- @@ -303,8 +298,7 @@ ownca_privatekey_passphrase: "{{ ca_ca_password }}" ownca_not_after: "{{ ca_valid_time }}" provider: ownca - force: - "{{ not crt_info.valid_at.check_period | default(omit) or + force: "{{ not crt_info.valid_at.check_period | default(omit) or hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" backup: true delegate_to: "{{ ca_ca_host }}" @@ -329,7 +323,6 @@ - name: Create etcd server certificate when: ca_etcd | bool block: - - name: Create CSR community.crypto.openssl_csr: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.csr" @@ -370,8 +363,7 @@ ownca_privatekey_passphrase: "{{ ca_ca_password }}" ownca_not_after: "{{ ca_valid_time }}" provider: ownca - force: - "{{ not crt_info.valid_at.check_period | default(omit) or + force: "{{ not crt_info.valid_at.check_period | default(omit) or hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" backup: true delegate_to: "{{ ca_ca_host }}" From dbf51aae71bd7862d1cf6b86fd4d2baa128539b9 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 20:48:03 +0300 Subject: [PATCH 02/30] don't use passphrase in command arguments which could be logged --- tasks/ca.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tasks/ca.yml b/tasks/ca.yml index 20a67c5..7648647 100644 --- a/tasks/ca.yml +++ b/tasks/ca.yml @@ -43,12 +43,14 @@ creates: "{{ ca_ca_dir }}/ca.key.{{ ansible_date_time.iso8601 }}" - name: Generate RSA CA key - ansible.builtin.command: > - openssl genrsa - -aes256 - -passout pass:{{ ca_ca_password }} - -out {{ ca_ca_dir }}/ca.key - {{ ca_ca_keylength }} + ansible.builtin.command: + cmd: > + openssl genrsa + -aes256 + -passout stdin + -out {{ ca_ca_dir }}/ca.key + {{ ca_ca_keylength }} + stdin: "{{ ca_ca_password }}" args: creates: "{{ ca_ca_dir }}/ca.key" when: ca_ca_signing_key_algorithm == 'RSA' From acd9418bcc11dc3622ab8e01f4cb9847cda59c65 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 22:14:24 +0300 Subject: [PATCH 03/30] document key algorithm vars --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a6e428b..8659185 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_common_name`: CN for certificates (default: `{{ inventory_hostname }}`) * `ca_email`: E-Mail address for certificates (default: `root@{{ ansible_fqdn }}`) * `ca_altname_1`: First alt name (default: `{{ ansible_fqdn }}`) +* `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) * `ca_ca_keylength`: CA keylength (default: `2048`) * `ca_server_cert`: Create server certificate as well (default: `true`) * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) @@ -51,6 +52,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_client_ca_dir_owner`: User to own the certificate directory on the clients (default: `root`) * `ca_client_ca_dir_group`: Group to own the certificate directory on the clients (default: `root`) * `ca_client_ca_dir_mode`: Permissions of the certificate directory on the clients (default: `0700`) +* `ca_client_key_algorithm`: Client key generation algorithm (default: `{{ ca_ca_signing_key_algorithm }}`) * `ca_renew`: Renew certificates if they expire within `ca_check_valid_time` timeframe (default: `false`) * `ca_valid_time`: Valid time of new created certificates (default: `+365d`) * `ca_check_valid_time`: Timeframe to check if certificates will expire (default: `+2w`) From a36219a2eb55c188e93f0c1f8c706475e3aa061e Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 23:35:27 +0300 Subject: [PATCH 04/30] ca_dir_owner is customizable --- README.md | 2 ++ defaults/main.yml | 2 ++ tasks/main.yml | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8659185..0ffe9f8 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_manage_openssl`: Install `openssl` package? (default: `true`) * `ca_ca_dir`: Directory to place CA and certificates (default: `/opt/ca`) +* `ca_ca_dir_owner`: CA directory owner (default: `root`) +* `ca_ca_dir_group`: CA directory group (default: `root`) * `ca_ca_days`: Runtime of the CA certificate (default: `3650`) * `ca_ca_password`: Password of CA key (default: `ChangeMe`) * `ca_localdir`: Temporary directory on Ansible management host (default: `/tmp/ca`) diff --git a/defaults/main.yml b/defaults/main.yml index 1ac75cb..3c06d34 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,6 +1,8 @@ --- ca_manage_openssl: true ca_ca_dir: /opt/ca +ca_ca_dir_owner: root +ca_ca_dir_group: root ca_client_ca_dir: /opt/ca ca_client_ca_dir_owner: root ca_client_ca_dir_group: root diff --git a/tasks/main.yml b/tasks/main.yml index 6dca271..c2301fb 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -10,8 +10,8 @@ - name: Ensure CA directory exists ansible.builtin.file: path: "{{ ca_ca_dir }}" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0700 state: directory delegate_to: "{{ ca_ca_host }}" @@ -77,8 +77,8 @@ ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}.csr" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0600 delegate_to: "{{ ca_ca_host }}" @@ -158,8 +158,8 @@ ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-server.csr" dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}-server.csr" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0600 delegate_to: "{{ ca_ca_host }}" @@ -235,8 +235,8 @@ - name: Set permissions on Logstash key ansible.builtin.file: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" - owner: root - group: root + owner: "{{ ca_client_ca_dir_owner }}" + group: "{{ ca_client_ca_dir_group }}" mode: 0600 ### etcd peer certificate ### @@ -278,8 +278,8 @@ ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd.csr" dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}-etcd.csr" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0600 delegate_to: "{{ ca_ca_host }}" @@ -343,8 +343,8 @@ ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd-server.csr" dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}-etcd-server.csr" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0600 delegate_to: "{{ ca_ca_host }}" From 9878f9afbd68eec20c8fda59ce37c47f96a095ef Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 23:46:29 +0300 Subject: [PATCH 05/30] require user to specify ca_ca_password --- README.md | 2 +- defaults/main.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ffe9f8..a627d72 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_ca_dir_owner`: CA directory owner (default: `root`) * `ca_ca_dir_group`: CA directory group (default: `root`) * `ca_ca_days`: Runtime of the CA certificate (default: `3650`) -* `ca_ca_password`: Password of CA key (default: `ChangeMe`) +* `ca_ca_password`: Password of CA key (no default, should be defined by user) * `ca_localdir`: Temporary directory on Ansible management host (default: `/tmp/ca`) * `ca_local_become`: Use `become` on the Ansible controller. Used for creation of `ca_localdir`. (default: `false`) * `ca_ca_host`: Hostname of the CA host (default: `localhost`) diff --git a/defaults/main.yml b/defaults/main.yml index 3c06d34..04bdfce 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -8,7 +8,6 @@ ca_client_ca_dir_owner: root ca_client_ca_dir_group: root ca_client_ca_dir_mode: 0700 ca_client_key_algorithm: "{{ ca_ca_signing_key_algorithm }}" -ca_ca_password: ChangeMe ca_localdir: /tmp/ca ca_local_become: false ca_ca_host: localhost From 780fd64a59504d232d67182161806474354a6ffd Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Mar 2024 23:47:09 +0300 Subject: [PATCH 06/30] do prepare both for RSA and Ed25519 CAs --- molecule/ca-renew/converge.yml | 1 + molecule/ca-renew/prepare.yml | 14 ++++++++++---- molecule/ca-renew/verify.yml | 2 ++ molecule/default/converge.yml | 1 + molecule/default/verify.yml | 2 ++ molecule/renew/converge.yml | 1 + molecule/renew/prepare.yml | 14 ++++++++++---- molecule/renew/verify.yml | 2 ++ 8 files changed, 29 insertions(+), 8 deletions(-) diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 973eeaa..4ce55c9 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -7,6 +7,7 @@ hosts: all vars: ca_ca_host: ca_default + ca_ca_password: ChangeMe ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index 6c96a2c..d86b284 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -3,14 +3,15 @@ hosts: all vars: ca_ca_host: ca_default + ca_ca_password: ChangeMe ca_logstash: true ca_etcd: true ca_etcd_group: molecule ca_ca_days: 5 ca_valid_time: "+14d" ca_renew: true - tasks: + pre_tasks: - name: Install Python libraries pip: name: cryptography>= 1.2.3 @@ -32,6 +33,11 @@ - name: Gather facts again to define ansible_default_ipv4 setup: - - name: "Include CA role" - include_role: - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + roles: + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index 98a7b92..65ec841 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -1,6 +1,8 @@ --- - name: Verify hosts: all + vars: + ca_ca_password: ChangeMe tasks: - name: Verify RSA ansible.builtin.include_tasks: verify_tasks.yml diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 7784b1e..1dc0bbe 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -7,6 +7,7 @@ hosts: all vars: ca_ca_host: ca_default + ca_ca_password: ChangeMe ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 98a7b92..65ec841 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -1,6 +1,8 @@ --- - name: Verify hosts: all + vars: + ca_ca_password: ChangeMe tasks: - name: Verify RSA ansible.builtin.include_tasks: verify_tasks.yml diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 6540a00..13477af 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -7,6 +7,7 @@ hosts: all vars: ca_ca_host: ca_default + ca_ca_password: ChangeMe ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index f251046..695eaa9 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -3,12 +3,13 @@ hosts: all vars: ca_ca_host: ca_default + ca_ca_password: ChangeMe ca_logstash: true ca_etcd: true ca_etcd_group: molecule ca_valid_time: "+5d" ca_renew: true - tasks: + pre_tasks: - name: Install Python libraries pip: name: cryptography>= 1.2.3 @@ -30,6 +31,11 @@ - name: Gather facts again to define ansible_default_ipv4 setup: - - name: "Include CA role" - include_role: - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + roles: + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + vars: + ca_ca_signing_key_algorithm: Ed25519 + ca_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/ca-ed25519 + ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index 98a7b92..65ec841 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -1,6 +1,8 @@ --- - name: Verify hosts: all + vars: + ca_ca_password: ChangeMe tasks: - name: Verify RSA ansible.builtin.include_tasks: verify_tasks.yml From db3deab730da1b60168e9827b7698355f68fe161 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 00:21:32 +0300 Subject: [PATCH 07/30] distinguished name components are omitted by default --- README.md | 14 +++++++------- defaults/main.yml | 7 ------- tasks/main.yml | 16 ++++++++-------- templates/ca.conf.j2 | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a627d72..7bbbf82 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_localdir`: Temporary directory on Ansible management host (default: `/tmp/ca`) * `ca_local_become`: Use `become` on the Ansible controller. Used for creation of `ca_localdir`. (default: `false`) * `ca_ca_host`: Hostname of the CA host (default: `localhost`) -* `ca_country`: Setting for certificates (default: `EX`) -* `ca_state`: Setting for certificates (default: `EX`) -* `ca_locality`: Setting for certificates (default: `EX`) -* `ca_postalcode`: Setting for certificates (default: `1234`) -* `ca_organization`: Setting for certificates (default: `example`) -* `ca_organizationalunit`: Setting for certificates (default: `example`) +* `ca_country`: Setting for certificates (omitted by default) +* `ca_state`: Setting for certificates (omitted by default) +* `ca_locality`: Setting for certificates (omitted by default) +* `ca_postalcode`: Setting for certificates (omitted by default) +* `ca_organization`: Setting for certificates (omitted by default) +* `ca_organizationalunit`: Setting for certificates (omitted by default) * `ca_common_name`: CN for certificates (default: `{{ inventory_hostname }}`) -* `ca_email`: E-Mail address for certificates (default: `root@{{ ansible_fqdn }}`) +* `ca_email`: E-Mail address for certificates (omitted by default) * `ca_altname_1`: First alt name (default: `{{ ansible_fqdn }}`) * `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) * `ca_ca_keylength`: CA keylength (default: `2048`) diff --git a/defaults/main.yml b/defaults/main.yml index 04bdfce..c214760 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -18,14 +18,7 @@ ca_server_cert: true ca_logstash: false ca_etcd: false -ca_country: EX -ca_state: EX -ca_locality: EX -ca_postalcode: 1234 -ca_organization: example -ca_organizationalunit: example ca_common_name: "{{ inventory_hostname }}" -ca_email: "root@{{ ansible_fqdn }}" ca_altname_1: "{{ ansible_hostname }}" ca_altname_2: "{{ ansible_fqdn }}" ca_altname_3: "{{ inventory_hostname }}" diff --git a/tasks/main.yml b/tasks/main.yml index c2301fb..9a102bd 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -61,8 +61,8 @@ path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" - country_name: "{{ ca_country }}" - organization_name: "{{ ca_organization }}" + country_name: "{{ ca_country | default(omit, true) }}" + organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ inventory_hostname }}" subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -142,8 +142,8 @@ path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.csr" privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" - country_name: "{{ ca_country }}" - organization_name: "{{ ca_organization }}" + country_name: "{{ ca_country | default(omit, true) }}" + organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ inventory_hostname }}" subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -262,8 +262,8 @@ community.crypto.openssl_csr: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.csr" privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - country_name: "{{ ca_country }}" - organization_name: "{{ ca_organization }}" + country_name: "{{ ca_country | default(omit, true) }}" + organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ inventory_hostname }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -327,8 +327,8 @@ community.crypto.openssl_csr: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.csr" privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - country_name: "{{ ca_country }}" - organization_name: "{{ ca_organization }}" + country_name: "{{ ca_country | default(omit, true) }}" + organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ inventory_hostname }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" diff --git a/templates/ca.conf.j2 b/templates/ca.conf.j2 index 97d04f8..80a6f24 100644 --- a/templates/ca.conf.j2 +++ b/templates/ca.conf.j2 @@ -4,14 +4,28 @@ req_extensions = v3_req prompt = no [req_distinguished_name] +{% if ca_country is defined %} countryName = {{ ca_country }} +{% endif %} +{% if ca_state is defined %} stateOrProvinceName = {{ ca_state }} +{% endif %} +{% if ca_localicy is defined %} localityName = {{ ca_locality }} +{% endif %} +{% if ca_postalcode is defined %} postalCode = {{ ca_postalcode }} +{% endif %} +{% if ca_organization is defined %} organizationName = {{ ca_organization }} +{% endif %} +{% if ca_organizationunit is defined %} organizationalUnitName = {{ ca_organizationalunit }} +{% endif %} commonName = {{ ca_common_name }} +{% if ca_email is defined %} emailAddress = {{ ca_email }} +{% endif %} {% if _ca_ca_openssl_version_3 | bool %} [v3_ca] From c9e4d69eb969f5e912c5e819ffa547c614fdf83c Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 00:28:19 +0300 Subject: [PATCH 08/30] use variables with defaults for common name and alternative names --- tasks/main.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 9a102bd..285a099 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -36,12 +36,12 @@ ansible.builtin.import_tasks: ca.yml when: ca_ca_host == inventory_hostname -- name: Set ca_san to hostname, FQDN, and inventory hostname +- name: Set ca_san to alternative names or (hostname, FQDN, and inventory hostname) ansible.builtin.set_fact: ca_san: >- - DNS:{{ ansible_hostname }}, - DNS:{{ ansible_fqdn }}, - DNS:{{ inventory_hostname }} + DNS:{{ ca_altname_1 }}, + DNS:{{ ca_altname_2 }}, + DNS:{{ ca_altname_3 }} ### client certificate ### @@ -63,7 +63,7 @@ privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" - common_name: "{{ inventory_hostname }}" + common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -144,7 +144,7 @@ privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" - common_name: "{{ inventory_hostname }}" + common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -264,7 +264,7 @@ privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" - common_name: "{{ inventory_hostname }}" + common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" @@ -329,7 +329,7 @@ privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" - common_name: "{{ inventory_hostname }}" + common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" From f0226de999925a04fb943b0c4d4c97f66dd26d11 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 22:18:28 +0300 Subject: [PATCH 09/30] add certificate update handlers --- README.md | 17 +++++++++++++++++ handlers/main.yml | 11 +++++++++++ tasks/main.yml | 4 ++++ 3 files changed, 32 insertions(+) create mode 100644 handlers/main.yml diff --git a/README.md b/README.md index 7bbbf82..935f79c 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,28 @@ All of these have the default value `false`. * `ca_ls7_workaround`: Enable pinning key parameters for a Logstash compatible key. These settings make sure the key works with a certain combination of OpenSSL and Logstash. Symptom: Logstash logs that a valid PKCS8 key is invalid. * `ca_ls7_workaround_cipher`: The cipher to use for the workaround (default: `PBE-SHA1-RC4-128`) +## Notification handlers + +It's possible to register handlers to run actions on certificate change. For example, to reload service and use the updated certificate. + +The following handler names are available for registration: + +* `ansible-role-ca : on certificate change`: runs on client certificate change +* `ansible-role-ca : on server certificate change`: runs on server certificate change +* `ansible-role-ca : on etcd certificate change`: runs on etcd certificate change +* `ansible-role-ca : on etcd server certificate change`: runs on etcd server certificate change + + ## Example Playbook ## - hosts: all roles: - ca + handlers: + - name: "ansible-role-ca : on certificate change" + ansible.builtin.systemd_service: + name: my_tls_service + state: reloaded ## Contributing ## diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..dd7e6ea --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,11 @@ +- name: "ansible-role-ca : on certificate change" + meta: noop + +- name: "ansible-role-ca : on server certificate change" + meta: noop + +- name: "ansible-role-ca : on etcd certificate change" + meta: noop + +- name: "ansible-role-ca : on etcd server certificate change" + meta: noop diff --git a/tasks/main.yml b/tasks/main.yml index 285a099..1519262 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -116,6 +116,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" + notify: "ansible-role-ca : on certificate change" - name: Fetch CA certificate ansible.builtin.fetch: @@ -197,6 +198,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" + notify: "ansible-role-ca : on server certificate change" - name: Handle Logstash compatible key when: ca_logstash | bool @@ -317,6 +319,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" + notify: "ansible-role-ca : on etcd certificate change" ### etcd server certificate ### @@ -382,3 +385,4 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" + notify: "ansible-role-ca : on etcd server certificate change" From 3ed008d2c69afe669edab8996c6a0ba03b274529 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 22:19:05 +0300 Subject: [PATCH 10/30] tests for handlers, notAfter validation uses openssl -checkvalid --- molecule/ca-renew/converge.yml | 11 ++- molecule/ca-renew/prepare.yml | 11 ++- molecule/ca-renew/verify.yml | 8 ++- molecule/ca-renew/verify_tasks.yml | 107 ++++++++++------------------- molecule/default/verify_tasks.yml | 10 +-- molecule/renew/converge.yml | 12 +++- molecule/renew/prepare.yml | 11 ++- molecule/renew/verify.yml | 8 ++- molecule/renew/verify_tasks.yml | 107 ++++++++++------------------- 9 files changed, 133 insertions(+), 152 deletions(-) diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 4ce55c9..4420f0e 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -8,6 +8,8 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe + # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change + ca_client_ca_dir: /opt/certs ca_logstash: true ca_etcd: true ca_etcd_group: molecule @@ -20,5 +22,12 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 + + handlers: + # runs once for both CAs for each notified host + - name: "ansible-role-ca : on certificate change" + file: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" + state: touch diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index d86b284..bbc9947 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -4,6 +4,8 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe + # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change + ca_client_ca_dir: /opt/certs ca_logstash: true ca_etcd: true ca_etcd_group: molecule @@ -39,5 +41,12 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 + + handlers: + # runs once for both CAs for each notified host + - name: "ansible-role-ca : on certificate change" + file: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" + state: touch diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index 65ec841..48b9ca2 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -3,16 +3,20 @@ hosts: all vars: ca_ca_password: ChangeMe + valid_days: 365 + ca_ca_days: 5 + # allowed delta between certificate generation and notAfter validation + max_test_duration_seconds: 900 tasks: - name: Verify RSA ansible.builtin.include_tasks: verify_tasks.yml vars: ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca + ca_client_ca_dir: /opt/certs - name: Verify Ed25519 ansible.builtin.include_tasks: verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/ca-renew/verify_tasks.yml b/molecule/ca-renew/verify_tasks.yml index f733d96..62458df 100644 --- a/molecule/ca-renew/verify_tasks.yml +++ b/molecule/ca-renew/verify_tasks.yml @@ -2,14 +2,14 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - name: Verify signature on server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - name: Check if instance key is present @@ -38,21 +38,21 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Register SAN of etcd peer certificate @@ -110,88 +110,57 @@ not in etcd_san_server_stat.stdout - '"127.0.0.1" not in etcd_san_server_stat.stdout' -- name: Get next year - set_fact: - next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" - -- name: Register notAfter of client certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of client certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - | grep notAfter - register: client_crt_stat - -- name: Fail if notAfter of client certificate is not next year - fail: - msg: "Failed: notAfter of client certificate is not next year" - when: next_year | string not in client_crt_stat.stdout -- name: Register notAfter of server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of server certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - | grep notAfter - register: server_crt_stat -- name: Fail if notAfter of server certificate is not next year - fail: - msg: "Failed: notAfter of server certificate is not next year" - when: next_year | string not in server_crt_stat.stdout - -- name: Register notAfter of etcd certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of etcd certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - | grep notAfter - register: etcd_crt_stat -- name: Fail if notAfter of etcd certificate is not next year - fail: - msg: "Failed: notAfter of etcd certificate is not next year" - when: next_year | string not in etcd_crt_stat.stdout - -- name: Register notAfter of etcd server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of etcd server certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - | grep notAfter - register: etcd_server_crt_stat -- name: Fail if notAfter of etcd server certificate is not next year - fail: - msg: "Failed: notAfter of etcd server certificate is not next year" - when: next_year | string not in etcd_server_crt_stat.stdout - -- name: Get year of next decade to check CA - set_fact: - next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" - -- name: Register notAfter of CA certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of CA certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/ca.crt - | grep notAfter - register: ca_crt_stat -# Caution: -# Can fail at end or beginning of year, since -# leap year days can be +- 3 -- name: Fail if notAfter of CA certificate is not next decade +- name: Register if on certificate change handler has run for initial client certificate + stat: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" + register: initial_handler_file + +- name: Fail if initial file of on certificate change handler does not exist + fail: + msg: "Failed because on certificate change handler hasn't created initial file" + when: not initial_handler_file.stat.exists + +- name: Register if on certificate change handler has run for renewed client certificate + stat: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" + register: renewed_handler_file + +- name: Fail if renewed file of on certificate change handler does not exist fail: - msg: "Failed: notAfter of CA certificate is not next decade" - when: next_decade | string not in ca_crt_stat.stdout + msg: "Failed because on certificate change handler hasn't created renewed file" + when: not renewed_handler_file.stat.exists diff --git a/molecule/default/verify_tasks.yml b/molecule/default/verify_tasks.yml index 8767496..08671b1 100644 --- a/molecule/default/verify_tasks.yml +++ b/molecule/default/verify_tasks.yml @@ -2,14 +2,14 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - name: Verify signature on server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - name: Check if instance key is present @@ -38,21 +38,21 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Register SAN of etcd peer certificate diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 13477af..1dba045 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -8,10 +8,11 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe + # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change + ca_client_ca_dir: /opt/certs ca_logstash: true ca_etcd: true ca_etcd_group: molecule - ca_ca_days: 3650 ca_valid_time: +365d ca_check_valid_time: +1w ca_renew: true @@ -22,5 +23,12 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 + + handlers: + # runs once for both CAs for each notified host + - name: "ansible-role-ca : on certificate change" + file: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" + state: touch diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 695eaa9..941293a 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -4,6 +4,8 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe + # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change + ca_client_ca_dir: /opt/certs ca_logstash: true ca_etcd: true ca_etcd_group: molecule @@ -37,5 +39,12 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 + + handlers: + # runs once for both CAs for each notified host + - name: "ansible-role-ca : on certificate change" + file: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" + state: touch diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index 65ec841..7f4d828 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -3,16 +3,20 @@ hosts: all vars: ca_ca_password: ChangeMe + ca_ca_days: 3650 + valid_days: 5 + # allowed delta between certificate generation and notAfter validation + max_test_duration_seconds: 900 tasks: - name: Verify RSA ansible.builtin.include_tasks: verify_tasks.yml vars: ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca + ca_client_ca_dir: /opt/certs - name: Verify Ed25519 ansible.builtin.include_tasks: verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_client_ca_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/renew/verify_tasks.yml b/molecule/renew/verify_tasks.yml index 66df987..aab58fe 100644 --- a/molecule/renew/verify_tasks.yml +++ b/molecule/renew/verify_tasks.yml @@ -2,14 +2,14 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - name: Verify signature on server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - name: Check if instance key is present @@ -38,21 +38,21 @@ command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Verify signature on etcd server certificate command: > openssl verify -verbose - -CAfile {{ ca_ca_dir }}/ca.crt + -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - name: Register SAN of etcd peer certificate @@ -109,88 +109,57 @@ not in etcd_san_server_stat.stdout - '"127.0.0.1" not in etcd_san_server_stat.stdout' -- name: Get next year - set_fact: - next_year: "{{ ( ansible_date_time.date.split('-')[0] | int ) +1 }}" - -- name: Register notAfter of client certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of client certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt - | grep notAfter - register: client_crt_stat - -- name: Fail if notAfter of client certificate is not next year - fail: - msg: "Failed: notAfter of client certificate is not next year" - when: next_year | string not in client_crt_stat.stdout -- name: Register notAfter of server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of server certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt - | grep notAfter - register: server_crt_stat -- name: Fail if notAfter of server certificate is not next year - fail: - msg: "Failed: notAfter of server certificate is not next year" - when: next_year | string not in server_crt_stat.stdout - -- name: Register notAfter of etcd certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of etcd certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt - | grep notAfter - register: etcd_crt_stat -- name: Fail if notAfter of etcd certificate is not next year - fail: - msg: "Failed: notAfter of etcd certificate is not next year" - when: next_year | string not in etcd_crt_stat.stdout - -- name: Register notAfter of etcd server certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of etcd server certificate is within {{ valid_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt - | grep notAfter - register: etcd_server_crt_stat -- name: Fail if notAfter of etcd server certificate is not next year - fail: - msg: "Failed: notAfter of etcd server certificate isn't next year" - when: next_year | string not in etcd_server_crt_stat.stdout - -- name: Get year of next decade to check CA - set_fact: - next_decade: "{{ ( ansible_date_time.date.split('-')[0] | int ) +10 }}" - -- name: Register notAfter of CA certificate - shell: > - if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; +- name: Check if notAfter of CA certificate is valid within {{ ca_ca_days }} days + command: > openssl x509 -noout - -dates + -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/ca.crt - | grep notAfter - register: ca_crt_stat -# Caution: -# Can fail at end or beginning of year, since -# leap year days can be +- 3 -- name: Fail if notAfter of CA certificate is not next decade +- name: Register if on certificate change handler has run for initial client certificate + stat: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" + register: initial_handler_file + +- name: Fail if initial file of on certificate change handler does not exist + fail: + msg: "Failed because on certificate change handler hasn't created initial file" + when: not initial_handler_file.stat.exists + +- name: Register if on certificate change handler has run for renewed client certificate + stat: + path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" + register: renewed_handler_file + +- name: Fail if renewed file of on certificate change handler does not exist fail: - msg: "Failed because notAfter of CA certificate is not next decade" - when: next_decade | string not in ca_crt_stat.stdout + msg: "Failed because on certificate change handler hasn't created renewed file" + when: not renewed_handler_file.stat.exists From 58c5d917e6354ca29589c584becb41b976a70ae3 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 22:20:05 +0300 Subject: [PATCH 11/30] fix bugs with wrong ca.crt path for clients and run_once for CA and localhost tasks --- tasks/main.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 1519262..346c8e2 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -15,6 +15,7 @@ mode: 0700 state: directory delegate_to: "{{ ca_ca_host }}" + run_once: true - name: Ensure local directory on Ansible host exists ansible.builtin.file: @@ -23,6 +24,7 @@ mode: 0700 become: "{{ ca_local_become }}" delegate_to: localhost + run_once: true - name: Install openssl ansible.builtin.package: @@ -124,6 +126,7 @@ dest: "{{ ca_localdir }}/ca.crt" flat: true delegate_to: "{{ ca_ca_host }}" + run_once: true - name: Push CA certificate to client ansible.builtin.copy: @@ -167,7 +170,7 @@ - name: Check if server certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_ca_dir }}/{{ inventory_hostname }}-server.crt" + crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt" when: ca_renew | bool - name: (Re)sign server CSR with CA key @@ -288,7 +291,7 @@ - name: Check if etcd peer certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_ca_dir }}/{{ inventory_hostname }}-etcd.crt" + crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt" when: ca_renew | bool - name: (Re)sign CSR with CA key @@ -354,7 +357,7 @@ - name: Check if etcd server certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt" + crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt" when: ca_renew | bool - name: (Re)sign CSR with CA key From 2e0858044e0ef78cb8cd8fed09586fb19f03d2b3 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sun, 10 Mar 2024 23:56:04 +0300 Subject: [PATCH 12/30] fix ansible-lint errors and enable ansible-lint in CI --- .github/workflows/molecule.yml | 3 + README.md | 10 +-- handlers/main.yml | 16 ++--- molecule/ca-renew/converge.yml | 9 +-- molecule/ca-renew/prepare.yml | 17 ++--- .../ca-renew/{ => tasks}/verify_tasks.yml | 66 +++++++++++-------- molecule/ca-renew/verify.yml | 4 +- molecule/default/converge.yml | 4 +- molecule/default/prepare.yml | 6 +- molecule/default/{ => tasks}/verify_tasks.yml | 33 ++++++---- molecule/default/verify.yml | 4 +- molecule/renew/converge.yml | 9 +-- molecule/renew/prepare.yml | 17 ++--- molecule/renew/{ => tasks}/verify_tasks.yml | 66 +++++++++++-------- molecule/renew/verify.yml | 4 +- tasks/main.yml | 16 ++--- 16 files changed, 159 insertions(+), 125 deletions(-) rename molecule/ca-renew/{ => tasks}/verify_tasks.yml (77%) rename molecule/default/{ => tasks}/verify_tasks.yml (84%) rename molecule/renew/{ => tasks}/verify_tasks.yml (77%) diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 884b1a2..49c3114 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -38,6 +38,9 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install -r requirements-test.txt + - name: ${{ matrix.scenario }} ansible-lint + run: ansible-lint + - name: ${{ matrix.scenario }} molecule test run: | molecule test -s ${{ matrix.scenario }} diff --git a/README.md b/README.md index 935f79c..18e88fe 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,10 @@ It's possible to register handlers to run actions on certificate change. For exa The following handler names are available for registration: -* `ansible-role-ca : on certificate change`: runs on client certificate change -* `ansible-role-ca : on server certificate change`: runs on server certificate change -* `ansible-role-ca : on etcd certificate change`: runs on etcd certificate change -* `ansible-role-ca : on etcd server certificate change`: runs on etcd server certificate change +* `Ansible-role-ca : on certificate change`: runs on client certificate change +* `Ansible-role-ca : on server certificate change`: runs on server certificate change +* `Ansible-role-ca : on etcd certificate change`: runs on etcd certificate change +* `Ansible-role-ca : on etcd server certificate change`: runs on etcd server certificate change ## Example Playbook ## @@ -88,7 +88,7 @@ The following handler names are available for registration: roles: - ca handlers: - - name: "ansible-role-ca : on certificate change" + - name: "Ansible-role-ca : on certificate change" ansible.builtin.systemd_service: name: my_tls_service state: reloaded diff --git a/handlers/main.yml b/handlers/main.yml index dd7e6ea..3813edb 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -1,11 +1,11 @@ -- name: "ansible-role-ca : on certificate change" - meta: noop +- name: "Ansible-role-ca : on certificate change" + ansible.builtin.meta: noop -- name: "ansible-role-ca : on server certificate change" - meta: noop +- name: "Ansible-role-ca : on server certificate change" + ansible.builtin.meta: noop -- name: "ansible-role-ca : on etcd certificate change" - meta: noop +- name: "Ansible-role-ca : on etcd certificate change" + ansible.builtin.meta: noop -- name: "ansible-role-ca : on etcd server certificate change" - meta: noop +- name: "Ansible-role-ca : on etcd server certificate change" + ansible.builtin.meta: noop diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 4420f0e..5ab6789 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -17,8 +17,8 @@ ca_valid_time: +365d ca_renew: true roles: - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 @@ -27,7 +27,8 @@ handlers: # runs once for both CAs for each notified host - - name: "ansible-role-ca : on certificate change" - file: + - name: "Ansible-role-ca : on certificate change" + ansible.builtin.file: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" state: touch + mode: 0600 diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index bbc9947..002f457 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -15,29 +15,29 @@ pre_tasks: - name: Install Python libraries - pip: + ansible.builtin.pip: name: cryptography>= 1.2.3 - name: Install packages for RHEL - package: + ansible.builtin.package: name: - iproute - NetworkManager when: ansible_os_family == "RedHat" - name: Start NetworkManager - service: + ansible.builtin.service: name: NetworkManager state: started enabled: yes when: ansible_os_family == "RedHat" - name: Gather facts again to define ansible_default_ipv4 - setup: + ansible.builtin.setup: roles: - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 @@ -46,7 +46,8 @@ handlers: # runs once for both CAs for each notified host - - name: "ansible-role-ca : on certificate change" - file: + - name: "Ansible-role-ca : on certificate change" + ansible.builtin.file: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" state: touch + mode: 0600 diff --git a/molecule/ca-renew/verify_tasks.yml b/molecule/ca-renew/tasks/verify_tasks.yml similarity index 77% rename from molecule/ca-renew/verify_tasks.yml rename to molecule/ca-renew/tasks/verify_tasks.yml index 62458df..53fe461 100644 --- a/molecule/ca-renew/verify_tasks.yml +++ b/molecule/ca-renew/tasks/verify_tasks.yml @@ -1,62 +1,67 @@ - name: Verify signature on certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + changed_when: false - name: Verify signature on server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + changed_when: false - name: Check if instance key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing - fail: + ansible.builtin.fail: msg: "Instance key is missing" when: - not instance_key_stat.stat.exists | bool - name: Check if Logstash key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing - fail: + ansible.builtin.fail: msg: "Logstash key is missing" when: - not logstash_key_stat.stat.exists | bool - name: Verify signature on etcd peer certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Register SAN of etcd peer certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout @@ -73,9 +78,10 @@ no_sigdump, no_aux" register: etcd_san_peer_stat + changed_when: false - name: Register SAN of etcd server certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout @@ -93,9 +99,10 @@ no_aux" register: etcd_san_server_stat + changed_when: false - name: Fail if SAN of etcd peer certificate is missing IP addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd peer certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] @@ -103,64 +110,69 @@ - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd server certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] not in etcd_san_server_stat.stdout - '"127.0.0.1" not in etcd_san_server_stat.stdout' -- name: Check if notAfter of client certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of client certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + changed_when: false -- name: Check if notAfter of server certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of server certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + changed_when: false -- name: Check if notAfter of etcd certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of etcd certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + changed_when: false -- name: Check if notAfter of etcd server certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of etcd server certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false -- name: Check if notAfter of CA certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of CA certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/ca.crt + changed_when: false - name: Register if on certificate change handler has run for initial client certificate - stat: + ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" register: initial_handler_file - name: Fail if initial file of on certificate change handler does not exist - fail: + ansible.builtin.fail: msg: "Failed because on certificate change handler hasn't created initial file" when: not initial_handler_file.stat.exists - name: Register if on certificate change handler has run for renewed client certificate - stat: + ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" register: renewed_handler_file - name: Fail if renewed file of on certificate change handler does not exist - fail: + ansible.builtin.fail: msg: "Failed because on certificate change handler hasn't created renewed file" when: not renewed_handler_file.stat.exists diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index 48b9ca2..67ccba7 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -9,12 +9,12 @@ max_test_duration_seconds: 900 tasks: - name: Verify RSA - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca ca_client_ca_dir: /opt/certs - name: Verify Ed25519 - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 1dc0bbe..90add7d 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -12,8 +12,8 @@ ca_etcd: true ca_etcd_group: molecule roles: - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml index f18d0b1..d645126 100644 --- a/molecule/default/prepare.yml +++ b/molecule/default/prepare.yml @@ -4,18 +4,18 @@ tasks: - name: Install Python libraries - pip: + ansible.builtin.pip: name: cryptography>= 1.2.3 - name: Install packages for RHEL - package: + ansible.builtin.package: name: - iproute - NetworkManager when: ansible_os_family == "RedHat" - name: Start NetworkManager - service: + ansible.builtin.service: name: NetworkManager state: started enabled: yes diff --git a/molecule/default/verify_tasks.yml b/molecule/default/tasks/verify_tasks.yml similarity index 84% rename from molecule/default/verify_tasks.yml rename to molecule/default/tasks/verify_tasks.yml index 08671b1..230fa27 100644 --- a/molecule/default/verify_tasks.yml +++ b/molecule/default/tasks/verify_tasks.yml @@ -1,62 +1,67 @@ - name: Verify signature on certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + changed_when: false - name: Verify signature on server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + changed_when: false - name: Check if instance key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing - fail: + ansible.builtin.fail: msg: "Instance key is missing" when: - not instance_key_stat.stat.exists | bool - name: Check if Logstash key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing - fail: + ansible.builtin.fail: msg: "Logstash key is missing" when: - not logstash_key_stat.stat.exists | bool - name: Verify signature on etcd peer certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Register SAN of etcd peer certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt @@ -72,9 +77,10 @@ no_sigdump, no_aux" register: etcd_san_peer_stat + changed_when: false - name: Register SAN of etcd server certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout @@ -91,9 +97,10 @@ no_sigdump, no_aux" register: etcd_san_server_stat + changed_when: false - name: Fail if SAN of etcd peer certificate is missing IP addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd peer certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] @@ -101,7 +108,7 @@ - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd server certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 65ec841..30028fb 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -5,12 +5,12 @@ ca_ca_password: ChangeMe tasks: - name: Verify RSA - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca ca_client_ca_dir: /opt/ca - name: Verify Ed25519 - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 1dba045..9824f14 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -18,8 +18,8 @@ ca_renew: true roles: - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 @@ -28,7 +28,8 @@ handlers: # runs once for both CAs for each notified host - - name: "ansible-role-ca : on certificate change" - file: + - name: "Ansible-role-ca : on certificate change" + ansible.builtin.file: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" state: touch + mode: 0600 diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 941293a..1831e21 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -13,29 +13,29 @@ ca_renew: true pre_tasks: - name: Install Python libraries - pip: + ansible.builtin.pip: name: cryptography>= 1.2.3 - name: Install packages for RHEL - package: + ansible.builtin.package: name: - iproute - NetworkManager when: ansible_os_family == "RedHat" - name: Start NetworkManager - service: + ansible.builtin.service: name: NetworkManager state: started enabled: yes when: ansible_os_family == "RedHat" - name: Gather facts again to define ansible_default_ipv4 - setup: + ansible.builtin.setup: roles: - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" - - name: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" + - role: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') | basename }}" vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 @@ -44,7 +44,8 @@ handlers: # runs once for both CAs for each notified host - - name: "ansible-role-ca : on certificate change" - file: + - name: "Ansible-role-ca : on certificate change" + ansible.builtin.file: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" state: touch + mode: 0600 diff --git a/molecule/renew/verify_tasks.yml b/molecule/renew/tasks/verify_tasks.yml similarity index 77% rename from molecule/renew/verify_tasks.yml rename to molecule/renew/tasks/verify_tasks.yml index aab58fe..effe927 100644 --- a/molecule/renew/verify_tasks.yml +++ b/molecule/renew/tasks/verify_tasks.yml @@ -1,62 +1,67 @@ - name: Verify signature on certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + changed_when: false - name: Verify signature on server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + changed_when: false - name: Check if instance key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing - fail: + ansible.builtin.fail: msg: "Instance key is missing" when: - not instance_key_stat.stat.exists | bool - name: Check if Logstash key is present - stat: + ansible.builtin.stat: path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing - fail: + ansible.builtin.fail: msg: "Logstash key is missing" when: - not logstash_key_stat.stat.exists | bool - name: Verify signature on etcd peer certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Verify signature on etcd server certificate - command: > + ansible.builtin.command: > openssl verify -verbose -CAfile {{ ca_client_ca_dir }}/ca.crt {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false - name: Register SAN of etcd peer certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout @@ -73,9 +78,10 @@ no_sigdump, no_aux" register: etcd_san_peer_stat + changed_when: false - name: Register SAN of etcd server certificate - command: > + ansible.builtin.command: > openssl x509 -text -noout @@ -92,9 +98,10 @@ no_sigdump, no_aux" register: etcd_san_server_stat + changed_when: false - name: Fail if SAN of etcd peer certificate is missing addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd peer certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] @@ -102,64 +109,69 @@ - '"127.0.0.1" not in etcd_san_peer_stat.stdout' - name: Fail if SAN of etcd server certificate is missing IP addresses - fail: + ansible.builtin.fail: msg: "Default IPv4 address in etcd server certifcate are missing" when: - hostvars['ca_default_client']['ansible_default_ipv4']['address'] not in etcd_san_server_stat.stdout - '"127.0.0.1" not in etcd_san_server_stat.stdout' -- name: Check if notAfter of client certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of client certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + changed_when: false -- name: Check if notAfter of server certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of server certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + changed_when: false -- name: Check if notAfter of etcd certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of etcd certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + changed_when: false -- name: Check if notAfter of etcd server certificate is within {{ valid_days }} days - command: > +- name: Check if notAfter of etcd server certificate is within valid_days={{ valid_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + changed_when: false -- name: Check if notAfter of CA certificate is valid within {{ ca_ca_days }} days - command: > +- name: Check if notAfter of CA certificate is valid within valid_days={{ ca_ca_days }} + ansible.builtin.command: > openssl x509 -noout -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} -in {{ ca_client_ca_dir }}/ca.crt + changed_when: false - name: Register if on certificate change handler has run for initial client certificate - stat: + ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" register: initial_handler_file - name: Fail if initial file of on certificate change handler does not exist - fail: + ansible.builtin.fail: msg: "Failed because on certificate change handler hasn't created initial file" when: not initial_handler_file.stat.exists - name: Register if on certificate change handler has run for renewed client certificate - stat: + ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" register: renewed_handler_file - name: Fail if renewed file of on certificate change handler does not exist - fail: + ansible.builtin.fail: msg: "Failed because on certificate change handler hasn't created renewed file" when: not renewed_handler_file.stat.exists diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index 7f4d828..f479af4 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -9,12 +9,12 @@ max_test_duration_seconds: 900 tasks: - name: Verify RSA - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca ca_client_ca_dir: /opt/certs - name: Verify Ed25519 - ansible.builtin.include_tasks: verify_tasks.yml + ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 diff --git a/tasks/main.yml b/tasks/main.yml index 346c8e2..b1ea313 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -118,7 +118,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" - notify: "ansible-role-ca : on certificate change" + notify: "Ansible-role-ca : on certificate change" - name: Fetch CA certificate ansible.builtin.fetch: @@ -201,7 +201,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" - notify: "ansible-role-ca : on server certificate change" + notify: "Ansible-role-ca : on server certificate change" - name: Handle Logstash compatible key when: ca_logstash | bool @@ -253,12 +253,8 @@ ansible.builtin.set_fact: ca_san_etcd: >- {{ ca_san }}, - IP:127.0.0.1, - {%- for host in groups[ca_etcd_group] -%} - IP:{{ hostvars[host]['ansible_default_ipv4']['address'] }} - {%- if not loop.last -%} - , - {%- endif -%} + IP:127.0.0.1,{%- for host in groups[ca_etcd_group] -%} + IP:{{ hostvars[host]['ansible_default_ipv4']['address'] }}{%- if not loop.last -%},{%- endif -%} {%- endfor -%} when: - ca_etcd | bool @@ -322,7 +318,7 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" - notify: "ansible-role-ca : on etcd certificate change" + notify: "Ansible-role-ca : on etcd certificate change" ### etcd server certificate ### @@ -388,4 +384,4 @@ owner: "{{ ca_client_ca_dir_owner }}" group: "{{ ca_client_ca_dir_group }}" mode: "{{ ca_client_ca_dir_mode }}" - notify: "ansible-role-ca : on etcd server certificate change" + notify: "Ansible-role-ca : on etcd server certificate change" From 3d5fbce15b6b9ac42fc0c8563dac989beb75962e Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Tue, 12 Mar 2024 18:47:47 +0300 Subject: [PATCH 13/30] add ca_alternative_name variable, nullable ca_altnameX vars --- README.md | 5 ++++- defaults/main.yml | 1 + tasks/main.yml | 13 +++---------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 18e88fe..f5871aa 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,10 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_organizationalunit`: Setting for certificates (omitted by default) * `ca_common_name`: CN for certificates (default: `{{ inventory_hostname }}`) * `ca_email`: E-Mail address for certificates (omitted by default) -* `ca_altname_1`: First alt name (default: `{{ ansible_fqdn }}`) +* `ca_subject_alternative_name`: Value for certificate `subjectAltName` field (default: `DNS:{{ ca_altname_1 }},DNS:{{ ca_altname_2 }},DNS:{{ ca_altname_3}}`, omitted if all `ca_altnameX` varaibles are `null`) +* `ca_altname_1`: First default alt name (default: `{{ ansible_hostname }}`). Omitted when set to `null`. +* `ca_altname_2`: Second default alt name (default: `{{ ansible_fqdn }}`). Omitted when set to `null`. +* `ca_altname_3`: Third default alt name (default: `{{ inventory_hostname }}`). Omitted when set to `null`. * `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) * `ca_ca_keylength`: CA keylength (default: `2048`) * `ca_server_cert`: Create server certificate as well (default: `true`) diff --git a/defaults/main.yml b/defaults/main.yml index c214760..4f7bf57 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -22,6 +22,7 @@ ca_common_name: "{{ inventory_hostname }}" ca_altname_1: "{{ ansible_hostname }}" ca_altname_2: "{{ ansible_fqdn }}" ca_altname_3: "{{ inventory_hostname }}" +ca_subject_alternative_name: "{{ [ca_altname_1, ca_altname_2, ca_altname_3] | reject('none') | map('regex_replace', '^(.*)$', 'DNS:\\1') | join('') }}" ca_ca_keylength: 2048 ca_ls7_workaround: false diff --git a/tasks/main.yml b/tasks/main.yml index b1ea313..aa20236 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -38,13 +38,6 @@ ansible.builtin.import_tasks: ca.yml when: ca_ca_host == inventory_hostname -- name: Set ca_san to alternative names or (hostname, FQDN, and inventory hostname) - ansible.builtin.set_fact: - ca_san: >- - DNS:{{ ca_altname_1 }}, - DNS:{{ ca_altname_2 }}, - DNS:{{ ca_altname_3 }} - ### client certificate ### - name: Create key @@ -66,7 +59,7 @@ country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" - subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" + subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" - name: Pull CSR @@ -149,7 +142,7 @@ country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" - subject_alt_name: "{{ ca_san | regex_replace(' ', '') }}" + subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" - name: Pull server CSR @@ -252,7 +245,7 @@ - name: Add IP addresses for etcd to ca_san_etcd ansible.builtin.set_fact: ca_san_etcd: >- - {{ ca_san }}, + {{ ca_subject_alternative_name }}, IP:127.0.0.1,{%- for host in groups[ca_etcd_group] -%} IP:{{ hostvars[host]['ansible_default_ipv4']['address'] }}{%- if not loop.last -%},{%- endif -%} {%- endfor -%} From 20c50c00ac6a41bf4284c6023126f81677a37965 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Tue, 12 Mar 2024 21:14:32 +0300 Subject: [PATCH 14/30] include x509v3 extensions to CA certificate --- tasks/ca.yml | 2 +- templates/ca.conf.j2 | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/tasks/ca.yml b/tasks/ca.yml index 7648647..8c1e74c 100644 --- a/tasks/ca.yml +++ b/tasks/ca.yml @@ -96,8 +96,8 @@ ansible.builtin.command: cmd: > openssl req - -x509 -new + -x509 -key {{ ca_ca_dir }}/ca.key -passin stdin -sha256 diff --git a/templates/ca.conf.j2 b/templates/ca.conf.j2 index 80a6f24..42053f2 100644 --- a/templates/ca.conf.j2 +++ b/templates/ca.conf.j2 @@ -1,6 +1,7 @@ [req] distinguished_name = req_distinguished_name -req_extensions = v3_req +req_extensions = v3_req_ca +x509_extensions = v3_req_ca prompt = no [req_distinguished_name] @@ -27,19 +28,12 @@ commonName = {{ ca_common_name }} emailAddress = {{ ca_email }} {% endif %} -{% if _ca_ca_openssl_version_3 | bool %} -[v3_ca] -basicConstraints = critical,CA:true,pathlen:0 -keyUsage = critical,keyCertSign,cRLSign +[v3_req_ca] +basicConstraints = critical, CA:true, pathlen:1 +# digitalSignature is needed if CA acts as a OCSP responder +keyUsage = keyCertSign, cRLSign, digitalSignature +# To facilitate certification path construction, this extension MUST +# appear in all conforming CA certificates, that is, all certificates +# including the basic constraints extension +# https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2 subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer - -{% endif %} -[v3_req] -keyUsage = keyEncipherment, dataEncipherment -extendedKeyUsage = serverAuth -subjectAltName = @alt_names - -[alt_names] -DNS.1 = {{ ca_altname_1 }} -DNS.2 = {{ ca_altname_2 }} From 50bf19c675aab40c06b9b855cfc8235ff08f669a Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Tue, 12 Mar 2024 21:46:27 +0300 Subject: [PATCH 15/30] fix typo --- templates/ca.conf.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/ca.conf.j2 b/templates/ca.conf.j2 index 42053f2..015f590 100644 --- a/templates/ca.conf.j2 +++ b/templates/ca.conf.j2 @@ -11,7 +11,7 @@ countryName = {{ ca_country }} {% if ca_state is defined %} stateOrProvinceName = {{ ca_state }} {% endif %} -{% if ca_localicy is defined %} +{% if ca_locality is defined %} localityName = {{ ca_locality }} {% endif %} {% if ca_postalcode is defined %} From 3d77d7dc084c0038e1c500b3523598fa9db3c8c4 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 11:35:40 +0300 Subject: [PATCH 16/30] don't generate client cert on CA host by default --- defaults/main.yml | 1 + molecule/ca-renew/converge.yml | 2 + molecule/ca-renew/prepare.yml | 2 + molecule/ca-renew/verify.yml | 2 + molecule/default/converge.yml | 2 + molecule/default/verify.yml | 2 + molecule/renew/converge.yml | 2 + molecule/renew/prepare.yml | 2 + molecule/renew/verify.yml | 2 + tasks/main.yml | 187 +++++++++++++++++---------------- 10 files changed, 112 insertions(+), 92 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 4f7bf57..7c785f5 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -14,6 +14,7 @@ ca_ca_host: localhost ca_ca_signing_key_algorithm: RSA ca_ca_signing_key_params: "" +ca_client_cert: "{{ ca_ca_host != inventory_hostname }}" ca_server_cert: true ca_logstash: false ca_etcd: false diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 5ab6789..33e736e 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -10,6 +10,8 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_client_ca_dir: /opt/certs + ca_client_cert: true + ca_server_cert: true ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index 002f457..8ed7c15 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -6,6 +6,8 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_client_ca_dir: /opt/certs + ca_client_cert: true + ca_server_cert: true ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index 67ccba7..a5dc2ad 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -3,6 +3,8 @@ hosts: all vars: ca_ca_password: ChangeMe + ca_client_cert: true + ca_server_cert: true valid_days: 365 ca_ca_days: 5 # allowed delta between certificate generation and notAfter validation diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 90add7d..fd4637e 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -8,6 +8,8 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe + ca_client_cert: true + ca_server_cert: true ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 30028fb..3d58492 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -3,6 +3,8 @@ hosts: all vars: ca_ca_password: ChangeMe + ca_client_cert: true + ca_server_cert: true tasks: - name: Verify RSA ansible.builtin.include_tasks: tasks/verify_tasks.yml diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 9824f14..fed9740 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -10,6 +10,8 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_client_ca_dir: /opt/certs + ca_client_cert: true + ca_server_cert: true ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 1831e21..1ccfa23 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -6,6 +6,8 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_client_ca_dir: /opt/certs + ca_client_cert: true + ca_server_cert: true ca_logstash: true ca_etcd: true ca_etcd_group: molecule diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index f479af4..d756412 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -5,6 +5,8 @@ ca_ca_password: ChangeMe ca_ca_days: 3650 valid_days: 5 + ca_client_cert: true + ca_server_cert: true # allowed delta between certificate generation and notAfter validation max_test_duration_seconds: 900 tasks: diff --git a/tasks/main.yml b/tasks/main.yml index aa20236..87327b4 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,12 +1,4 @@ --- -- name: Ensure CA directory exists on clients - ansible.builtin.file: - path: "{{ ca_client_ca_dir }}" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" - state: directory - - name: Ensure CA directory exists ansible.builtin.file: path: "{{ ca_ca_dir }}" @@ -40,94 +32,105 @@ ### client certificate ### -- name: Create key - community.crypto.openssl_privatekey: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - passphrase: "{{ ca_keypassphrase | default(omit, true) }}" - cipher: "{{ ca_openssl_cipher | default(omit, true) }}" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" - type: "{{ ca_client_key_algorithm }}" - register: key - -- name: Create CSR - community.crypto.openssl_csr: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" - privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" - privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" - country_name: "{{ ca_country | default(omit, true) }}" - organization_name: "{{ ca_organization | default(omit, true) }}" - common_name: "{{ ca_common_name }}" - subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" - -- name: Pull CSR - ansible.builtin.fetch: - src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" - dest: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" - flat: true - -- name: Push CSR to CA host - ansible.builtin.copy: - src: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" - dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}.csr" - owner: "{{ ca_ca_dir_owner }}" - group: "{{ ca_ca_dir_group }}" - mode: 0600 - delegate_to: "{{ ca_ca_host }}" +- name: Create client certificate + when: ca_client_cert | bool + block: + - name: Ensure CA directory exists on clients + ansible.builtin.file: + path: "{{ ca_client_ca_dir }}" + owner: "{{ ca_client_ca_dir_owner }}" + group: "{{ ca_client_ca_dir_group }}" + mode: "{{ ca_client_ca_dir_mode }}" + state: directory -- name: Check if client certificate has to be renewed - ansible.builtin.import_tasks: renew.yml - vars: - crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" - when: ca_renew | bool - -- name: (Re)sign CSR with CA key - community.crypto.x509_certificate: - path: "{{ ca_ca_dir }}/{{ inventory_hostname }}.crt" - csr_path: "{{ ca_ca_dir }}/{{ inventory_hostname }}.csr" - ownca_path: "{{ ca_ca_dir }}/ca.crt" - ownca_privatekey_path: "{{ ca_ca_dir }}/ca.key" - ownca_privatekey_passphrase: "{{ ca_ca_password }}" - ownca_not_after: "{{ ca_valid_time }}" - provider: ownca - force: "{{ not crt_info.valid_at.check_period | default(omit) or - hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" - backup: true - delegate_to: "{{ ca_ca_host }}" + - name: Create key + community.crypto.openssl_privatekey: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + passphrase: "{{ ca_keypassphrase | default(omit, true) }}" + cipher: "{{ ca_openssl_cipher | default(omit, true) }}" + owner: "{{ ca_client_ca_dir_owner }}" + group: "{{ ca_client_ca_dir_group }}" + mode: "{{ ca_client_ca_dir_mode }}" + type: "{{ ca_client_key_algorithm }}" + register: key -- name: Fetch certificate - ansible.builtin.fetch: - src: "{{ ca_ca_dir }}/{{ inventory_hostname }}.crt" - dest: "{{ ca_localdir }}/{{ inventory_hostname }}.crt" - flat: true - delegate_to: "{{ ca_ca_host }}" + - name: Create CSR + community.crypto.openssl_csr: + path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" + privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" + country_name: "{{ ca_country | default(omit, true) }}" + organization_name: "{{ ca_organization | default(omit, true) }}" + common_name: "{{ ca_common_name }}" + subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" + extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" -- name: Push certificate to client - ansible.builtin.copy: - src: "{{ ca_localdir }}/{{ inventory_hostname }}.crt" - dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" - notify: "Ansible-role-ca : on certificate change" - -- name: Fetch CA certificate - ansible.builtin.fetch: - src: "{{ ca_ca_dir }}/ca.crt" - dest: "{{ ca_localdir }}/ca.crt" - flat: true - delegate_to: "{{ ca_ca_host }}" - run_once: true + - name: Pull CSR + ansible.builtin.fetch: + src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" + dest: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" + flat: true + + - name: Push CSR to CA host + ansible.builtin.copy: + src: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" + dest: "{{ ca_ca_dir }}/{{ inventory_hostname }}.csr" + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" + mode: 0600 + delegate_to: "{{ ca_ca_host }}" -- name: Push CA certificate to client - ansible.builtin.copy: - src: "{{ ca_localdir }}/ca.crt" - dest: "{{ ca_client_ca_dir }}/ca.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + - name: Check if client certificate has to be renewed + ansible.builtin.import_tasks: renew.yml + vars: + crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" + when: ca_renew | bool + + - name: (Re)sign CSR with CA key + community.crypto.x509_certificate: + path: "{{ ca_ca_dir }}/{{ inventory_hostname }}.crt" + csr_path: "{{ ca_ca_dir }}/{{ inventory_hostname }}.csr" + ownca_path: "{{ ca_ca_dir }}/ca.crt" + ownca_privatekey_path: "{{ ca_ca_dir }}/ca.key" + ownca_privatekey_passphrase: "{{ ca_ca_password }}" + ownca_not_after: "{{ ca_valid_time }}" + provider: ownca + force: "{{ not crt_info.valid_at.check_period | default(omit) or + hostvars[ca_ca_host]['ca_ca_renewed'] | default(omit) }}" + backup: true + delegate_to: "{{ ca_ca_host }}" + + - name: Fetch certificate + ansible.builtin.fetch: + src: "{{ ca_ca_dir }}/{{ inventory_hostname }}.crt" + dest: "{{ ca_localdir }}/{{ inventory_hostname }}.crt" + flat: true + delegate_to: "{{ ca_ca_host }}" + + - name: Push certificate to client + ansible.builtin.copy: + src: "{{ ca_localdir }}/{{ inventory_hostname }}.crt" + dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" + owner: "{{ ca_client_ca_dir_owner }}" + group: "{{ ca_client_ca_dir_group }}" + mode: "{{ ca_client_ca_dir_mode }}" + notify: "Ansible-role-ca : on certificate change" + + - name: Fetch CA certificate + ansible.builtin.fetch: + src: "{{ ca_ca_dir }}/ca.crt" + dest: "{{ ca_localdir }}/ca.crt" + flat: true + delegate_to: "{{ ca_ca_host }}" + run_once: true + + - name: Push CA certificate to client + ansible.builtin.copy: + src: "{{ ca_localdir }}/ca.crt" + dest: "{{ ca_client_ca_dir }}/ca.crt" + owner: "{{ ca_client_ca_dir_owner }}" + group: "{{ ca_client_ca_dir_group }}" + mode: "{{ ca_client_ca_dir_mode }}" ### server certificate ### From 678003b206bf23665f60605f1d0e5c069bb925bc Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 11:37:41 +0300 Subject: [PATCH 17/30] Don't generate server certificate by default --- defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/defaults/main.yml b/defaults/main.yml index 7c785f5..eea6d15 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -15,7 +15,7 @@ ca_ca_signing_key_algorithm: RSA ca_ca_signing_key_params: "" ca_client_cert: "{{ ca_ca_host != inventory_hostname }}" -ca_server_cert: true +ca_server_cert: false ca_logstash: false ca_etcd: false From 70d3605d7a838ddcc153e3ca5b223f9e0adbc443 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 11:48:20 +0300 Subject: [PATCH 18/30] Rename: ca_client_ca_* -> ca_certs_* --- README.md | 10 +- defaults/main.yml | 8 +- molecule/ca-renew/converge.yml | 4 +- molecule/ca-renew/prepare.yml | 4 +- molecule/ca-renew/tasks/verify_tasks.yml | 38 +++---- molecule/ca-renew/verify.yml | 4 +- molecule/default/converge.yml | 2 +- molecule/default/tasks/verify_tasks.yml | 28 +++--- molecule/default/verify.yml | 4 +- molecule/renew/converge.yml | 4 +- molecule/renew/prepare.yml | 4 +- molecule/renew/tasks/verify_tasks.yml | 38 +++---- molecule/renew/verify.yml | 4 +- tasks/main.yml | 122 +++++++++++------------ tasks/openssl_detect.yml | 32 ------ 15 files changed, 135 insertions(+), 171 deletions(-) delete mode 100644 tasks/openssl_detect.yml diff --git a/README.md b/README.md index f5871aa..eae448c 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,12 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) * `ca_etcd`: Create additional etcd compatible certificates. Requires `ca_etcd_group` to be defined. (default: `false`) * `ca_etcd_group`: Needs to be set to the group name of etcd nodes and will add the default IPv4 address of each node to the certificates. 127.0.0.1 will also be added by the role to the SAN for loopback purposes.(default: `undefined`) -* `ca_keypassphrase`: Password for the client key, default not defined +* `ca_keypassphrase`: Password for the leaf certificate key, default not defined * `ca_openssl_cipher`: Cipher to use for key creation, default not defined -* `ca_client_ca_dir`: Directory to place CA and certificates on the clients (default: `/opt/ca`) -* `ca_client_ca_dir_owner`: User to own the certificate directory on the clients (default: `root`) -* `ca_client_ca_dir_group`: Group to own the certificate directory on the clients (default: `root`) -* `ca_client_ca_dir_mode`: Permissions of the certificate directory on the clients (default: `0700`) +* `ca_certs_dir`: Directory to place CA and certificates on the clients (default: `/opt/ca`) +* `ca_certs_dir_owner`: User to own the certificate directory on the clients (default: `root`) +* `ca_certs_dir_group`: Group to own the certificate directory on the clients (default: `root`) +* `ca_certs_dir_mode`: Permissions of the certificate directory on the clients (default: `0700`) * `ca_client_key_algorithm`: Client key generation algorithm (default: `{{ ca_ca_signing_key_algorithm }}`) * `ca_renew`: Renew certificates if they expire within `ca_check_valid_time` timeframe (default: `false`) * `ca_valid_time`: Valid time of new created certificates (default: `+365d`) diff --git a/defaults/main.yml b/defaults/main.yml index eea6d15..562a7de 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -3,10 +3,10 @@ ca_manage_openssl: true ca_ca_dir: /opt/ca ca_ca_dir_owner: root ca_ca_dir_group: root -ca_client_ca_dir: /opt/ca -ca_client_ca_dir_owner: root -ca_client_ca_dir_group: root -ca_client_ca_dir_mode: 0700 +ca_certs_dir: /opt/ca +ca_certs_dir_owner: root +ca_certs_dir_group: root +ca_certs_dir_mode: 0700 ca_client_key_algorithm: "{{ ca_ca_signing_key_algorithm }}" ca_localdir: /tmp/ca ca_local_become: false diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 33e736e..14a1ea0 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -9,7 +9,7 @@ ca_ca_host: ca_default ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs ca_client_cert: true ca_server_cert: true ca_logstash: true @@ -24,7 +24,7 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 handlers: diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index 8ed7c15..9a873bb 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -5,7 +5,7 @@ ca_ca_host: ca_default ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs ca_client_cert: true ca_server_cert: true ca_logstash: true @@ -43,7 +43,7 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 handlers: diff --git a/molecule/ca-renew/tasks/verify_tasks.yml b/molecule/ca-renew/tasks/verify_tasks.yml index 53fe461..9ee7b56 100644 --- a/molecule/ca-renew/tasks/verify_tasks.yml +++ b/molecule/ca-renew/tasks/verify_tasks.yml @@ -2,21 +2,21 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}.crt changed_when: false - name: Verify signature on server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt changed_when: false - name: Check if instance key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing @@ -27,7 +27,7 @@ - name: Check if Logstash key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing @@ -40,24 +40,24 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Register SAN of etcd peer certificate @@ -65,7 +65,7 @@ openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt -certopt " no_subject, no_header, @@ -85,7 +85,7 @@ openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt -certopt " no_subject, no_header, @@ -122,7 +122,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}.crt changed_when: false - name: Check if notAfter of server certificate is within valid_days={{ valid_days }} @@ -130,7 +130,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt changed_when: false - name: Check if notAfter of etcd certificate is within valid_days={{ valid_days }} @@ -138,7 +138,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt changed_when: false - name: Check if notAfter of etcd server certificate is within valid_days={{ valid_days }} @@ -146,7 +146,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Check if notAfter of CA certificate is within valid_days={{ valid_days }} @@ -154,7 +154,7 @@ openssl x509 -noout -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/ca.crt + -in {{ ca_certs_dir }}/ca.crt changed_when: false - name: Register if on certificate change handler has run for initial client certificate diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index a5dc2ad..5a7881e 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -14,11 +14,11 @@ ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs - name: Verify Ed25519 ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index fd4637e..1685369 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -19,5 +19,5 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_certs_dir: /opt/ca-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/default/tasks/verify_tasks.yml b/molecule/default/tasks/verify_tasks.yml index 230fa27..2129bb1 100644 --- a/molecule/default/tasks/verify_tasks.yml +++ b/molecule/default/tasks/verify_tasks.yml @@ -2,21 +2,21 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}.crt changed_when: false - name: Verify signature on server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt changed_when: false - name: Check if instance key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing @@ -27,7 +27,7 @@ - name: Check if Logstash key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing @@ -40,31 +40,31 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Register SAN of etcd peer certificate ansible.builtin.command: > openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt -certopt " no_subject, no_header, @@ -84,7 +84,7 @@ openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt -certopt " no_subject, no_header, diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 3d58492..b29b178 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -10,11 +10,11 @@ ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/ca + ca_certs_dir: /opt/ca - name: Verify Ed25519 ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/ca-ed25519 + ca_certs_dir: /opt/ca-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index fed9740..1f67fab 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -9,7 +9,7 @@ ca_ca_host: ca_default ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs ca_client_cert: true ca_server_cert: true ca_logstash: true @@ -25,7 +25,7 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 handlers: diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 1ccfa23..1ef9b1b 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -5,7 +5,7 @@ ca_ca_host: ca_default ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs ca_client_cert: true ca_server_cert: true ca_logstash: true @@ -41,7 +41,7 @@ vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 handlers: diff --git a/molecule/renew/tasks/verify_tasks.yml b/molecule/renew/tasks/verify_tasks.yml index effe927..7c90e59 100644 --- a/molecule/renew/tasks/verify_tasks.yml +++ b/molecule/renew/tasks/verify_tasks.yml @@ -2,21 +2,21 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}.crt changed_when: false - name: Verify signature on server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt changed_when: false - name: Check if instance key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" register: instance_key_stat - name: Fail if instance key is missing @@ -27,7 +27,7 @@ - name: Check if Logstash key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" register: logstash_key_stat - name: Fail if Logstash key is missing @@ -40,24 +40,24 @@ ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Verify signature on etcd server certificate ansible.builtin.command: > openssl verify -verbose - -CAfile {{ ca_client_ca_dir }}/ca.crt - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -CAfile {{ ca_certs_dir }}/ca.crt + {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Register SAN of etcd peer certificate @@ -65,7 +65,7 @@ openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt -certopt " no_subject, no_header, @@ -85,7 +85,7 @@ openssl x509 -text -noout - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt -certopt " no_subject, no_header, @@ -121,7 +121,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}.crt changed_when: false - name: Check if notAfter of server certificate is within valid_days={{ valid_days }} @@ -129,7 +129,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt changed_when: false - name: Check if notAfter of etcd certificate is within valid_days={{ valid_days }} @@ -137,7 +137,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt changed_when: false - name: Check if notAfter of etcd server certificate is within valid_days={{ valid_days }} @@ -145,7 +145,7 @@ openssl x509 -noout -checkend {{ valid_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt changed_when: false - name: Check if notAfter of CA certificate is valid within valid_days={{ ca_ca_days }} @@ -153,7 +153,7 @@ openssl x509 -noout -checkend {{ ca_ca_days * 24 * 60 * 60 - max_test_duration_seconds }} - -in {{ ca_client_ca_dir }}/ca.crt + -in {{ ca_certs_dir }}/ca.crt changed_when: false - name: Register if on certificate change handler has run for initial client certificate diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index d756412..2089c2c 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -14,11 +14,11 @@ ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_dir: /opt/ca - ca_client_ca_dir: /opt/certs + ca_certs_dir: /opt/certs - name: Verify Ed25519 ansible.builtin.include_tasks: tasks/verify_tasks.yml vars: ca_ca_signing_key_algorithm: Ed25519 ca_ca_dir: /opt/ca-ed25519 - ca_client_ca_dir: /opt/certs-ed25519 + ca_certs_dir: /opt/certs-ed25519 ca_localdir: /tmp/ca-ed25519 diff --git a/tasks/main.yml b/tasks/main.yml index 87327b4..31d824a 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -23,41 +23,38 @@ name: openssl when: ca_manage_openssl | bool -- name: Check OpenSSL version - ansible.builtin.import_tasks: openssl_detect.yml - - name: Set up ca ansible.builtin.import_tasks: ca.yml when: ca_ca_host == inventory_hostname -### client certificate ### +### certificate ### -- name: Create client certificate +- name: Create certificate when: ca_client_cert | bool block: - - name: Ensure CA directory exists on clients + - name: Ensure CA cert directory exists ansible.builtin.file: - path: "{{ ca_client_ca_dir }}" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + path: "{{ ca_certs_dir }}" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" state: directory - name: Create key community.crypto.openssl_privatekey: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" passphrase: "{{ ca_keypassphrase | default(omit, true) }}" cipher: "{{ ca_openssl_cipher | default(omit, true) }}" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" type: "{{ ca_client_key_algorithm }}" register: key - name: Create CSR community.crypto.openssl_csr: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" - privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.csr" + privatekey_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" @@ -67,7 +64,7 @@ - name: Pull CSR ansible.builtin.fetch: - src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.csr" + src: "{{ ca_certs_dir }}/{{ inventory_hostname }}.csr" dest: "{{ ca_localdir }}/{{ inventory_hostname }}.csr" flat: true @@ -83,7 +80,7 @@ - name: Check if client certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" + crt_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.crt" when: ca_renew | bool - name: (Re)sign CSR with CA key @@ -110,10 +107,10 @@ - name: Push certificate to client ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}.crt" - dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}.crt" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" notify: "Ansible-role-ca : on certificate change" - name: Fetch CA certificate @@ -124,13 +121,13 @@ delegate_to: "{{ ca_ca_host }}" run_once: true - - name: Push CA certificate to client + - name: Push CA certificate ansible.builtin.copy: src: "{{ ca_localdir }}/ca.crt" - dest: "{{ ca_client_ca_dir }}/ca.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + dest: "{{ ca_certs_dir }}/ca.crt" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" ### server certificate ### @@ -139,8 +136,8 @@ block: - name: Create server CSR community.crypto.openssl_csr: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.csr" - privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-server.csr" + privatekey_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" privatekey_passphrase: "{{ ca_keypassphrase | default(omit, true) }}" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" @@ -150,7 +147,7 @@ - name: Pull server CSR ansible.builtin.fetch: - src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.csr" + src: "{{ ca_certs_dir }}/{{ inventory_hostname }}-server.csr" dest: "{{ ca_localdir }}/{{ inventory_hostname }}-server.csr" flat: true @@ -166,7 +163,7 @@ - name: Check if server certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt" + crt_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt" when: ca_renew | bool - name: (Re)sign server CSR with CA key @@ -193,10 +190,10 @@ - name: Push server certificate to client ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-server.crt" - dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-server.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" notify: "Ansible-role-ca : on server certificate change" - name: Handle Logstash compatible key @@ -204,14 +201,14 @@ block: - name: Check if Logstash key is present ansible.builtin.stat: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" register: key_stat - name: Move old Logstash key if common key was replaced ansible.builtin.command: > mv - {{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key - {{ ca_client_ca_dir }}/\ + {{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key + {{ ca_certs_dir }}/\ {{ inventory_hostname }}-pkcs8.key.\ {{ ansible_date_time.iso8601 }} changed_when: false @@ -222,22 +219,21 @@ - name: Create Logstash compatible key ansible.builtin.command: > openssl pkcs8 - -inform PEM - -outform PEM - -nocrypt + -in {{ ca_certs_dir }}/{{ inventory_hostname }}.key -topk8 -in {{ ca_client_ca_dir }}/{{ inventory_hostname }}.key -passin pass:{{ ca_keypassphrase | default(omit, true) }} {% if ca_ls7_workaround | bool %}-v1 {{ ca_ls7_workaround_cipher }}{% endif %} - -out {{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key + -out {{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key + -passout pass:{{ ca_keypassphrase | default(omit, true) }} args: - creates: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" + creates: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" - name: Set permissions on Logstash key ansible.builtin.file: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-pkcs8.key" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-pkcs8.key" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" mode: 0600 ### etcd peer certificate ### @@ -257,8 +253,8 @@ - name: Create CSR community.crypto.openssl_csr: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.csr" - privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.csr" + privatekey_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" @@ -267,7 +263,7 @@ - name: Pull CSR ansible.builtin.fetch: - src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.csr" + src: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.csr" dest: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd.csr" flat: true @@ -283,7 +279,7 @@ - name: Check if etcd peer certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt" + crt_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt" when: ca_renew | bool - name: (Re)sign CSR with CA key @@ -307,13 +303,13 @@ flat: true delegate_to: "{{ ca_ca_host }}" - - name: Push certificate to client + - name: Push certificate to etcd peer ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd.crt" - dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" notify: "Ansible-role-ca : on etcd certificate change" ### etcd server certificate ### @@ -323,8 +319,8 @@ block: - name: Create CSR community.crypto.openssl_csr: - path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.csr" - privatekey_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}.key" + path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.csr" + privatekey_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}.key" country_name: "{{ ca_country | default(omit, true) }}" organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" @@ -333,7 +329,7 @@ - name: Pull CSR ansible.builtin.fetch: - src: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.csr" + src: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.csr" dest: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd-server.csr" flat: true @@ -349,7 +345,7 @@ - name: Check if etcd server certificate has to be renewed ansible.builtin.import_tasks: renew.yml vars: - crt_path: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt" + crt_path: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt" when: ca_renew | bool - name: (Re)sign CSR with CA key @@ -376,8 +372,8 @@ - name: Push certificate to client ansible.builtin.copy: src: "{{ ca_localdir }}/{{ inventory_hostname }}-etcd-server.crt" - dest: "{{ ca_client_ca_dir }}/{{ inventory_hostname }}-etcd-server.crt" - owner: "{{ ca_client_ca_dir_owner }}" - group: "{{ ca_client_ca_dir_group }}" - mode: "{{ ca_client_ca_dir_mode }}" + dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: "{{ ca_certs_dir_mode }}" notify: "Ansible-role-ca : on etcd server certificate change" diff --git a/tasks/openssl_detect.yml b/tasks/openssl_detect.yml deleted file mode 100644 index a13794a..0000000 --- a/tasks/openssl_detect.yml +++ /dev/null @@ -1,32 +0,0 @@ ---- - -### -# The following code checks for the OpenSSL version being used -# When signing a CA key, OpenSSL 3 needs paramaters older versions don't -# understand. -# The `version` filter is unkown to Jinja2 so we need a fact that helps -# with changing templates accordingly - -- name: Get OpenSSL version - ansible.builtin.command: openssl version - register: _ca_openssl_version_raw - changed_when: false - -- name: Extract OpenSSL version string - ansible.builtin.set_fact: - _ca_openssl_version: "{{ _ca_openssl_version_raw.stdout.split(' ')[1] }}" - -- name: Set OpenSSL extension parameters (OpenSSL ≥ 3.0) - ansible.builtin.set_fact: - _ca_openssl_ca_ext_opts: "-extensions v3_ca" - when: _ca_openssl_version is version('3.0', '>=') - -- name: Set OpenSSL extension parameters (OpenSSL < 3.0) - ansible.builtin.set_fact: - _ca_openssl_ca_ext_opts: "" - when: _ca_openssl_version is version('3.0', '<') - -- name: Set Variable for Jinja2 templates - ansible.builtin.set_fact: - _ca_ca_openssl_version_3: true - when: _ca_openssl_version is version('3.0', '>=') From 30c06110b12dacfcf9a018ca4e1ad2c2166ca175 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 11:53:52 +0300 Subject: [PATCH 19/30] Rename: ca_client_key_algorithm -> ca_key_algorithm --- README.md | 12 ++++++------ defaults/main.yml | 2 +- tasks/main.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index eae448c..642ea8f 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,11 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_etcd_group`: Needs to be set to the group name of etcd nodes and will add the default IPv4 address of each node to the certificates. 127.0.0.1 will also be added by the role to the SAN for loopback purposes.(default: `undefined`) * `ca_keypassphrase`: Password for the leaf certificate key, default not defined * `ca_openssl_cipher`: Cipher to use for key creation, default not defined -* `ca_certs_dir`: Directory to place CA and certificates on the clients (default: `/opt/ca`) -* `ca_certs_dir_owner`: User to own the certificate directory on the clients (default: `root`) -* `ca_certs_dir_group`: Group to own the certificate directory on the clients (default: `root`) -* `ca_certs_dir_mode`: Permissions of the certificate directory on the clients (default: `0700`) -* `ca_client_key_algorithm`: Client key generation algorithm (default: `{{ ca_ca_signing_key_algorithm }}`) +* `ca_certs_dir`: Directory to place key, CA and leaf certificates on the hosts (default: `/opt/ca`) +* `ca_certs_dir_owner`: User to own the certificate directory on the hosts (default: `root`) +* `ca_certs_dir_group`: Group to own the certificate directory on the hosts (default: `root`) +* `ca_certs_dir_mode`: Permissions of the certificate directory on the hosts (default: `0700`) +* `ca_key_algorithm`: End-user key generation algorithm (default: `{{ ca_ca_signing_key_algorithm }}`) * `ca_renew`: Renew certificates if they expire within `ca_check_valid_time` timeframe (default: `false`) * `ca_valid_time`: Valid time of new created certificates (default: `+365d`) * `ca_check_valid_time`: Timeframe to check if certificates will expire (default: `+2w`) @@ -79,7 +79,7 @@ It's possible to register handlers to run actions on certificate change. For exa The following handler names are available for registration: -* `Ansible-role-ca : on certificate change`: runs on client certificate change +* `Ansible-role-ca : on certificate change`: runs on certificate change * `Ansible-role-ca : on server certificate change`: runs on server certificate change * `Ansible-role-ca : on etcd certificate change`: runs on etcd certificate change * `Ansible-role-ca : on etcd server certificate change`: runs on etcd server certificate change diff --git a/defaults/main.yml b/defaults/main.yml index 562a7de..e40a7da 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -7,7 +7,7 @@ ca_certs_dir: /opt/ca ca_certs_dir_owner: root ca_certs_dir_group: root ca_certs_dir_mode: 0700 -ca_client_key_algorithm: "{{ ca_ca_signing_key_algorithm }}" +ca_key_algorithm: "{{ ca_ca_signing_key_algorithm }}" ca_localdir: /tmp/ca ca_local_become: false ca_ca_host: localhost diff --git a/tasks/main.yml b/tasks/main.yml index 31d824a..5030a72 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -48,7 +48,7 @@ owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" mode: "{{ ca_certs_dir_mode }}" - type: "{{ ca_client_key_algorithm }}" + type: "{{ ca_key_algorithm }}" register: key - name: Create CSR From 9b5e9b0c10f6b7b49d8f8ddc6ef33f94fabda1c8 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 11:57:18 +0300 Subject: [PATCH 20/30] Rename ca_client_cert -> ca_cert --- README.md | 3 ++- defaults/main.yml | 2 +- molecule/ca-renew/converge.yml | 2 +- molecule/ca-renew/prepare.yml | 2 +- molecule/ca-renew/verify.yml | 2 +- molecule/default/converge.yml | 2 +- molecule/default/verify.yml | 2 +- molecule/renew/converge.yml | 2 +- molecule/renew/prepare.yml | 2 +- molecule/renew/verify.yml | 2 +- tasks/main.yml | 2 +- 11 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 642ea8f..e55fa1c 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_altname_3`: Third default alt name (default: `{{ inventory_hostname }}`). Omitted when set to `null`. * `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) * `ca_ca_keylength`: CA keylength (default: `2048`) -* `ca_server_cert`: Create server certificate as well (default: `true`) +* `ca_cert`: Create certificate (default skips CA host: `{{ inventory_hostname != ca_ca_host }}`) +* `ca_server_cert`: Create server certificate as well (default: `false`) * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) * `ca_etcd`: Create additional etcd compatible certificates. Requires `ca_etcd_group` to be defined. (default: `false`) * `ca_etcd_group`: Needs to be set to the group name of etcd nodes and will add the default IPv4 address of each node to the certificates. 127.0.0.1 will also be added by the role to the SAN for loopback purposes.(default: `undefined`) diff --git a/defaults/main.yml b/defaults/main.yml index e40a7da..3e5539d 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -14,7 +14,7 @@ ca_ca_host: localhost ca_ca_signing_key_algorithm: RSA ca_ca_signing_key_params: "" -ca_client_cert: "{{ ca_ca_host != inventory_hostname }}" +ca_cert: "{{ inventory_hostname != ca_ca_host }}" ca_server_cert: false ca_logstash: false ca_etcd: false diff --git a/molecule/ca-renew/converge.yml b/molecule/ca-renew/converge.yml index 14a1ea0..287edca 100644 --- a/molecule/ca-renew/converge.yml +++ b/molecule/ca-renew/converge.yml @@ -10,7 +10,7 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_certs_dir: /opt/certs - ca_client_cert: true + ca_cert: true ca_server_cert: true ca_logstash: true ca_etcd: true diff --git a/molecule/ca-renew/prepare.yml b/molecule/ca-renew/prepare.yml index 9a873bb..1c2ebbe 100644 --- a/molecule/ca-renew/prepare.yml +++ b/molecule/ca-renew/prepare.yml @@ -6,7 +6,7 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_certs_dir: /opt/certs - ca_client_cert: true + ca_cert: true ca_server_cert: true ca_logstash: true ca_etcd: true diff --git a/molecule/ca-renew/verify.yml b/molecule/ca-renew/verify.yml index 5a7881e..4149dbe 100644 --- a/molecule/ca-renew/verify.yml +++ b/molecule/ca-renew/verify.yml @@ -3,7 +3,7 @@ hosts: all vars: ca_ca_password: ChangeMe - ca_client_cert: true + ca_cert: true ca_server_cert: true valid_days: 365 ca_ca_days: 5 diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 1685369..b027e82 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -8,7 +8,7 @@ vars: ca_ca_host: ca_default ca_ca_password: ChangeMe - ca_client_cert: true + ca_cert: true ca_server_cert: true ca_logstash: true ca_etcd: true diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index b29b178..22b9996 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -3,7 +3,7 @@ hosts: all vars: ca_ca_password: ChangeMe - ca_client_cert: true + ca_cert: true ca_server_cert: true tasks: - name: Verify RSA diff --git a/molecule/renew/converge.yml b/molecule/renew/converge.yml index 1f67fab..2c81929 100644 --- a/molecule/renew/converge.yml +++ b/molecule/renew/converge.yml @@ -10,7 +10,7 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_certs_dir: /opt/certs - ca_client_cert: true + ca_cert: true ca_server_cert: true ca_logstash: true ca_etcd: true diff --git a/molecule/renew/prepare.yml b/molecule/renew/prepare.yml index 1ef9b1b..c6ecc7f 100644 --- a/molecule/renew/prepare.yml +++ b/molecule/renew/prepare.yml @@ -6,7 +6,7 @@ ca_ca_password: ChangeMe # for CA server separate CA and cert client directories allow to trigger notify action on client certificate change ca_certs_dir: /opt/certs - ca_client_cert: true + ca_cert: true ca_server_cert: true ca_logstash: true ca_etcd: true diff --git a/molecule/renew/verify.yml b/molecule/renew/verify.yml index 2089c2c..08e4b1b 100644 --- a/molecule/renew/verify.yml +++ b/molecule/renew/verify.yml @@ -5,7 +5,7 @@ ca_ca_password: ChangeMe ca_ca_days: 3650 valid_days: 5 - ca_client_cert: true + ca_cert: true ca_server_cert: true # allowed delta between certificate generation and notAfter validation max_test_duration_seconds: 900 diff --git a/tasks/main.yml b/tasks/main.yml index 5030a72..5f1243c 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -30,7 +30,7 @@ ### certificate ### - name: Create certificate - when: ca_client_cert | bool + when: ca_cert | bool block: - name: Ensure CA cert directory exists ansible.builtin.file: From ec02e7c883248d01c3d02b92360491a48f301a1a Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 12:01:32 +0300 Subject: [PATCH 21/30] Rename extended_key_usage -> ca_extended_key_usage --- tasks/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 5f1243c..edd1529 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -60,7 +60,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" + extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" - name: Pull CSR ansible.builtin.fetch: @@ -143,7 +143,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" + extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" - name: Pull server CSR ansible.builtin.fetch: @@ -259,7 +259,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" + extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" - name: Pull CSR ansible.builtin.fetch: @@ -325,7 +325,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ extended_key_usage | default(omit, true) }}" + extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" - name: Pull CSR ansible.builtin.fetch: From 6e33c57b7cb0c615fdfa84fd864b299c4a36d6e8 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 12:23:49 +0300 Subject: [PATCH 22/30] ca_extended_key_usage could be a list --- README.md | 3 ++- tasks/main.yml | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e55fa1c..fc3a5a0 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_altname_3`: Third default alt name (default: `{{ inventory_hostname }}`). Omitted when set to `null`. * `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) * `ca_ca_keylength`: CA keylength (default: `2048`) -* `ca_cert`: Create certificate (default skips CA host: `{{ inventory_hostname != ca_ca_host }}`) +* `ca_cert`: Create certificate (default skips CA host: `{{ inventory_hostname != ca_ca_host }}`). It's up to an operator to configure the certificate for TLS client or/and TLS server. +* `ca_extended_key_usage`: Configures certificate `extendedKeyUsage` field. For example, to support both client and server authentication pass `['clientAuth', 'serverAuth']` (default: omitted) * `ca_server_cert`: Create server certificate as well (default: `false`) * `ca_logstash`: Create Logstash compatible certificate as well. Needs `ca_server_cert` to be set. (default: `false`) * `ca_etcd`: Create additional etcd compatible certificates. Requires `ca_etcd_group` to be defined. (default: `false`) diff --git a/tasks/main.yml b/tasks/main.yml index edd1529..e0d9e74 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -60,7 +60,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" + extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: @@ -143,7 +143,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" + extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" - name: Pull server CSR ansible.builtin.fetch: @@ -259,7 +259,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" + extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: @@ -325,7 +325,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ ca_extended_key_usage| default(omit, true) }}" + extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: From 950bcc5ab0c06875315a645025c854a4c3410576 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 14:05:07 +0300 Subject: [PATCH 23/30] use ca_dir_owner/group for ca.conf --- tasks/ca.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/ca.yml b/tasks/ca.yml index 8c1e74c..fee8382 100644 --- a/tasks/ca.yml +++ b/tasks/ca.yml @@ -3,8 +3,8 @@ ansible.builtin.template: src: ca.conf.j2 dest: "{{ ca_ca_dir }}/ca.conf" - owner: root - group: root + owner: "{{ ca_ca_dir_owner }}" + group: "{{ ca_ca_dir_group }}" mode: 0600 - name: Check if CA key is already created From 1bf41754245ca1d06a53c846fc23fd1633625323 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 14:05:30 +0300 Subject: [PATCH 24/30] extended_key_usage is comma-separated --- tasks/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index e0d9e74..4c4ec41 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -60,7 +60,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" + extended_key_usage: "{{ (ca_extended_key_usage | join(',')) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: @@ -143,7 +143,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" - extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" + extended_key_usage: "{{ (ca_extended_key_usage | join(',')) if ca_extended_key_usage is defined else omit }}" - name: Pull server CSR ansible.builtin.fetch: @@ -259,7 +259,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" + extended_key_usage: "{{ (ca_extended_key_usage | join(',')) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: @@ -325,7 +325,7 @@ organization_name: "{{ ca_organization | default(omit, true) }}" common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_san_etcd | regex_replace(' ', '') }}" - extended_key_usage: "{{ (ca_extended_key_usage | to_json) if ca_extended_key_usage is defined else omit }}" + extended_key_usage: "{{ (ca_extended_key_usage | join(',')) if ca_extended_key_usage is defined else omit }}" - name: Pull CSR ansible.builtin.fetch: From 65154e3bd13c113516d921e422c7d5ba8ebd8024 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 14:14:45 +0300 Subject: [PATCH 25/30] split san using commas --- defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/defaults/main.yml b/defaults/main.yml index 3e5539d..85d511a 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -23,7 +23,7 @@ ca_common_name: "{{ inventory_hostname }}" ca_altname_1: "{{ ansible_hostname }}" ca_altname_2: "{{ ansible_fqdn }}" ca_altname_3: "{{ inventory_hostname }}" -ca_subject_alternative_name: "{{ [ca_altname_1, ca_altname_2, ca_altname_3] | reject('none') | map('regex_replace', '^(.*)$', 'DNS:\\1') | join('') }}" +ca_subject_alternative_name: "{{ [ca_altname_1, ca_altname_2, ca_altname_3] | reject('none') | map('regex_replace', '^(.*)$', 'DNS:\\1') | join(',') }}" ca_ca_keylength: 2048 ca_ls7_workaround: false From 1bdfbb2b571847be55aa2b0187c8c60fe76898c6 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 14:28:52 +0300 Subject: [PATCH 26/30] fix mode for certs and keys --- tasks/main.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 4c4ec41..515439f 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -47,7 +47,7 @@ cipher: "{{ ca_openssl_cipher | default(omit, true) }}" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: 0600 type: "{{ ca_key_algorithm }}" register: key @@ -61,6 +61,9 @@ common_name: "{{ ca_common_name }}" subject_alt_name: "{{ ca_subject_alternative_name | default(omit, true) }}" extended_key_usage: "{{ (ca_extended_key_usage | join(',')) if ca_extended_key_usage is defined else omit }}" + owner: "{{ ca_certs_dir_owner }}" + group: "{{ ca_certs_dir_group }}" + mode: 0600 - name: Pull CSR ansible.builtin.fetch: @@ -110,7 +113,7 @@ dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}.crt" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: 0600 notify: "Ansible-role-ca : on certificate change" - name: Fetch CA certificate @@ -127,7 +130,7 @@ dest: "{{ ca_certs_dir }}/ca.crt" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: "0600" ### server certificate ### @@ -193,7 +196,7 @@ dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-server.crt" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: "0600" notify: "Ansible-role-ca : on server certificate change" - name: Handle Logstash compatible key @@ -309,7 +312,7 @@ dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd.crt" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: "0600" notify: "Ansible-role-ca : on etcd certificate change" ### etcd server certificate ### @@ -375,5 +378,5 @@ dest: "{{ ca_certs_dir }}/{{ inventory_hostname }}-etcd-server.crt" owner: "{{ ca_certs_dir_owner }}" group: "{{ ca_certs_dir_group }}" - mode: "{{ ca_certs_dir_mode }}" + mode: "0600" notify: "Ansible-role-ca : on etcd server certificate change" From 9293a7e9264cf96efde15043d4d644cb231c1c3e Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Wed, 13 Mar 2024 16:13:46 +0300 Subject: [PATCH 27/30] always fetch CA certificate to the control host --- tasks/main.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tasks/main.yml b/tasks/main.yml index 515439f..36eb17e 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -27,6 +27,15 @@ ansible.builtin.import_tasks: ca.yml when: ca_ca_host == inventory_hostname + +- name: Fetch CA certificate + ansible.builtin.fetch: + src: "{{ ca_ca_dir }}/ca.crt" + dest: "{{ ca_localdir }}/ca.crt" + flat: true + delegate_to: "{{ ca_ca_host }}" + run_once: true + ### certificate ### - name: Create certificate @@ -116,14 +125,6 @@ mode: 0600 notify: "Ansible-role-ca : on certificate change" - - name: Fetch CA certificate - ansible.builtin.fetch: - src: "{{ ca_ca_dir }}/ca.crt" - dest: "{{ ca_localdir }}/ca.crt" - flat: true - delegate_to: "{{ ca_ca_host }}" - run_once: true - - name: Push CA certificate ansible.builtin.copy: src: "{{ ca_localdir }}/ca.crt" From 3c35b7c90530b8667f2114bb8909dd6713063114 Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Aug 2025 09:28:04 +0800 Subject: [PATCH 28/30] remove leftover for openssl version detection --- defaults/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 85d511a..a1afafa 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -33,5 +33,3 @@ ca_renew: false ca_ca_days: 3650 ca_valid_time: +365d ca_check_valid_time: +2w - -_ca_ca_openssl_version_3: false From e15894bb892cd0b97dffe5dc0365a598fcd002bb Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Aug 2025 04:47:20 +0300 Subject: [PATCH 29/30] Apply suggestions from code review Co-authored-by: Tobias Bauriedel --- .github/workflows/molecule.yml | 3 --- molecule/ca-renew/tasks/verify_tasks.yml | 12 ++++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 49c3114..884b1a2 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -38,9 +38,6 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install -r requirements-test.txt - - name: ${{ matrix.scenario }} ansible-lint - run: ansible-lint - - name: ${{ matrix.scenario }} molecule test run: | molecule test -s ${{ matrix.scenario }} diff --git a/molecule/ca-renew/tasks/verify_tasks.yml b/molecule/ca-renew/tasks/verify_tasks.yml index 9ee7b56..77e382f 100644 --- a/molecule/ca-renew/tasks/verify_tasks.yml +++ b/molecule/ca-renew/tasks/verify_tasks.yml @@ -157,22 +157,22 @@ -in {{ ca_certs_dir }}/ca.crt changed_when: false -- name: Register if on certificate change handler has run for initial client certificate +- name: Register if 'on certificate change' handler has run for initial client certificate ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.initial_certificate" register: initial_handler_file -- name: Fail if initial file of on certificate change handler does not exist +- name: Fail if initial file of 'on certificate change' handler does not exist ansible.builtin.fail: - msg: "Failed because on certificate change handler hasn't created initial file" + msg: "Failed because 'on certificate change' handler hasn't created initial file" when: not initial_handler_file.stat.exists -- name: Register if on certificate change handler has run for renewed client certificate +- name: Register if 'on certificate change' handler has run for renewed client certificate ansible.builtin.stat: path: "{{ ansible_env.HOME }}/{{ inventory_hostname }}.renewed_certificate" register: renewed_handler_file -- name: Fail if renewed file of on certificate change handler does not exist +- name: Fail if renewed file of 'on certificate change' handler does not exist ansible.builtin.fail: - msg: "Failed because on certificate change handler hasn't created renewed file" + msg: "Failed because 'on certificate change handler' hasn't created renewed file" when: not renewed_handler_file.stat.exists From bc8660c7c1926888b0a6f06005aebedd686b3a0d Mon Sep 17 00:00:00 2001 From: Rinat Shigapov Date: Sat, 9 Aug 2025 10:00:16 +0800 Subject: [PATCH 30/30] document CA key generation params variable --- README.md | 1 + tasks/ca.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index fc3a5a0..49d0e4a 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ You need to have the Python library `cryptography` in version `>1.2.3` available * `ca_altname_2`: Second default alt name (default: `{{ ansible_fqdn }}`). Omitted when set to `null`. * `ca_altname_3`: Third default alt name (default: `{{ inventory_hostname }}`). Omitted when set to `null`. * `ca_ca_signing_key_algorithm`: CA key generation algorithm (default: `RSA`) +* `ca_ca_signing_key_params`: CA key generation command options (empty by default) * `ca_ca_keylength`: CA keylength (default: `2048`) * `ca_cert`: Create certificate (default skips CA host: `{{ inventory_hostname != ca_ca_host }}`). It's up to an operator to configure the certificate for TLS client or/and TLS server. * `ca_extended_key_usage`: Configures certificate `extendedKeyUsage` field. For example, to support both client and server authentication pass `['clientAuth', 'serverAuth']` (default: omitted) diff --git a/tasks/ca.yml b/tasks/ca.yml index fee8382..e05acbc 100644 --- a/tasks/ca.yml +++ b/tasks/ca.yml @@ -49,6 +49,7 @@ -aes256 -passout stdin -out {{ ca_ca_dir }}/ca.key + {{ ca_ca_signing_key_params }} {{ ca_ca_keylength }} stdin: "{{ ca_ca_password }}" args: