From e4dcfc6a9807abcdbea5a8d48228dccb82408b54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20K=C3=A4b?= <alexander.kaeb@h-da.de>
Date: Wed, 28 Feb 2024 14:37:31 +0100
Subject: [PATCH] cleanup: remove distribution specific task files and strip
 dns renewal hook

Remove the specific task files for each distribution and replace them with
a dict in the defaults. Furthermore, remove the fixed deploy hook from
the DNS challenge mode and provide ways to extend the `lego` cmd.
---
 README.md                           | 25 +++++++++++++----
 defaults/main.yml                   | 25 +++++++++++++----
 tasks/CentOS-packages-webserver.yml | 10 -------
 tasks/CentOS-timer-webserver.yml    |  7 -----
 tasks/Debian-packages-webserver.yml |  7 -----
 tasks/Debian-timer-webserver.yml    |  7 -----
 tasks/Fedora-packages-webserver.yml |  6 ----
 tasks/Fedora-timer-webserver.yml    |  7 -----
 tasks/Ubuntu-packages-webserver.yml |  7 -----
 tasks/Ubuntu-timer-webserver.yml    |  7 -----
 tasks/application.yml               | 34 -----------------------
 tasks/dns-challenge.yml             | 43 +++++++++--------------------
 tasks/renewal-hook.yml              |  7 +++++
 tasks/webserver.yml                 | 33 ++++++++++++++++++----
 templates/dns-challenge.service.j2  |  3 +-
 templates/renew-hook.sh.j2          |  4 ---
 16 files changed, 88 insertions(+), 144 deletions(-)
 delete mode 100644 tasks/CentOS-packages-webserver.yml
 delete mode 100644 tasks/CentOS-timer-webserver.yml
 delete mode 100644 tasks/Debian-packages-webserver.yml
 delete mode 100644 tasks/Debian-timer-webserver.yml
 delete mode 100644 tasks/Fedora-packages-webserver.yml
 delete mode 100644 tasks/Fedora-timer-webserver.yml
 delete mode 100644 tasks/Ubuntu-packages-webserver.yml
 delete mode 100644 tasks/Ubuntu-timer-webserver.yml
 delete mode 100644 tasks/application.yml
 create mode 100644 tasks/renewal-hook.yml
 delete mode 100644 templates/renew-hook.sh.j2

diff --git a/README.md b/README.md
index 2d91e95..7a34437 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Role Variables
 ---
 # certbot settings
 certbot_dns_challenge: false                                                          # default use webserver, true to obtain certificate using dns challenge
-certbot_lego_version: "4.11.0"                                                        # version of LEGO client
+certbot_lego_version: "4.11.0"                                                        # lego version - Let's Encrypt client
 certbot_dns_provider: "designate"                                                     # list of available providers https://go-acme.github.io/lego/dns/
 dns_provider_auth_env_variables:                                                      # variables required to authenticate dns provider
   OS_AUTH_URL: "https://openstack.example.org"
@@ -19,16 +19,17 @@ dns_provider_auth_env_variables:
   OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}"
   OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}"
 
-
 certbot_fqdn:
   - example.de
 certbot_admin_email: "admin@example.de"
 # certbot_webroot: "/var/www/example"                                                 # if undefined use --standalone
 
-# application settings
-# certbot_application: "example"                                                      # if defined copy certs to application dir and setup deploy hooks
-certbot_application_dir: "/etc/{{ certbot_application }}"
-certbot_application_deploy_hook: |
+# LEGO Settings
+lego_extra_flags: []
+
+# renewal settings
+certbot_renewal_hook_file_name: "example"
+certbot_renewal_hook: |
   #!/bin/sh
 
   cp {{ certbot_live_dir }}/fullchain.pem {{ certbot_application_dir }}
@@ -54,6 +55,18 @@ roles:
     - hdacloud.certbot
 ```
 
+### Info when using DNS challenge
+
+In some cases you might need the certificates split up into the cert itself and a separate fullchain certificate
+chain. For this case you need to add the `--no-bundle` option to the `lego_extra_flags` array. Then add the
+following to the `certbot_renewal_hook` script (replacing variables accordingly):
+
+```sh
+rm "/etc/letsencrypt/live/fullchain.pem"
+cat "/etc/letsencrypt/live/certificates/{{ certbot_fqdn_first }}.crt" >> "/etc/letsencrypt/live/fullchain.pem" &&
+cat "/etc/letsencrypt/live/certificates/{{ certbot_fqdn_first }}.issuer.crt" >> "/etc/letsencrypt/live/fullchain.pem"
+```
+
 License
 -------
 
diff --git a/defaults/main.yml b/defaults/main.yml
index 43d75cc..eea0c5c 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -1,4 +1,18 @@
 ---
+# Packages / timer names based on Distribution
+certbot_pkg_name:
+  CentOS: "certbot"
+  Debian: "certbot"
+  Ubuntu: "certbot"
+  Fedora: "certbot"
+
+# TODO: Might need to change
+certbot_timer_name:
+  CentOS: "certbot-renew.timer"
+  Debian: "certbot.timer"
+  Ubuntu: "snap.certbot-renew.timer"
+  Fedora: "certbot-renew.timer"
+
 # certbot settings
 certbot_dns_challenge: false                                                          # default use webserver, true to obtain certificate using dns challenge
 certbot_lego_version: "4.11.0"                                                        # lego version - Let's Encrypt client
@@ -10,16 +24,17 @@ dns_provider_auth_env_variables:
   OS_APPLICATION_CREDENTIAL_ID: "{{ vault_os_application_credential_id }}"
   OS_APPLICATION_CREDENTIAL_SECRET: "{{ vault_os_application_credential_secret }}"
 
-
 certbot_fqdn:
   - example.de
 certbot_admin_email: "admin@example.de"
 # certbot_webroot: "/var/www/example"                                                 # if undefined use --standalone
 
-# application settings
-# certbot_application: "example"                                                      # if defined copy certs to application dir and setup deploy hooks
-certbot_application_dir: "/etc/{{ certbot_application }}"
-certbot_application_deploy_hook: |
+# LEGO Settings
+lego_extra_flags: []
+
+# renewal settings
+certbot_renewal_hook_file_name: "example"
+certbot_renewal_hook: |
   #!/bin/sh
 
   cp {{ certbot_live_dir }}/fullchain.pem {{ certbot_application_dir }}
diff --git a/tasks/CentOS-packages-webserver.yml b/tasks/CentOS-packages-webserver.yml
deleted file mode 100644
index 4915865..0000000
--- a/tasks/CentOS-packages-webserver.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-- name: Install Certbot
-  ansible.builtin.package:
-    name: # sadly doesn't as a list
-      - "{{ item }}"
-    state: present
-  loop:
-    - epel-release
-    - certbot
-  become: true
diff --git a/tasks/CentOS-timer-webserver.yml b/tasks/CentOS-timer-webserver.yml
deleted file mode 100644
index 78b2674..0000000
--- a/tasks/CentOS-timer-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Enable Letsencrypt Renew Timer
-  ansible.builtin.systemd:
-    name: certbot-renew.timer
-    state: started
-    enabled: true
-  become: true
diff --git a/tasks/Debian-packages-webserver.yml b/tasks/Debian-packages-webserver.yml
deleted file mode 100644
index 42ad8f1..0000000
--- a/tasks/Debian-packages-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Install Certbot
-  ansible.builtin.apt:
-    name: certbot
-    state: present
-    update_cache: true
-  become: true
diff --git a/tasks/Debian-timer-webserver.yml b/tasks/Debian-timer-webserver.yml
deleted file mode 100644
index 5192071..0000000
--- a/tasks/Debian-timer-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Enable Letsencrypt Renew Timer
-  ansible.builtin.systemd:
-    name: certbot.timer
-    state: started
-    enabled: true
-  become: true
diff --git a/tasks/Fedora-packages-webserver.yml b/tasks/Fedora-packages-webserver.yml
deleted file mode 100644
index 5c31930..0000000
--- a/tasks/Fedora-packages-webserver.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-- name: Install Certbot
-  ansible.builtin.package:
-    name: certbot
-    state: present
-  become: true
diff --git a/tasks/Fedora-timer-webserver.yml b/tasks/Fedora-timer-webserver.yml
deleted file mode 100644
index 78b2674..0000000
--- a/tasks/Fedora-timer-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Enable Letsencrypt Renew Timer
-  ansible.builtin.systemd:
-    name: certbot-renew.timer
-    state: started
-    enabled: true
-  become: true
diff --git a/tasks/Ubuntu-packages-webserver.yml b/tasks/Ubuntu-packages-webserver.yml
deleted file mode 100644
index 42ad8f1..0000000
--- a/tasks/Ubuntu-packages-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Install Certbot
-  ansible.builtin.apt:
-    name: certbot
-    state: present
-    update_cache: true
-  become: true
diff --git a/tasks/Ubuntu-timer-webserver.yml b/tasks/Ubuntu-timer-webserver.yml
deleted file mode 100644
index 5192071..0000000
--- a/tasks/Ubuntu-timer-webserver.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-- name: Enable Letsencrypt Renew Timer
-  ansible.builtin.systemd:
-    name: certbot.timer
-    state: started
-    enabled: true
-  become: true
diff --git a/tasks/application.yml b/tasks/application.yml
deleted file mode 100644
index b9bf4b4..0000000
--- a/tasks/application.yml
+++ /dev/null
@@ -1,34 +0,0 @@
----
-- name: Ensure Application Certificate Directory exists
-  ansible.builtin.file:
-    path: "{{ certbot_application_dir }}"
-    state: directory
-    owner: root
-    group: root
-    mode: "0755"
-  become: true
-
-- name: Check Whether Application Cert Exists
-  ansible.builtin.stat:
-    path: "{{ certbot_application_dir }}/fullchain.pem"
-  register: leoacert
-  become: true
-
-- name: Setup Application Deploy Hook
-  ansible.builtin.copy:
-    dest: "/etc/letsencrypt/renewal-hooks/deploy/{{ certbot_application }}"
-    content: "{{ certbot_application_deploy_hook }}"
-    mode: "0755"
-  become: true
-
-- name: Copy Application Cert If Necessary
-  ansible.builtin.copy:
-    src: "{{ item }}"
-    dest: "{{ certbot_application_dir }}"
-    remote_src: true
-    mode: "0644"
-  loop:
-    - "{{ certbot_live_dir }}/fullchain.pem"
-    - "{{ certbot_live_dir }}/privkey.pem"
-  when: not leoacert.stat.exists
-  become: true
diff --git a/tasks/dns-challenge.yml b/tasks/dns-challenge.yml
index 15b94bb..8916ec2 100644
--- a/tasks/dns-challenge.yml
+++ b/tasks/dns-challenge.yml
@@ -38,13 +38,6 @@
     - /etc/letsencrypt/renewal-hooks/deploy
   become: true
 
-- name: Render deploy hook script
-  ansible.builtin.template:
-    src: templates/renew-hook.sh.j2
-    dest: /etc/letsencrypt/renewal-hooks/deploy/create-fullchain.sh
-    mode: '0755'
-  become: true
-
 - name: Request Cert If Necessary - DNS Challenge
   when: not lecert.stat.exists
   become: true
@@ -60,26 +53,16 @@
       changed_when: lego.rc == 0
 
     - name: Mirror Letsencrypt Structure
-      block:
-        - name: Copy cert and key files
-          ansible.builtin.copy:
-            src: "{{ item.src }}"
-            dest: "{{ item.dest }}"
-            owner: root
-            group: root
-            mode: '0600'
-            remote_src: true
-          loop:
-            - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt", dest: "{{ certbot_live_dir }}/cert.pem" }
-            - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key", dest: "{{ certbot_live_dir }}/privkey.pem" }
-
-        - name: Build fullchain.pem file
-          ansible.builtin.shell:
-            cmd: >-
-              cat "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt" >> "{{ certbot_live_dir }}/fullchain.pem" &&
-              cat "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.issuer.crt" >> "{{ certbot_live_dir }}/fullchain.pem"
-          changed_when: false
-
+      ansible.builtin.copy:
+        src: "{{ item.src }}"
+        dest: "{{ item.dest }}"
+        owner: root
+        group: root
+        mode: '0600'
+        remote_src: true
+      loop:
+        - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt", dest: "{{ certbot_live_dir }}/cert.pem" }
+        - { src: "{{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key", dest: "{{ certbot_live_dir }}/privkey.pem" }
 
 - name: Render Systemd Files
   become: true
@@ -102,9 +85,9 @@
         dest: /lib/systemd/system/dns-challenge.timer
         mode: "0644"
 
-- name: Setup Certbot With Application
-  ansible.builtin.include_tasks: "application.yml"
-  when: certbot_application is defined
+- name: Setup renewal hook
+  ansible.builtin.include_tasks: "renewal-hook.yml"
+  when: certbot_renewal_hook is defined
 
 - name: Enable LEGO Renew Timer
   ansible.builtin.systemd:
diff --git a/tasks/renewal-hook.yml b/tasks/renewal-hook.yml
new file mode 100644
index 0000000..a24a31c
--- /dev/null
+++ b/tasks/renewal-hook.yml
@@ -0,0 +1,7 @@
+---
+- name: Setup Renewal Hook
+  ansible.builtin.copy:
+    dest: "/etc/letsencrypt/renewal-hooks/deploy/{{ certbot_renewal_hook_file_name }}"
+    content: "{{ certbot_renewal_hook }}"
+    mode: "0755"
+  become: true
diff --git a/tasks/webserver.yml b/tasks/webserver.yml
index 96e6074..06f4e64 100644
--- a/tasks/webserver.yml
+++ b/tasks/webserver.yml
@@ -1,5 +1,24 @@
+---
 - name: Install Packages Based On Distribution
-  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-packages-webserver.yml"
+  become: true
+  block:
+    - name: Install epel-release on CentOS
+      when: ansible_facts.distribution == "CentOS"
+      ansible.builtin.yum:
+        name: epel-release
+        state: present
+
+    - name: Install certbot
+      ansible.builtin.package:
+        name: "{{ certbot_pkg_name[ansible_facts.distribution] }}"
+        state: present
+
+    - name: Disable 'epel' again
+      when: ansible_facts.distribution == "CentOS"
+      ansible.builtin.yum_repository:
+        name: epel
+        state: absent
+
 
 - name: Check Whether Cert Exists
   ansible.builtin.stat:
@@ -46,9 +65,13 @@
   changed_when: cbwebroot.rc == 0
   become: true
 
-- name: Setup Certbot With Application
-  ansible.builtin.include_tasks: "application.yml"
-  when: certbot_application is defined
+- name: Setup Certbot renewal hook
+  ansible.builtin.include_tasks: "renewal-hook.yml"
+  when: certbot_renewal_hook is defined
 
 - name: Enable Letsencrypt Renew Timer Based On Distribution
-  ansible.builtin.include_tasks: "{{ ansible_facts.distribution }}-timer-webserver.yml"
+  become: true
+  ansible.builtin.systemd:
+    name: "{{ certbot_itmer_name[ansible_facts.distribution] }}"
+    state: started
+    enabled: true
diff --git a/templates/dns-challenge.service.j2 b/templates/dns-challenge.service.j2
index 107dbe9..695172d 100644
--- a/templates/dns-challenge.service.j2
+++ b/templates/dns-challenge.service.j2
@@ -5,8 +5,7 @@ Description=LEGO DNS challenge
 
 [Service]
 Type=oneshot
-ExecStart=/usr/bin/lego -a --dns {{ certbot_dns_provider }} --email {{ certbot_admin_email }} -d {{ lego_dflag }} --path {{ certbot_live_dir }} renew --no-bundle --renew-hook /etc/letsencrypt/renewal-hooks/deploy/create-fullchain.sh
+ExecStart=/usr/bin/lego -a --dns {{ certbot_dns_provider }} --email {{ certbot_admin_email }} -d {{ lego_dflag }} --path {{ certbot_live_dir }} renew {% for f in lego_extra_flags %}{{ f }} {% endfor %} {{ '--renew-hook /etc/letsencrypt/renewal-hooks/deploy/' + certbot_renewal_hook_file_name if certbot_renewal_hook is defined else ''}}
 ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt {{ certbot_live_dir }}/cert.pem
 ExecStartPost=cp {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.key {{ certbot_live_dir }}/privkey.pem
-{{ "ExecStartPost=/etc/letsencrypt/renewal-hooks/deploy/" + certbot_application if certbot_application is defined else "" }}
 EnvironmentFile=/etc/default/dns-challenge.env
diff --git a/templates/renew-hook.sh.j2 b/templates/renew-hook.sh.j2
deleted file mode 100644
index 820e87a..0000000
--- a/templates/renew-hook.sh.j2
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-
-cat {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.crt >> {{ certbot_live_dir }}/fullchain.pem
-cat {{ certbot_live_dir }}/certificates/{{ certbot_fqdn_first }}.issuer.crt >> {{ certbot_live_dir }}/fullchain.pem
\ No newline at end of file
-- 
GitLab