summaryrefslogtreecommitdiff
path: root/roles/x509/uacme/cert
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2023-08-22 19:53:49 +0200
committerChristian Pointner <equinox@spreadspace.org>2023-08-22 19:53:49 +0200
commitfc5d0657bfcba53ace230ff2ada64b7fcf9b97a3 (patch)
tree350a8d401e0113bff7d78aee4d8547cddf06b8f7 /roles/x509/uacme/cert
parentfix docker for debian bookworm+ (diff)
parentsome more cleanup for acme specific variables (diff)
Merge branch 'topic/uacme'
Diffstat (limited to 'roles/x509/uacme/cert')
-rw-r--r--roles/x509/uacme/cert/finalize/defaults/main.yml3
-rw-r--r--roles/x509/uacme/cert/finalize/tasks/main.yml5
-rw-r--r--roles/x509/uacme/cert/meta/main.yml4
-rw-r--r--roles/x509/uacme/cert/prepare/defaults/main.yml15
-rw-r--r--roles/x509/uacme/cert/prepare/handlers/main.yml10
-rw-r--r--roles/x509/uacme/cert/prepare/tasks/main.yml112
-rw-r--r--roles/x509/uacme/cert/prepare/templates/updated.sh.j233
7 files changed, 182 insertions, 0 deletions
diff --git a/roles/x509/uacme/cert/finalize/defaults/main.yml b/roles/x509/uacme/cert/finalize/defaults/main.yml
new file mode 100644
index 00000000..611dc6fc
--- /dev/null
+++ b/roles/x509/uacme/cert/finalize/defaults/main.yml
@@ -0,0 +1,3 @@
+---
+uacme_cert_hostnames: "{{ x509_certificate_hostnames }}"
+uacme_cert_name: "{{ x509_certificate_name | default(uacme_cert_hostnames[0]) }}"
diff --git a/roles/x509/uacme/cert/finalize/tasks/main.yml b/roles/x509/uacme/cert/finalize/tasks/main.yml
new file mode 100644
index 00000000..6578c418
--- /dev/null
+++ b/roles/x509/uacme/cert/finalize/tasks/main.yml
@@ -0,0 +1,5 @@
+---
+- name: running uacme issue command
+ command: "/usr/local/bin/uacme-reconcile.sh '{{ uacme_cert_name }}'"
+ register: uacme_reconcile
+ changed_when: "'not updated.' not in uacme_reconcile.stdout"
diff --git a/roles/x509/uacme/cert/meta/main.yml b/roles/x509/uacme/cert/meta/main.yml
new file mode 100644
index 00000000..5106342c
--- /dev/null
+++ b/roles/x509/uacme/cert/meta/main.yml
@@ -0,0 +1,4 @@
+---
+dependencies:
+ - role: x509/uacme/cert/prepare
+ - role: x509/uacme/cert/finalize
diff --git a/roles/x509/uacme/cert/prepare/defaults/main.yml b/roles/x509/uacme/cert/prepare/defaults/main.yml
new file mode 100644
index 00000000..b15c1e44
--- /dev/null
+++ b/roles/x509/uacme/cert/prepare/defaults/main.yml
@@ -0,0 +1,15 @@
+---
+uacme_cert_hostnames: "{{ x509_certificate_hostnames }}"
+uacme_cert_name: "{{ x509_certificate_name | default(uacme_cert_hostnames[0]) }}"
+
+# uacme_cert_config:
+# key:
+# mode: "0640"
+# owner: root
+# group: www-data
+# type: RSA
+# size: 4096
+# cert:
+# mode: "0644"
+# owner: root
+# group: www-data
diff --git a/roles/x509/uacme/cert/prepare/handlers/main.yml b/roles/x509/uacme/cert/prepare/handlers/main.yml
new file mode 100644
index 00000000..330bcd11
--- /dev/null
+++ b/roles/x509/uacme/cert/prepare/handlers/main.yml
@@ -0,0 +1,10 @@
+---
+- name: reload systemd
+ systemd:
+ daemon_reload: yes
+
+- name: reload services for x509 certificates
+ loop: "{{ x509_certificate_reload_services | default([]) }}"
+ service:
+ name: "{{ item }}"
+ state: reloaded
diff --git a/roles/x509/uacme/cert/prepare/tasks/main.yml b/roles/x509/uacme/cert/prepare/tasks/main.yml
new file mode 100644
index 00000000..a83651b3
--- /dev/null
+++ b/roles/x509/uacme/cert/prepare/tasks/main.yml
@@ -0,0 +1,112 @@
+---
+- name: create directory for uacme-controlled certificate
+ file:
+ path: "/var/lib/uacme.d/{{ uacme_cert_name }}"
+ state: directory
+
+- name: generate key for uacme-controlled certificate
+ openssl_privatekey:
+ path: "/var/lib/uacme.d/{{ uacme_cert_name }}/key.pem"
+ mode: "{{ uacme_cert_config.key.mode | default('0600') }}"
+ owner: "{{ uacme_cert_config.key.owner | default(omit) }}"
+ group: "{{ uacme_cert_config.key.group | default(omit) }}"
+ type: "{{ uacme_cert_config.key.type | default(omit) }}"
+ size: "{{ uacme_cert_config.key.size | default(omit) }}"
+ notify: reload services for x509 certificates
+
+- name: generate csr for uacme-controlled certificate
+ community.crypto.openssl_csr:
+ path: "/var/lib/uacme.d/{{ uacme_cert_name }}/{{ uacme_cert_name }}.csr"
+ mode: "{{ uacme_cert_config.cert.mode | default('0644') }}"
+ owner: "{{ uacme_cert_config.cert.owner | default(omit) }}"
+ group: "{{ uacme_cert_config.cert.group | default(omit) }}"
+ privatekey_path: "/var/lib/uacme.d/{{ uacme_cert_name }}/key.pem"
+ common_name: "{{ uacme_cert_hostnames[0] }}"
+ subject_alt_name: "{{ ['DNS:'] | product(uacme_cert_hostnames) | map('join') | list }}"
+ subject_alt_name_critical: yes
+ use_common_name_for_san: no
+
+- name: test if uacme-controlled certificate already exists
+ stat:
+ path: "/var/lib/uacme.d/{{ uacme_cert_name }}/{{ uacme_cert_name }}-cert.pem"
+ register: uacme_cert_file
+
+- name: generate selfsigned interim certificate
+ when: not uacme_cert_file.stat.exists
+ block:
+ ### this is needed because strftime filter in ansible is exceptionally stupid
+ ### see: https://github.com/ansible/ansible/issues/39835
+ - name: get remote date-time 10s ago
+ command: date -d '10 seconds ago' -u '+%Y%m%d%H%M%SZ'
+ register: remote_datetime_10sago
+ changed_when: false
+
+ - name: get remote date-time now
+ command: date -u '+%Y%m%d%H%M%SZ'
+ register: remote_datetime_now
+ changed_when: false
+
+ - name: generate selfsigned interim certificate
+ community.crypto.x509_certificate:
+ path: "/var/lib/uacme.d/{{ uacme_cert_name }}/{{ uacme_cert_name }}-cert.pem"
+ mode: "{{ uacme_cert_config.cert.mode | default('0644') }}"
+ owner: "{{ uacme_cert_config.cert.owner | default(omit) }}"
+ group: "{{ uacme_cert_config.cert.group | default(omit) }}"
+ privatekey_path: "/var/lib/uacme.d/{{ uacme_cert_name }}/key.pem"
+ csr_path: "/var/lib/uacme.d/{{ uacme_cert_name }}/{{ uacme_cert_name }}.csr"
+ provider: selfsigned
+ ## make sure the certificate is not valid anymore to force uacme to create a new cert
+ selfsigned_not_before: "{{ remote_datetime_10sago.stdout }}"
+ selfsigned_not_after: "{{ remote_datetime_now.stdout }}"
+ return_content: yes
+ register: uacme_cert_selfsigned
+ notify: reload services for x509 certificates
+
+ - name: make sure cert-only file exists
+ copy:
+ content: "{{ uacme_cert_selfsigned.certificate }}"
+ dest: "/var/lib/uacme.d/{{ uacme_cert_name }}/crt.pem"
+ mode: "{{ uacme_cert_config.cert.mode | default('0644') }}"
+ owner: "{{ uacme_cert_config.cert.owner | default(omit) }}"
+ group: "{{ uacme_cert_config.cert.group | default(omit) }}"
+ notify: reload services for x509 certificates
+
+ - name: make sure the chain file exists
+ copy:
+ content: ""
+ dest: "/var/lib/uacme.d/{{ uacme_cert_name }}/chain.pem"
+ mode: "{{ uacme_cert_config.cert.mode | default('0644') }}"
+ owner: "{{ uacme_cert_config.cert.owner | default(omit) }}"
+ group: "{{ uacme_cert_config.cert.group | default(omit) }}"
+ notify: reload services for x509 certificates
+
+- name: export paths to certificate files
+ set_fact:
+ x509_certificate_path_key: "/var/lib/uacme.d/{{ uacme_cert_name }}/key.pem"
+ x509_certificate_path_cert: "/var/lib/uacme.d/{{ uacme_cert_name }}/crt.pem"
+ x509_certificate_path_chain: "/var/lib/uacme.d/{{ uacme_cert_name }}/chain.pem"
+ x509_certificate_path_fullchain: "/var/lib/uacme.d/{{ uacme_cert_name }}/{{ uacme_cert_name }}-cert.pem"
+
+- name: install script to be called when new certificate is generated
+ template:
+ src: updated.sh.j2
+ dest: "/var/lib/uacme.d/{{ uacme_cert_name }}/updated.sh"
+ mode: 0755
+
+- name: install systemd unit snippet
+ when: "x509_certificate_renewal is defined and 'install' in x509_certificate_renewal"
+ copy:
+ dest: "/etc/systemd/system/uacme-reconcile.service.d/{{ x509_certificate_name }}.conf"
+ content: |
+ [Service]
+ {% for path in (x509_certificate_renewal.install | map(attribute='dest') | map('dirname') | unique | list) %}
+ ReadWritePaths={{ path }}
+ {% endfor %}
+ notify: reload systemd
+
+- name: remove systemd unit snippet
+ when: "x509_certificate_renewal is undefined or 'install' not in x509_certificate_renewal"
+ file:
+ path: "/etc/systemd/system/uacme-reconcile.service.d/{{ x509_certificate_name }}.conf"
+ state: absent
+ notify: reload systemd
diff --git a/roles/x509/uacme/cert/prepare/templates/updated.sh.j2 b/roles/x509/uacme/cert/prepare/templates/updated.sh.j2
new file mode 100644
index 00000000..275ca189
--- /dev/null
+++ b/roles/x509/uacme/cert/prepare/templates/updated.sh.j2
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+BASE_D="/var/lib/uacme.d/{{ uacme_cert_name }}"
+
+# split fullchain and fix permissions
+awk '{if(length($0) > 0) print} /-----END CERTIFICATE-----/ { exit }' "$BASE_D/{{ uacme_cert_name }}-cert.pem" > "$BASE_D/crt.pem"
+awk '(show==1) {if(length($0) > 0) print} /-----END CERTIFICATE-----/ { show=1 }' "$BASE_D/{{ uacme_cert_name }}-cert.pem" > "$BASE_D/chain.pem"
+chmod "{{ uacme_cert_config.cert.mode | default('0644') }}" $BASE_D/{{ uacme_cert_name }}-cert.pem $BASE_D/crt.pem $BASE_D/chain.pem
+{% if uacme_cert_config.cert.owner is defined %}
+chown "{{ uacme_cert_config.cert.owner }}" $BASE_D/{{ uacme_cert_name }}-cert.pem $BASE_D/crt.pem $BASE_D/chain.pem
+{% endif %}
+{% if uacme_cert_config.cert.group is defined %}
+chgrp "{{ uacme_cert_config.cert.group }}" $BASE_D/{{ uacme_cert_name }}-cert.pem $BASE_D/crt.pem $BASE_D/chain.pem
+{% endif %}
+{% if x509_certificate_renewal is defined and 'install' in x509_certificate_renewal %}
+{% for file in x509_certificate_renewal.install %}
+
+install{% if 'mode' in file %} -m {{ file.mode }}{% endif %}{% if 'owner' in file %} -o {{ file.owner }}{% endif %}{% if 'owner' in file %} -g {{ file.group }}{% endif %} /dev/null "{{ file.dest }}.new"
+{% for src in file.src %}
+cat "{{ hostvars[inventory_hostname]['x509_certificate_path_' + src] }}" >> "{{ file.dest }}.new"
+mv "{{ file.dest }}.new" "{{ file.dest }}"
+{% endfor %}
+{% endfor %}
+{% endif %}
+
+## reload services
+{% for service in (x509_certificate_reload_services | default([])) %}
+systemctl reload "{{ service }}.service"
+{% endfor %}
+{% if x509_certificate_renewal is defined and 'reload' in x509_certificate_renewal %}
+
+{{ x509_certificate_renewal.reload | trim }}
+{% endif %}