From e56713301b19c67480d84b55dd513575b50cfd42 Mon Sep 17 00:00:00 2001 From: Jonas Gunz Date: Tue, 20 Sep 2022 18:11:00 +0200 Subject: ACME for signed_certificate --- roles/signed_certificate/README.md | 35 ++++++---- roles/signed_certificate/defaults/main.yml | 12 ++++ .../signed_certificate/playbook_create_account.yml | 28 ++++++++ roles/signed_certificate/tasks/letsencrypt.yml | 81 ++++++++++++++++++++++ roles/signed_certificate/tasks/main.yml | 27 ++------ roles/signed_certificate/tasks/selfsigned.yml | 25 +++++++ roles/signed_certificate/tasks/sign.yml | 31 --------- roles/signed_certificate/tasks/sign_selfsigned.yml | 31 +++++++++ 8 files changed, 204 insertions(+), 66 deletions(-) create mode 100644 roles/signed_certificate/playbook_create_account.yml create mode 100644 roles/signed_certificate/tasks/letsencrypt.yml create mode 100644 roles/signed_certificate/tasks/selfsigned.yml delete mode 100644 roles/signed_certificate/tasks/sign.yml create mode 100644 roles/signed_certificate/tasks/sign_selfsigned.yml (limited to 'roles') diff --git a/roles/signed_certificate/README.md b/roles/signed_certificate/README.md index b048295..b1fa10e 100644 --- a/roles/signed_certificate/README.md +++ b/roles/signed_certificate/README.md @@ -3,22 +3,31 @@ ## CA Settings ``` -signed_certificate: - issuer_cn: 'Tets CN' - renew_at: '+5d' - valid_for: '+30d' - privkey_path: '/tmp/ca.key' - privkey_passphrase: '1234' - cert_content: '{{ lookup('file', /tmp/cert.pem) }}' -``` - -## Certificate settings - -``` +--- cert_name: '{{ ansible_facts.fqdn }}' +common_name: '{{ ansible_facts.fqdn }}' key_path: '/etc/ssl/private/' cert_path: '/etc/ssl/certs/' alt_name: '{{ "DNS:" + ansible_facts.fqdn }}' owner: root -group: root +group: ssl-cert + +signed_certificate: + issuer_cn: '' + renew_at: '+5d' + valid_for: '+30d' + privkey_path: '/invalid' + privkey_passphrase: '' + cert_content: '' + +use_acme: false + +acme: + directory: https://acme-v01.api.letsencrypt.org/directory + renew_at: 10 + account_email: mail@example.com + account_key: 'INVALID' + gandi: + api_key: '' + domain: '' ``` diff --git a/roles/signed_certificate/defaults/main.yml b/roles/signed_certificate/defaults/main.yml index d0ee48e..a94a14e 100644 --- a/roles/signed_certificate/defaults/main.yml +++ b/roles/signed_certificate/defaults/main.yml @@ -1,5 +1,6 @@ --- cert_name: '{{ ansible_facts.fqdn }}' +common_name: '{{ ansible_facts.fqdn }}' key_path: '/etc/ssl/private/' cert_path: '/etc/ssl/certs/' alt_name: '{{ "DNS:" + ansible_facts.fqdn }}' @@ -13,3 +14,14 @@ signed_certificate: privkey_path: '/invalid' privkey_passphrase: '' cert_content: '' + +use_acme: false + +acme: + directory: https://acme-v01.api.letsencrypt.org/directory + renew_at: 10 + account_email: mail@example.com + account_key: 'INVALID' + gandi: + api_key: '' + domain: '' diff --git a/roles/signed_certificate/playbook_create_account.yml b/roles/signed_certificate/playbook_create_account.yml new file mode 100644 index 0000000..ac6c474 --- /dev/null +++ b/roles/signed_certificate/playbook_create_account.yml @@ -0,0 +1,28 @@ +--- +- name: Create ACME Account + hosts: localhost + vars: + directory: 'https://acme-staging-v02.api.letsencrypt.org/directory' + contact: + - 'mailto:mail@assdfkjsdhf.com' + tasks: + - name: Create private key + community.crypto.openssl_privatekey: + path: acme_account.key + return_content: True + size: 2048 + register: privkey + + - name: Create ACME Account + community.crypto.acme_account: + acme_directory: '{{ directory }}' + acme_version: 2 + account_key_content: '{{ privkey.privatekey }}' + contact: '{{ contact }}' + state: present + terms_agreed: True + register: account + + - name: Print account + debug: + var: account diff --git a/roles/signed_certificate/tasks/letsencrypt.yml b/roles/signed_certificate/tasks/letsencrypt.yml new file mode 100644 index 0000000..9d84bd3 --- /dev/null +++ b/roles/signed_certificate/tasks/letsencrypt.yml @@ -0,0 +1,81 @@ +--- +- name: Create CSR + community.crypto.openssl_csr_pipe: + privatekey_path: '{{ key_path }}/{{ cert_name }}.key' + common_name: '{{ common_name }}' + subject_alt_name: '{{ alt_name }}' + register: request + become: yes + +- name: Create a challenge using account key file. + community.crypto.acme_certificate: + account_key_content: '{{ acme.account_key }}' + modify_account: False + dest: '{{ cert_path }}/{{ cert_name }}.pem' + fullchain_dest: '{{ cert_path }}/{{ cert_name }}.fullchain.pem' + csr_content: '{{ request.csr }}' + challenge: dns-01 + acme_directory: '{{ acme.directory }}' + acme_version: 2 + remaining_days: '{{ acme.renew_at }}' + register: dns_challenge + +- name: Create DNS Challenge DNS Entry in LiveDNS + community.general.gandi_livedns: + domain: '{{ acme.gandi.domain }}' + record: '{{ item.key }}.' + type: TXT + ttl: 300 + values: "{{ item.value | map('regex_replace', '^(.*)$', '\"\\1\"' ) | list }}" + api_key: '{{ acme.gandi.api_key }}' + state: present + loop: "{{ dns_challenge.challenge_data_dns | dict2items }}" + when: dns_challenge is changed + +- name: Wait a bit + pause: + seconds: 20 + when: dns_challenge is changed + +- name: Validate the challenge and install certificates and chain + community.crypto.acme_certificate: + account_key_content: '{{ acme.account_key }}' + modify_account: False + csr_content: '{{ request.csr }}' + dest: '{{ cert_path }}/{{ cert_name }}.pem' + fullchain_dest: '{{ cert_path }}/{{ cert_name }}.fullchain.pem' + challenge: dns-01 + acme_directory: '{{ acme.directory }}' + acme_version: 2 + remaining_days: '{{ acme.renew_at }}' + data: '{{ dns_challenge }}' + register: dns_challenge + when: dns_challenge is changed + become: yes + +- name: Remove DNS Challenge DNS Entry in LiveDNS + community.general.gandi_livedns: + domain: '{{ acme.gandi.domain }}' + record: '{{ item.key }}.' + type: TXT + api_key: '{{ acme.gandi.api_key }}' + state: absent + loop: "{{ dns_challenge.challenge_data_dns | dict2items }}" + when: dns_challenge is changed + +# =========================== + +- name: Adjust file permissions + file: + path: '{{ item }}' + owner: '{{ owner }}' + group: '{{ group }}' + loop: + - '{{ cert_path }}/{{ cert_name }}.pem' + - '{{ cert_path }}/{{ cert_name }}.fullchain.pem' + become: yes + +- name: Set cert_changed flag + set_fact: + cert_changed: True + when: dns_challenge is changed diff --git a/roles/signed_certificate/tasks/main.yml b/roles/signed_certificate/tasks/main.yml index 4e214d2..4fb424d 100644 --- a/roles/signed_certificate/tasks/main.yml +++ b/roles/signed_certificate/tasks/main.yml @@ -35,27 +35,10 @@ become: yes when: not key_check.failed -- name: Read Existing Certificate - community.crypto.x509_certificate_info: - path: '{{ cert_path }}/{{ cert_name }}.pem' - valid_at: - point_1: '{{ signed_certificate.renew_at }}' - ignore_errors: yes - become: yes - register: existing_cert - -- name: Check Certificate - assert: - that: - - existing_cert.valid_at.point_1 - - not existing_cert.failed - - existing_cert.subject.commonName == ansible_facts.fqdn - - existing_cert.issuer.commonName == '{{ signed_certificate.issuer_cn }}' - success_msg: Certificate is valid - fail_msg: Certificate is not valid. creating a new one. - ignore_errors: yes - register: cert_assert +- name: Trigger Cert Generation + include_tasks: selfsigned.yml + when: use_acme == false - name: Trigger Cert Generation - include: sign.yml - when: cert_assert.failed + include_tasks: letsencrypt.yml + when: use_acme == true diff --git a/roles/signed_certificate/tasks/selfsigned.yml b/roles/signed_certificate/tasks/selfsigned.yml new file mode 100644 index 0000000..7b0957c --- /dev/null +++ b/roles/signed_certificate/tasks/selfsigned.yml @@ -0,0 +1,25 @@ +--- +- name: Read Existing Certificate + community.crypto.x509_certificate_info: + path: '{{ cert_path }}/{{ cert_name }}.pem' + valid_at: + point_1: '{{ signed_certificate.renew_at }}' + ignore_errors: yes + become: yes + register: existing_cert + +- name: Check Certificate + assert: + that: + - existing_cert.valid_at.point_1 + - not existing_cert.failed + - existing_cert.subject.commonName == common_name + - existing_cert.issuer.commonName == '{{ signed_certificate.issuer_cn }}' + success_msg: Certificate is valid + fail_msg: Certificate is not valid. creating a new one. + ignore_errors: yes + register: cert_assert + +- name: Trigger Cert Generation + include_tasks: sign_selfsigned.yml + when: cert_assert.failed diff --git a/roles/signed_certificate/tasks/sign.yml b/roles/signed_certificate/tasks/sign.yml deleted file mode 100644 index b99df32..0000000 --- a/roles/signed_certificate/tasks/sign.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- -- name: Create CSR - community.crypto.openssl_csr_pipe: - privatekey_path: '{{ key_path }}/{{ cert_name }}.key' - common_name: '{{ ansible_facts.fqdn }}' - subject_alt_name: '{{ alt_name }}' - register: request - become: yes - -- name: Sign OpenSSL Certificate - community.crypto.x509_certificate_pipe: - provider: ownca - ownca_privatekey_path: '{{ signed_certificate.privkey_path }}' - ownca_privatekey_passphrase: '{{ signed_certificate.privkey_passphrase }}' - ownca_content: '{{ signed_certificate.cert_content }}' - ownca_not_after: '{{ signed_certificate.valid_for }}' - csr_content: '{{ request.csr }}' - delegate_to: localhost - register: cert - -- name: Install Signed OpenSSL Certificate - copy: - dest: '{{ cert_path }}/{{ cert_name }}.pem' - content: '{{ cert.certificate }}' - owner: '{{ owner }}' - group: '{{ group }}' - become: yes - -- name: Set cert_changed flag - set_fact: - cert_changed: True diff --git a/roles/signed_certificate/tasks/sign_selfsigned.yml b/roles/signed_certificate/tasks/sign_selfsigned.yml new file mode 100644 index 0000000..fb610f6 --- /dev/null +++ b/roles/signed_certificate/tasks/sign_selfsigned.yml @@ -0,0 +1,31 @@ +--- +- name: Create CSR + community.crypto.openssl_csr_pipe: + privatekey_path: '{{ key_path }}/{{ cert_name }}.key' + common_name: '{{ common_name }}' + subject_alt_name: '{{ alt_name }}' + register: request + become: yes + +- name: Sign OpenSSL Certificate + community.crypto.x509_certificate_pipe: + provider: ownca + ownca_privatekey_path: '{{ signed_certificate.privkey_path }}' + ownca_privatekey_passphrase: '{{ signed_certificate.privkey_passphrase }}' + ownca_content: '{{ signed_certificate.cert_content }}' + ownca_not_after: '{{ signed_certificate.valid_for }}' + csr_content: '{{ request.csr }}' + delegate_to: localhost + register: cert + +- name: Install Signed OpenSSL Certificate + copy: + dest: '{{ cert_path }}/{{ cert_name }}.pem' + content: '{{ cert.certificate }}' + owner: '{{ owner }}' + group: '{{ group }}' + become: yes + +- name: Set cert_changed flag + set_fact: + cert_changed: True -- cgit v1.2.3