From f0f0122c59512deaac889b1818c204f488e7860d Mon Sep 17 00:00:00 2001 From: Yi-Ting Shih Date: Fri, 25 Sep 2026 21:34:34 +0800 Subject: [PATCH] feat: add rotation --- roles/create_ldap_cert/tasks/main.yml | 53 ++---------- roles/create_rootca/tasks/main.yml | 4 +- roles/create_www_cert/tasks/main.yml | 53 ++---------- roles/pki_cert/defaults/main.yml | 3 + roles/pki_cert/tasks/main.yml | 113 ++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 94 deletions(-) create mode 100644 roles/pki_cert/defaults/main.yml create mode 100644 roles/pki_cert/tasks/main.yml diff --git a/roles/create_ldap_cert/tasks/main.yml b/roles/create_ldap_cert/tasks/main.yml index ff008b2..d910e1f 100644 --- a/roles/create_ldap_cert/tasks/main.yml +++ b/roles/create_ldap_cert/tasks/main.yml @@ -1,51 +1,12 @@ --- -- name: Install ldap directory - ansible.builtin.file: - path: /etc/pki/ldap.konchin.com - state: directory - mode: '0700' - owner: root - group: root - -- name: Create private key for ldap - community.crypto.openssl_privatekey: - path: /etc/pki/ldap.konchin.com/cert.key - -- name: Create CSR for ldap - community.crypto.openssl_csr_pipe: - privatekey_path: /etc/pki/ldap.konchin.com/cert.key - subject_alt_name: +- name: Create/rotate ldap certificate + ansible.builtin.include_role: + name: pki_cert + vars: + pki_cert_name: ldap.konchin.com + pki_cert_dir: /etc/pki/ldap.konchin.com + pki_cert_sans: - 'DNS:ldap' - 'DNS:ldaps' - 'DNS:ldap.konchin.com' - 'DNS:ldaps.konchin.com' - register: csr - -- name: Sign with root ca - community.crypto.x509_certificate: - path: /etc/pki/ldap.konchin.com/cert.pem - csr_content: "{{ csr.csr }}" - provider: ownca - ownca_path: /etc/pki/konchin.com/rootca.pem - ownca_privatekey_path: /etc/pki/konchin.com/rootca.key - ownca_privatekey_passphrase: "{{ secret_ca_passphrase }}" - ownca_not_after: +365d # valid for one year - ownca_not_before: -1d # valid since yesterday - return_content: true - register: cert - -- name: Create full chain - block: - - name: Slurp root ca - ansible.builtin.slurp: - src: /etc/pki/konchin.com/rootca.pem - register: rootca - - name: Create full chain cert - ansible.builtin.copy: - content: | - {{ cert.certificate | trim }} - {{ rootca['content'] | b64decode | trim }} - dest: /etc/pki/ldap.konchin.com/full-chain.pem - mode: '0600' - owner: root - group: root diff --git a/roles/create_rootca/tasks/main.yml b/roles/create_rootca/tasks/main.yml index 54e0bf6..bcf3b83 100644 --- a/roles/create_rootca/tasks/main.yml +++ b/roles/create_rootca/tasks/main.yml @@ -33,12 +33,12 @@ key_usage: - keyCertSign key_usage_critical: true - register: ca_csr + register: create_rootca_ca_csr - name: Create cert for root ca community.crypto.x509_certificate: path: /etc/pki/konchin.com/rootca.pem - csr_content: "{{ ca_csr.csr }}" + csr_content: "{{ create_rootca_ca_csr.csr }}" privatekey_path: /etc/pki/konchin.com/rootca.key privatekey_passphrase: "{{ secret_ca_passphrase }}" provider: selfsigned diff --git a/roles/create_www_cert/tasks/main.yml b/roles/create_www_cert/tasks/main.yml index 08658ab..05c421c 100644 --- a/roles/create_www_cert/tasks/main.yml +++ b/roles/create_www_cert/tasks/main.yml @@ -1,49 +1,10 @@ --- -- name: Install www directory - ansible.builtin.file: - path: /etc/pki/www.konchin.com - state: directory - mode: '0700' - owner: root - group: root - -- name: Create private key for www - community.crypto.openssl_privatekey: - path: /etc/pki/www.konchin.com/cert.key - -- name: Create CSR for www - community.crypto.openssl_csr_pipe: - privatekey_path: /etc/pki/www.konchin.com/cert.key - subject_alt_name: +- name: Create/rotate www certificate + ansible.builtin.include_role: + name: pki_cert + vars: + pki_cert_name: www.konchin.com + pki_cert_dir: /etc/pki/www.konchin.com + pki_cert_sans: - 'DNS:www.konchin.com' - 'DNS:*.konchin.com' - register: csr - -- name: Sign with root ca - community.crypto.x509_certificate: - path: /etc/pki/www.konchin.com/cert.pem - csr_content: "{{ csr.csr }}" - provider: ownca - ownca_path: /etc/pki/konchin.com/rootca.pem - ownca_privatekey_path: /etc/pki/konchin.com/rootca.key - ownca_privatekey_passphrase: "{{ secret_ca_passphrase }}" - ownca_not_after: +365d # valid for one year - ownca_not_before: -1d # valid since yesterday - return_content: true - register: cert - -- name: Create full chain - block: - - name: Slurp root ca - ansible.builtin.slurp: - src: /etc/pki/konchin.com/rootca.pem - register: rootca - - name: Create full chain cert - ansible.builtin.copy: - content: | - {{ cert.certificate | trim }} - {{ rootca['content'] | b64decode | trim }} - dest: /etc/pki/www.konchin.com/full-chain.pem - mode: '0600' - owner: root - group: root diff --git a/roles/pki_cert/defaults/main.yml b/roles/pki_cert/defaults/main.yml new file mode 100644 index 0000000..6ae54dc --- /dev/null +++ b/roles/pki_cert/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# How long before its actual expiry a certificate should be rotated. +pki_cert_renew_before: +183d diff --git a/roles/pki_cert/tasks/main.yml b/roles/pki_cert/tasks/main.yml new file mode 100644 index 0000000..747d45b --- /dev/null +++ b/roles/pki_cert/tasks/main.yml @@ -0,0 +1,113 @@ +--- +- name: Install PKI directory for {{ pki_cert_name }} + ansible.builtin.file: + path: "{{ pki_cert_dir }}" + state: directory + mode: '0700' + owner: root + group: root + +- name: Check for existing certificate + ansible.builtin.stat: + path: "{{ pki_cert_dir }}/cert.pem" + register: pki_cert_existing + +- name: Inspect existing certificate expiry + community.crypto.x509_certificate_info: + path: "{{ pki_cert_dir }}/cert.pem" + valid_at: + still_valid_before_renewal_window: "{{ pki_cert_renew_before }}" + register: pki_cert_info + when: pki_cert_existing.stat.exists + +- name: Determine whether certificate needs rotation + ansible.builtin.set_fact: + pki_cert_needs_rotation: "{{ pki_cert_existing.stat.exists and not pki_cert_info.valid_at.still_valid_before_renewal_window }}" + +- name: Archive certificate nearing expiry + when: pki_cert_needs_rotation + block: + - name: Compute archive directory name + ansible.builtin.set_fact: + pki_cert_archive_dir: "{{ pki_cert_dir }}/{{ (pki_cert_info.not_before | to_datetime('%Y%m%d%H%M%SZ')).strftime('%Y-%m') }}" + + - name: Create archive directory + ansible.builtin.file: + path: "{{ pki_cert_archive_dir }}" + state: directory + mode: '0700' + owner: root + group: root + + - name: Check which certificate files exist + ansible.builtin.stat: + path: "{{ pki_cert_dir }}/{{ item }}" + register: pki_cert_files + loop: + - cert.key + - cert.pem + - full-chain.pem + loop_control: + label: "{{ item }}" + + - name: Copy existing certificate files into archive + ansible.builtin.copy: + src: "{{ pki_cert_dir }}/{{ item.item }}" + dest: "{{ pki_cert_archive_dir }}/{{ item.item }}" + remote_src: true + mode: preserve + owner: root + group: root + loop: "{{ pki_cert_files.results }}" + loop_control: + label: "{{ item.item }}" + when: item.stat.exists + + - name: Remove archived certificate files from live directory + ansible.builtin.file: + path: "{{ pki_cert_dir }}/{{ item.item }}" + state: absent + loop: "{{ pki_cert_files.results }}" + loop_control: + label: "{{ item.item }}" + when: item.stat.exists + +- name: Create private key for {{ pki_cert_name }} + community.crypto.openssl_privatekey: + path: "{{ pki_cert_dir }}/cert.key" + +- name: Create CSR for {{ pki_cert_name }} + community.crypto.openssl_csr_pipe: + privatekey_path: "{{ pki_cert_dir }}/cert.key" + subject_alt_name: "{{ pki_cert_sans }}" + register: pki_cert_csr + +- name: Sign with root ca + community.crypto.x509_certificate: + path: "{{ pki_cert_dir }}/cert.pem" + csr_content: "{{ pki_cert_csr.csr }}" + provider: ownca + ownca_path: /etc/pki/konchin.com/rootca.pem + ownca_privatekey_path: /etc/pki/konchin.com/rootca.key + ownca_privatekey_passphrase: "{{ secret_ca_passphrase }}" + ownca_not_after: +365d # valid for one year + ownca_not_before: -1d # valid since yesterday + return_content: true + register: pki_cert_signed + +- name: Create full chain + block: + - name: Slurp root ca + ansible.builtin.slurp: + src: /etc/pki/konchin.com/rootca.pem + register: pki_cert_rootca + + - name: Create full chain cert + ansible.builtin.copy: + content: | + {{ pki_cert_signed.certificate | trim }} + {{ pki_cert_rootca['content'] | b64decode | trim }} + dest: "{{ pki_cert_dir }}/full-chain.pem" + mode: '0600' + owner: root + group: root