From 394e28b8150327e8da06ff54055b2acf34742726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lecour?= Date: Fri, 27 Sep 2019 00:13:30 +0200 Subject: [PATCH 1/5] WIP: new certbot role --- CHANGELOG.md | 1 + certbot/defaults/main.yml | 3 ++ certbot/files/hooks/apache.sh | 30 +++++++++++ certbot/files/hooks/commit-etc.sh | 32 ++++++++++++ certbot/files/hooks/dovecot.sh | 30 +++++++++++ certbot/files/hooks/haproxy.sh | 41 +++++++++++++++ certbot/files/hooks/nginx.sh | 30 +++++++++++ certbot/files/hooks/postfix.sh | 30 +++++++++++ certbot/handlers/main.yml | 16 ++++++ certbot/tasks/acme-challenge.yml | 50 +++++++++++++++++++ certbot/tasks/main.yml | 23 +++++++++ .../templates/acme-challenge/apache.conf.j2 | 6 +++ .../templates/acme-challenge/nginx.conf.j2 | 5 ++ 13 files changed, 297 insertions(+) create mode 100644 certbot/defaults/main.yml create mode 100644 certbot/files/hooks/apache.sh create mode 100644 certbot/files/hooks/commit-etc.sh create mode 100644 certbot/files/hooks/dovecot.sh create mode 100644 certbot/files/hooks/haproxy.sh create mode 100644 certbot/files/hooks/nginx.sh create mode 100644 certbot/files/hooks/postfix.sh create mode 100644 certbot/handlers/main.yml create mode 100644 certbot/tasks/acme-challenge.yml create mode 100644 certbot/tasks/main.yml create mode 100644 certbot/templates/acme-challenge/apache.conf.j2 create mode 100644 certbot/templates/acme-challenge/nginx.conf.j2 diff --git a/CHANGELOG.md b/CHANGELOG.md index dbec4b78..3b3dbe9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The **patch** part changes incrementally at each release. ## [Unreleased] ### Added +* certbot : new role to install and configure certbot * evocheck: explicit PATH * evolinux-base: On debian 10 and later, add noexec on /dev/shm * evolinux-base: default value for "evolinux_ssh_group" diff --git a/certbot/defaults/main.yml b/certbot/defaults/main.yml new file mode 100644 index 00000000..876be14e --- /dev/null +++ b/certbot/defaults/main.yml @@ -0,0 +1,3 @@ +--- + +certbot_work_dir: /var/lib/letsencrypt diff --git a/certbot/files/hooks/apache.sh b/certbot/files/hooks/apache.sh new file mode 100644 index 00000000..765943e0 --- /dev/null +++ b/certbot/files/hooks/apache.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +if [ -n "$(pidof apache2)" ]; then + apache2ctl_bin=$(command -v apache2ctl) + if ${apache2ctl_bin} configtest > /dev/null; then + if grep --dereference-recursive -E "^\s*SSLCertificate" /etc/apache2/sites-enabled | grep -q "letsencrypt"; then + debug "Apache detected... reloading" + systemctl reload apache2 + else + debug "Apache doesn't use Let's Encrypt certificate. Skip." + fi + else + error "Apache config is broken, you must fix it !" + fi +else + debug "Apache is not running. Skip." +fi diff --git a/certbot/files/hooks/commit-etc.sh b/certbot/files/hooks/commit-etc.sh new file mode 100644 index 00000000..a37ec85d --- /dev/null +++ b/certbot/files/hooks/commit-etc.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +git_bin=$(command -v git) +letsencrypt_dir=/etc/letsencrypt +export GIT_DIR="/etc/.git" +export GIT_WORK_TREE="/etc" + +if test -x "${git_bin}" && test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then + changed_lines=$(${git_bin} status --porcelain -- ${letsencrypt_dir} | wc -l | tr -d ' ') + + if [ "${changed_lines}" != "0" ]; then + debug "Committing for ${RENEWED_DOMAINS}" + ${git_bin} add --all ${letsencrypt_dir} + message="[letsencrypt] certificates renewal (${RENEWED_DOMAINS})" + ${git_bin} commit --message "${message}" --quiet + else + error "Weird, nothing has changed but the hook has been executed for '${RENEWED_DOMAINS}'" + fi +fi diff --git a/certbot/files/hooks/dovecot.sh b/certbot/files/hooks/dovecot.sh new file mode 100644 index 00000000..ffed2994 --- /dev/null +++ b/certbot/files/hooks/dovecot.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +if [ -n "$(pidof dovecot)" ]; then + doveconf_bin=$(command -v doveconf) + if ${doveconf_bin} > /dev/null; then + if ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt"; then + debug "Dovecot detected... reloading" + systemctl reload dovecot + else + debug "Dovecot doesn't use Let's Encrypt certificate. Skip." + fi + else + error "Dovecot config is broken, you must fix it !" + fi +else + debug "Dovecot is not running. Skip." +fi diff --git a/certbot/files/hooks/haproxy.sh b/certbot/files/hooks/haproxy.sh new file mode 100644 index 00000000..b6023bfc --- /dev/null +++ b/certbot/files/hooks/haproxy.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +if [ -z "${RENEWED_LINEAGE}" ]; then + error "This script must be called only by certbot!" +fi + +if [ -n "$(pidof haproxy)" ]; then + haproxy_bin=$(command -v haproxy) + if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null; then + if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then + haproxy_cert_file="/etc/ssl/haproxy/$(basename "${RENEWED_LINEAGE}").pem" + + debug "Concatenating certificate files to ${haproxy_cert_file}" + cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" + chmod 600 "${haproxy_cert_file}" + chown root: "${haproxy_cert_file}" + + debug "HAProxy detected... reloading" + systemctl reload apache2 + else + error "Couldn't find ${RENEWED_LINEAGE}/fullchain.pem or ${RENEWED_LINEAGE}/privkey.pem" + fi + else + error "HAProxy config is broken, you must fix it !" + fi +else + debug "HAProxy is not running. Skip." +fi diff --git a/certbot/files/hooks/nginx.sh b/certbot/files/hooks/nginx.sh new file mode 100644 index 00000000..ff78166a --- /dev/null +++ b/certbot/files/hooks/nginx.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +if [ -n "$(pidof nginx)" ]; then + nginx_bin=$(command -v nginx) + if ${nginx_bin} -t > /dev/null; then + if grep --dereference-recursive -E "^\s*ssl_certificate" /etc/nginx/sites-enabled | grep -q "letsencrypt"; then + debug "Nginx detected... reloading" + systemctl reload nginx + else + debug "Nginx doesn't use Let's Encrypt certificate. Skip." + fi + else + error "Nginx config is broken, you must fix it !" + fi +else + debug "Nginx is not running. Skip." +fi diff --git a/certbot/files/hooks/postfix.sh b/certbot/files/hooks/postfix.sh new file mode 100644 index 00000000..68948d0a --- /dev/null +++ b/certbot/files/hooks/postfix.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +error() { + >&2 echo "${PROGNAME}: $1" + exit 1 +} +debug() { + if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then + >&2 echo "${PROGNAME}: $1" + fi +} + +if [ -n "$(pidof master)" ]; then + postconf_bin=$(command -v postconf) + if ${postconf_bin} > /dev/null; then + if ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt"; then + debug "Postfix detected... reloading" + systemctl reload postfix + else + debug "Postfix doesn't use Let's Encrypt certificate. Skip." + fi + else + error "Postfix config is broken, you must fix it !" + fi +else + debug "Postfix is not running. Skip." +fi diff --git a/certbot/handlers/main.yml b/certbot/handlers/main.yml new file mode 100644 index 00000000..903fe696 --- /dev/null +++ b/certbot/handlers/main.yml @@ -0,0 +1,16 @@ +--- + +- name: reload nginx + service: + name: nginx + state: reloaded + +- name: reload apache + service: + name: apache2 + state: reloaded + +- name: reload haproxy + service: + name: haproxy + state: reloaded diff --git a/certbot/tasks/acme-challenge.yml b/certbot/tasks/acme-challenge.yml new file mode 100644 index 00000000..467b4606 --- /dev/null +++ b/certbot/tasks/acme-challenge.yml @@ -0,0 +1,50 @@ +--- + +- name: Certbot work directory is present + file: + dest: "{{ certbot_work_dir }}" + state: directory + mode: "0755" + +- name: Check if Nginx is installed + stat: + path: /etc/nginx + register: is_nginx + +- name: ACME challenge for Nginx is installed + template: + src: acme-challenge/nginx.conf.j2 + dest: /etc/nginx/snippets/letsencrypt.conf + force: yes + notify: reload nginx + when: is_nginx.stat.exists + +- name: Check if Apache is installed + stat: + path: /etc/apache2 + register: is_apache + +- block: + - name: ACME challenge for Apache is installed + template: + src: acme-challenge/apache.conf.j2 + dest: /etc/apache2/conf-available/letsencrypt.conf + force: yes + notify: reload apache + + - name: ACME challenge for Apache is enabled + command: "a2enconf letsencrypt" + register: command_result + changed_when: "'Enabling' in command_result.stderr" + notify: reload apache + when: is_apache.stat.exists + +- name: Check if HAProxy is installed + stat: + path: /etc/haproxy + register: is_haproxy + +- name: ACME challenge for HAProxy is installed + debug: + msg: "ACME challenge configuration for HAProxy should be configured manually" + when: is_haproxy.stat.exists diff --git a/certbot/tasks/main.yml b/certbot/tasks/main.yml new file mode 100644 index 00000000..b0218092 --- /dev/null +++ b/certbot/tasks/main.yml @@ -0,0 +1,23 @@ +--- + +- name: "System compatibility checks" + assert: + that: + - ansible_distribution == "Debian" + - ansible_distribution_major_version | version_compare('9', '>=') + msg: only compatible with Debian 9+ + +- name: certbot package is installed + apt: + name: certbot + state: latest + +- include: acme-challenge.yml + +- name: Deploy hooks are present + copy: + src: hooks/ + dest: /etc/letsencrypt/renewal-hooks/deploy/ + mode: "0700" + owner: root + group: root diff --git a/certbot/templates/acme-challenge/apache.conf.j2 b/certbot/templates/acme-challenge/apache.conf.j2 new file mode 100644 index 00000000..bae29e10 --- /dev/null +++ b/certbot/templates/acme-challenge/apache.conf.j2 @@ -0,0 +1,6 @@ +Alias /.well-known/acme-challenge {{ certbot_work_dir }}/.well-known/acme-challenge + + Options -Indexes + Allow from all + Require all granted + diff --git a/certbot/templates/acme-challenge/nginx.conf.j2 b/certbot/templates/acme-challenge/nginx.conf.j2 new file mode 100644 index 00000000..a0730f3c --- /dev/null +++ b/certbot/templates/acme-challenge/nginx.conf.j2 @@ -0,0 +1,5 @@ +location ~ /.well-known/acme-challenge { + alias {{ certbot_work_dir }}/; + try_files $uri =404; + allow all; +} From 86108999c10363f8b214bda81f5703e0eee8a3be Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 27 Sep 2019 09:31:22 +0200 Subject: [PATCH 2/5] certbot: check syntax in hooks just before reload --- certbot/files/hooks/apache.sh | 15 ++++++++------- certbot/files/hooks/dovecot.sh | 15 ++++++++------- certbot/files/hooks/haproxy.sh | 25 +++++++++++++------------ certbot/files/hooks/nginx.sh | 15 ++++++++------- certbot/files/hooks/postfix.sh | 15 ++++++++------- 5 files changed, 45 insertions(+), 40 deletions(-) diff --git a/certbot/files/hooks/apache.sh b/certbot/files/hooks/apache.sh index 765943e0..86b2c10d 100644 --- a/certbot/files/hooks/apache.sh +++ b/certbot/files/hooks/apache.sh @@ -13,18 +13,19 @@ debug() { fi } -if [ -n "$(pidof apache2)" ]; then - apache2ctl_bin=$(command -v apache2ctl) - if ${apache2ctl_bin} configtest > /dev/null; then - if grep --dereference-recursive -E "^\s*SSLCertificate" /etc/apache2/sites-enabled | grep -q "letsencrypt"; then +apache2ctl_bin=$(command -v apache2ctl) + +if [ -n "$(pidof apache2)" ] && [ -n "${apache2ctl_bin}" ]; then + if grep -r -E "letsencrypt" /etc/apache2/; then + if ${apache2ctl_bin} configtest > /dev/null; then debug "Apache detected... reloading" systemctl reload apache2 else - debug "Apache doesn't use Let's Encrypt certificate. Skip." + error "Apache config is broken, you must fix it !" fi else - error "Apache config is broken, you must fix it !" + debug "Apache doesn't use Let's Encrypt certificate. Skip." fi else - debug "Apache is not running. Skip." + debug "Apache is not running or missing. Skip." fi diff --git a/certbot/files/hooks/dovecot.sh b/certbot/files/hooks/dovecot.sh index ffed2994..d09e668c 100644 --- a/certbot/files/hooks/dovecot.sh +++ b/certbot/files/hooks/dovecot.sh @@ -13,18 +13,19 @@ debug() { fi } -if [ -n "$(pidof dovecot)" ]; then - doveconf_bin=$(command -v doveconf) - if ${doveconf_bin} > /dev/null; then - if ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt"; then +doveconf_bin=$(command -v doveconf) + +if [ -n "$(pidof dovecot)" ] && [ -n "${doveconf_bin}" ]; then + if ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt"; then + if ${doveconf_bin} > /dev/null; then debug "Dovecot detected... reloading" systemctl reload dovecot else - debug "Dovecot doesn't use Let's Encrypt certificate. Skip." + error "Dovecot config is broken, you must fix it !" fi else - error "Dovecot config is broken, you must fix it !" + debug "Dovecot doesn't use Let's Encrypt certificate. Skip." fi else - debug "Dovecot is not running. Skip." + debug "Dovecot is not running or missing. Skip." fi diff --git a/certbot/files/hooks/haproxy.sh b/certbot/files/hooks/haproxy.sh index b6023bfc..20d68fde 100644 --- a/certbot/files/hooks/haproxy.sh +++ b/certbot/files/hooks/haproxy.sh @@ -17,25 +17,26 @@ if [ -z "${RENEWED_LINEAGE}" ]; then error "This script must be called only by certbot!" fi -if [ -n "$(pidof haproxy)" ]; then - haproxy_bin=$(command -v haproxy) - if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null; then - if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then - haproxy_cert_file="/etc/ssl/haproxy/$(basename "${RENEWED_LINEAGE}").pem" +haproxy_bin=$(command -v haproxy) - debug "Concatenating certificate files to ${haproxy_cert_file}" - cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" - chmod 600 "${haproxy_cert_file}" - chown root: "${haproxy_cert_file}" +if [ -n "$(pidof haproxy)" ] && [ -n "${haproxy_bin}" ]; then + if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then + haproxy_cert_file="/etc/ssl/haproxy/$(basename "${RENEWED_LINEAGE}").pem" + debug "Concatenating certificate files to ${haproxy_cert_file}" + cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" + chmod 600 "${haproxy_cert_file}" + chown root: "${haproxy_cert_file}" + + if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null; then debug "HAProxy detected... reloading" systemctl reload apache2 else - error "Couldn't find ${RENEWED_LINEAGE}/fullchain.pem or ${RENEWED_LINEAGE}/privkey.pem" + error "HAProxy config is broken, you must fix it !" fi else - error "HAProxy config is broken, you must fix it !" + error "Couldn't find ${RENEWED_LINEAGE}/fullchain.pem or ${RENEWED_LINEAGE}/privkey.pem" fi else - debug "HAProxy is not running. Skip." + debug "HAProxy is not running or missing. Skip." fi diff --git a/certbot/files/hooks/nginx.sh b/certbot/files/hooks/nginx.sh index ff78166a..4c212634 100644 --- a/certbot/files/hooks/nginx.sh +++ b/certbot/files/hooks/nginx.sh @@ -13,18 +13,19 @@ debug() { fi } -if [ -n "$(pidof nginx)" ]; then - nginx_bin=$(command -v nginx) - if ${nginx_bin} -t > /dev/null; then - if grep --dereference-recursive -E "^\s*ssl_certificate" /etc/nginx/sites-enabled | grep -q "letsencrypt"; then +nginx_bin=$(command -v nginx) + +if [ -n "$(pidof nginx)" ] && [ -n "${nginx_bin}" ]; then + if grep --dereference-recursive -E "letsencrypt" /etc/nginx/sites-enabled; then + if ${nginx_bin} -t > /dev/null; then debug "Nginx detected... reloading" systemctl reload nginx else - debug "Nginx doesn't use Let's Encrypt certificate. Skip." + error "Nginx config is broken, you must fix it !" fi else - error "Nginx config is broken, you must fix it !" + debug "Nginx doesn't use Let's Encrypt certificate. Skip." fi else - debug "Nginx is not running. Skip." + debug "Nginx is not running or missing. Skip." fi diff --git a/certbot/files/hooks/postfix.sh b/certbot/files/hooks/postfix.sh index 68948d0a..f08a0334 100644 --- a/certbot/files/hooks/postfix.sh +++ b/certbot/files/hooks/postfix.sh @@ -13,18 +13,19 @@ debug() { fi } -if [ -n "$(pidof master)" ]; then - postconf_bin=$(command -v postconf) - if ${postconf_bin} > /dev/null; then - if ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt"; then +postconf_bin=$(command -v postconf) + +if [ -n "$(pidof master)" ] && [ -n "${postconf_bin}" ]; then + if ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt"; then + if ${postconf_bin} > /dev/null; then debug "Postfix detected... reloading" systemctl reload postfix else - debug "Postfix doesn't use Let's Encrypt certificate. Skip." + error "Postfix config is broken, you must fix it !" fi else - error "Postfix config is broken, you must fix it !" + debug "Postfix doesn't use Let's Encrypt certificate. Skip." fi else - debug "Postfix is not running. Skip." + debug "Postfix is not running or missing. Skip." fi From 3521d4a7652e6ac06137c4af0d27fe30a06ede07 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 27 Sep 2019 09:47:51 +0200 Subject: [PATCH 3/5] certbot: verify generated combined certificate file for HAProxy --- certbot/files/hooks/haproxy.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/certbot/files/hooks/haproxy.sh b/certbot/files/hooks/haproxy.sh index 20d68fde..0ddc32e8 100644 --- a/certbot/files/hooks/haproxy.sh +++ b/certbot/files/hooks/haproxy.sh @@ -22,12 +22,21 @@ haproxy_bin=$(command -v haproxy) if [ -n "$(pidof haproxy)" ] && [ -n "${haproxy_bin}" ]; then if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then haproxy_cert_file="/etc/ssl/haproxy/$(basename "${RENEWED_LINEAGE}").pem" + failed_cert_file="/root/$(basename "${RENEWED_LINEAGE}").failed.pem" debug "Concatenating certificate files to ${haproxy_cert_file}" cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" chmod 600 "${haproxy_cert_file}" chown root: "${haproxy_cert_file}" + haproxy_cert_md5=$(openssl x509 -noout -modulus -in "${haproxy_cert_file}" | openssl md5) + haproxy_key_md5=$(openssl rsa -noout -modulus -in "${haproxy_cert_file}" | openssl md5) + + if [ "${haproxy_cert_md5}" != "${haproxy_key_md5}" ]; then + mv "${haproxy_cert_file}" "${failed_cert_file}" + error "Key and cert don't match, we moved the file to ${failed_cert_file} for inspection" + fi + if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null; then debug "HAProxy detected... reloading" systemctl reload apache2 From 44b2480e031a7f83a755685021394df6f79f411b Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 27 Sep 2019 10:15:33 +0200 Subject: [PATCH 4/5] certbot: improve hooks --- certbot/files/hooks/apache.sh | 5 +++-- certbot/files/hooks/commit-etc.sh | 1 + certbot/files/hooks/dovecot.sh | 3 ++- certbot/files/hooks/haproxy.sh | 10 ++++++++-- certbot/files/hooks/nginx.sh | 5 +++-- certbot/files/hooks/postfix.sh | 3 ++- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/certbot/files/hooks/apache.sh b/certbot/files/hooks/apache.sh index 86b2c10d..9692c3c5 100644 --- a/certbot/files/hooks/apache.sh +++ b/certbot/files/hooks/apache.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} @@ -16,8 +17,8 @@ debug() { apache2ctl_bin=$(command -v apache2ctl) if [ -n "$(pidof apache2)" ] && [ -n "${apache2ctl_bin}" ]; then - if grep -r -E "letsencrypt" /etc/apache2/; then - if ${apache2ctl_bin} configtest > /dev/null; then + if grep -q -r -E "letsencrypt" /etc/apache2/; then + if ${apache2ctl_bin} configtest > /dev/null 2>&1; then debug "Apache detected... reloading" systemctl reload apache2 else diff --git a/certbot/files/hooks/commit-etc.sh b/certbot/files/hooks/commit-etc.sh index a37ec85d..970fa81b 100644 --- a/certbot/files/hooks/commit-etc.sh +++ b/certbot/files/hooks/commit-etc.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} diff --git a/certbot/files/hooks/dovecot.sh b/certbot/files/hooks/dovecot.sh index d09e668c..49ec9bb5 100644 --- a/certbot/files/hooks/dovecot.sh +++ b/certbot/files/hooks/dovecot.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} @@ -17,7 +18,7 @@ doveconf_bin=$(command -v doveconf) if [ -n "$(pidof dovecot)" ] && [ -n "${doveconf_bin}" ]; then if ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt"; then - if ${doveconf_bin} > /dev/null; then + if ${doveconf_bin} > /dev/null 2>&1; then debug "Dovecot detected... reloading" systemctl reload dovecot else diff --git a/certbot/files/hooks/haproxy.sh b/certbot/files/hooks/haproxy.sh index 0ddc32e8..1e99cc88 100644 --- a/certbot/files/hooks/haproxy.sh +++ b/certbot/files/hooks/haproxy.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} @@ -18,12 +19,17 @@ if [ -z "${RENEWED_LINEAGE}" ]; then fi haproxy_bin=$(command -v haproxy) +haproxy_cert_dir="/etc/ssl/haproxy/" if [ -n "$(pidof haproxy)" ] && [ -n "${haproxy_bin}" ]; then if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then - haproxy_cert_file="/etc/ssl/haproxy/$(basename "${RENEWED_LINEAGE}").pem" + haproxy_cert_file="${haproxy_cert_dir}/$(basename "${RENEWED_LINEAGE}").pem" failed_cert_file="/root/$(basename "${RENEWED_LINEAGE}").failed.pem" + # shellcheck disable=SC2174 + mkdir --mode=700 --parents "${haproxy_cert_dir}" + chown root: "${haproxy_cert_dir}" + debug "Concatenating certificate files to ${haproxy_cert_file}" cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" chmod 600 "${haproxy_cert_file}" @@ -37,7 +43,7 @@ if [ -n "$(pidof haproxy)" ] && [ -n "${haproxy_bin}" ]; then error "Key and cert don't match, we moved the file to ${failed_cert_file} for inspection" fi - if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null; then + if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null 2>&1; then debug "HAProxy detected... reloading" systemctl reload apache2 else diff --git a/certbot/files/hooks/nginx.sh b/certbot/files/hooks/nginx.sh index 4c212634..aafbedd3 100644 --- a/certbot/files/hooks/nginx.sh +++ b/certbot/files/hooks/nginx.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} @@ -16,8 +17,8 @@ debug() { nginx_bin=$(command -v nginx) if [ -n "$(pidof nginx)" ] && [ -n "${nginx_bin}" ]; then - if grep --dereference-recursive -E "letsencrypt" /etc/nginx/sites-enabled; then - if ${nginx_bin} -t > /dev/null; then + if grep -q --dereference-recursive -E "letsencrypt" /etc/nginx/sites-enabled; then + if ${nginx_bin} -t > /dev/null 2>&1; then debug "Nginx detected... reloading" systemctl reload nginx else diff --git a/certbot/files/hooks/postfix.sh b/certbot/files/hooks/postfix.sh index f08a0334..3a9b3b3d 100644 --- a/certbot/files/hooks/postfix.sh +++ b/certbot/files/hooks/postfix.sh @@ -1,5 +1,6 @@ #!/bin/sh +readonly PROGNAME=$(basename "$0") readonly VERBOSE=${VERBOSE:-"0"} readonly QUIET=${QUIET:-"0"} @@ -17,7 +18,7 @@ postconf_bin=$(command -v postconf) if [ -n "$(pidof master)" ] && [ -n "${postconf_bin}" ]; then if ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt"; then - if ${postconf_bin} > /dev/null; then + if ${postconf_bin} > /dev/null 2>&1; then debug "Postfix detected... reloading" systemctl reload postfix else From 68e6d6cb23fffaaf4e386c484f91c444475cf725 Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 27 Sep 2019 14:03:39 +0200 Subject: [PATCH 5/5] improve hooks maintainability --- certbot/files/hooks/apache.sh | 46 ++++++++++------ certbot/files/hooks/commit-etc.sh | 42 ++++++++------- certbot/files/hooks/dovecot.sh | 46 ++++++++++------ certbot/files/hooks/haproxy.sh | 90 ++++++++++++++++++------------- certbot/files/hooks/nginx.sh | 46 ++++++++++------ certbot/files/hooks/postfix.sh | 46 ++++++++++------ certbot/tasks/acme-challenge.yml | 2 +- 7 files changed, 194 insertions(+), 124 deletions(-) diff --git a/certbot/files/hooks/apache.sh b/certbot/files/hooks/apache.sh index 9692c3c5..1235a23e 100644 --- a/certbot/files/hooks/apache.sh +++ b/certbot/files/hooks/apache.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,20 +9,36 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } - -apache2ctl_bin=$(command -v apache2ctl) - -if [ -n "$(pidof apache2)" ] && [ -n "${apache2ctl_bin}" ]; then - if grep -q -r -E "letsencrypt" /etc/apache2/; then - if ${apache2ctl_bin} configtest > /dev/null 2>&1; then - debug "Apache detected... reloading" - systemctl reload apache2 +daemon_found_and_running() { + test -n "$(pidof apache2)" && test -n "${apache2ctl_bin}" +} +config_check() { + ${apache2ctl_bin} configtest > /dev/null 2>&1 +} +letsencrypt_used() { + grep -q -r -E "letsencrypt" /etc/apache2/ +} +main() { + if daemon_found_and_running; then + if letsencrypt_used; then + if config_check; then + debug "Apache detected... reloading" + systemctl reload apache2 + else + error "Apache config is broken, you must fix it !" + fi else - error "Apache config is broken, you must fix it !" + debug "Apache doesn't use Let's Encrypt certificate. Skip." fi else - debug "Apache doesn't use Let's Encrypt certificate. Skip." + debug "Apache is not running or missing. Skip." fi -else - debug "Apache is not running or missing. Skip." -fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly apache2ctl_bin=$(command -v apache2ctl) + +main diff --git a/certbot/files/hooks/commit-etc.sh b/certbot/files/hooks/commit-etc.sh index 970fa81b..f55b253c 100644 --- a/certbot/files/hooks/commit-etc.sh +++ b/certbot/files/hooks/commit-etc.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,21 +9,29 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } +main() { + export GIT_DIR="/etc/.git" + export GIT_WORK_TREE="/etc" -git_bin=$(command -v git) -letsencrypt_dir=/etc/letsencrypt -export GIT_DIR="/etc/.git" -export GIT_WORK_TREE="/etc" + if test -x "${git_bin}" && test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then + changed_lines=$(${git_bin} status --porcelain -- ${letsencrypt_dir} | wc -l | tr -d ' ') -if test -x "${git_bin}" && test -d "${GIT_DIR}" && test -d "${GIT_WORK_TREE}"; then - changed_lines=$(${git_bin} status --porcelain -- ${letsencrypt_dir} | wc -l | tr -d ' ') + if [ "${changed_lines}" != "0" ]; then + debug "Committing for ${RENEWED_DOMAINS}" + ${git_bin} add --all ${letsencrypt_dir} + message="[letsencrypt] certificates renewal (${RENEWED_DOMAINS})" + ${git_bin} commit --message "${message}" --quiet + else + error "Weird, nothing has changed but the hook has been executed for '${RENEWED_DOMAINS}'" + fi + fi +} - if [ "${changed_lines}" != "0" ]; then - debug "Committing for ${RENEWED_DOMAINS}" - ${git_bin} add --all ${letsencrypt_dir} - message="[letsencrypt] certificates renewal (${RENEWED_DOMAINS})" - ${git_bin} commit --message "${message}" --quiet - else - error "Weird, nothing has changed but the hook has been executed for '${RENEWED_DOMAINS}'" - fi -fi +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly git_bin=$(command -v git) +readonly letsencrypt_dir=/etc/letsencrypt + +main diff --git a/certbot/files/hooks/dovecot.sh b/certbot/files/hooks/dovecot.sh index 49ec9bb5..56e5b5ae 100644 --- a/certbot/files/hooks/dovecot.sh +++ b/certbot/files/hooks/dovecot.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,20 +9,36 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } - -doveconf_bin=$(command -v doveconf) - -if [ -n "$(pidof dovecot)" ] && [ -n "${doveconf_bin}" ]; then - if ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt"; then - if ${doveconf_bin} > /dev/null 2>&1; then - debug "Dovecot detected... reloading" - systemctl reload dovecot +daemon_found_and_running() { + test -n "$(pidof dovecot)" && test -n "${doveconf_bin}" +} +config_check() { + ${doveconf_bin} > /dev/null 2>&1 +} +letsencrypt_used() { + ${doveconf_bin} | grep -E "^ssl_cert[^_]" | grep -q "letsencrypt" +} +main() { + if daemon_found_and_running; then + if letsencrypt_used; then + if config_check; then + debug "Dovecot detected... reloading" + systemctl reload dovecot + else + error "Dovecot config is broken, you must fix it !" + fi else - error "Dovecot config is broken, you must fix it !" + debug "Dovecot doesn't use Let's Encrypt certificate. Skip." fi else - debug "Dovecot doesn't use Let's Encrypt certificate. Skip." + debug "Dovecot is not running or missing. Skip." fi -else - debug "Dovecot is not running or missing. Skip." -fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly doveconf_bin=$(command -v doveconf) + +main diff --git a/certbot/files/hooks/haproxy.sh b/certbot/files/hooks/haproxy.sh index 1e99cc88..63ef6990 100644 --- a/certbot/files/hooks/haproxy.sh +++ b/certbot/files/hooks/haproxy.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,45 +9,67 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } +daemon_found_and_running() { + test -n "$(pidof haproxy)" && test -n "${haproxy_bin}" +} +found_renewed_lineage() { + test -f "${RENEWED_LINEAGE}/fullchain.pem" && test -f "${RENEWED_LINEAGE}/privkey.pem" +} +config_check() { + ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null 2>&1 +} +concat_files() { + # shellcheck disable=SC2174 + mkdir --mode=700 --parents "${haproxy_cert_dir}" + chown root: "${haproxy_cert_dir}" -if [ -z "${RENEWED_LINEAGE}" ]; then - error "This script must be called only by certbot!" -fi + debug "Concatenating certificate files to ${haproxy_cert_file}" + cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" + chmod 600 "${haproxy_cert_file}" + chown root: "${haproxy_cert_file}" +} +cert_and_key_mismatch() { + haproxy_cert_md5=$(openssl x509 -noout -modulus -in "${haproxy_cert_file}" | openssl md5) + haproxy_key_md5=$(openssl rsa -noout -modulus -in "${haproxy_cert_file}" | openssl md5) -haproxy_bin=$(command -v haproxy) -haproxy_cert_dir="/etc/ssl/haproxy/" + test "${haproxy_cert_md5}" != "${haproxy_key_md5}" +} +main() { + if [ -z "${RENEWED_LINEAGE}" ]; then + error "This script must be called only by certbot!" + fi -if [ -n "$(pidof haproxy)" ] && [ -n "${haproxy_bin}" ]; then - if [ -f "${RENEWED_LINEAGE}/fullchain.pem" ] && [ -f "${RENEWED_LINEAGE}/privkey.pem" ]; then - haproxy_cert_file="${haproxy_cert_dir}/$(basename "${RENEWED_LINEAGE}").pem" - failed_cert_file="/root/$(basename "${RENEWED_LINEAGE}").failed.pem" + if daemon_found_and_running; then + if found_renewed_lineage; then + haproxy_cert_file="${haproxy_cert_dir}/$(basename "${RENEWED_LINEAGE}").pem" + failed_cert_file="/root/$(basename "${RENEWED_LINEAGE}").failed.pem" - # shellcheck disable=SC2174 - mkdir --mode=700 --parents "${haproxy_cert_dir}" - chown root: "${haproxy_cert_dir}" + concat_files - debug "Concatenating certificate files to ${haproxy_cert_file}" - cat "${RENEWED_LINEAGE}/fullchain.pem" "${RENEWED_LINEAGE}/privkey.pem" > "${haproxy_cert_file}" - chmod 600 "${haproxy_cert_file}" - chown root: "${haproxy_cert_file}" + if cert_and_key_mismatch; then + mv "${haproxy_cert_file}" "${failed_cert_file}" + error "Key and cert don't match, we moved the file to ${failed_cert_file} for inspection" + fi - haproxy_cert_md5=$(openssl x509 -noout -modulus -in "${haproxy_cert_file}" | openssl md5) - haproxy_key_md5=$(openssl rsa -noout -modulus -in "${haproxy_cert_file}" | openssl md5) - - if [ "${haproxy_cert_md5}" != "${haproxy_key_md5}" ]; then - mv "${haproxy_cert_file}" "${failed_cert_file}" - error "Key and cert don't match, we moved the file to ${failed_cert_file} for inspection" - fi - - if ${haproxy_bin} -c -f /etc/haproxy/haproxy.cfg > /dev/null 2>&1; then - debug "HAProxy detected... reloading" - systemctl reload apache2 + if config_check; then + debug "HAProxy detected... reloading" + systemctl reload apache2 + else + error "HAProxy config is broken, you must fix it !" + fi else - error "HAProxy config is broken, you must fix it !" + error "Couldn't find ${RENEWED_LINEAGE}/fullchain.pem or ${RENEWED_LINEAGE}/privkey.pem" fi else - error "Couldn't find ${RENEWED_LINEAGE}/fullchain.pem or ${RENEWED_LINEAGE}/privkey.pem" + debug "HAProxy is not running or missing. Skip." fi -else - debug "HAProxy is not running or missing. Skip." -fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly haproxy_bin=$(command -v haproxy) +readonly haproxy_cert_dir="/etc/ssl/haproxy" + +main diff --git a/certbot/files/hooks/nginx.sh b/certbot/files/hooks/nginx.sh index aafbedd3..2a1d27e9 100644 --- a/certbot/files/hooks/nginx.sh +++ b/certbot/files/hooks/nginx.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,20 +9,36 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } - -nginx_bin=$(command -v nginx) - -if [ -n "$(pidof nginx)" ] && [ -n "${nginx_bin}" ]; then - if grep -q --dereference-recursive -E "letsencrypt" /etc/nginx/sites-enabled; then - if ${nginx_bin} -t > /dev/null 2>&1; then - debug "Nginx detected... reloading" - systemctl reload nginx +daemon_found_and_running() { + test -n "$(pidof nginx)" && test -n "${nginx_bin}" +} +config_check() { + ${nginx_bin} -t > /dev/null 2>&1 +} +letsencrypt_used() { + grep -q --dereference-recursive -E "letsencrypt" /etc/nginx/sites-enabled +} +main() { + if daemon_found_and_running; then + if letsencrypt_used; then + if config_check; then + debug "Nginx detected... reloading" + systemctl reload nginx + else + error "Nginx config is broken, you must fix it !" + fi else - error "Nginx config is broken, you must fix it !" + debug "Nginx doesn't use Let's Encrypt certificate. Skip." fi else - debug "Nginx doesn't use Let's Encrypt certificate. Skip." + debug "Nginx is not running or missing. Skip." fi -else - debug "Nginx is not running or missing. Skip." -fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly nginx_bin=$(command -v nginx) + +main diff --git a/certbot/files/hooks/postfix.sh b/certbot/files/hooks/postfix.sh index 3a9b3b3d..de6a4b7c 100644 --- a/certbot/files/hooks/postfix.sh +++ b/certbot/files/hooks/postfix.sh @@ -1,9 +1,5 @@ #!/bin/sh -readonly PROGNAME=$(basename "$0") -readonly VERBOSE=${VERBOSE:-"0"} -readonly QUIET=${QUIET:-"0"} - error() { >&2 echo "${PROGNAME}: $1" exit 1 @@ -13,20 +9,36 @@ debug() { >&2 echo "${PROGNAME}: $1" fi } - -postconf_bin=$(command -v postconf) - -if [ -n "$(pidof master)" ] && [ -n "${postconf_bin}" ]; then - if ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt"; then - if ${postconf_bin} > /dev/null 2>&1; then - debug "Postfix detected... reloading" - systemctl reload postfix +daemon_found_and_running() { + test -n "$(pidof master)" && test -n "${postconf_bin}" +} +config_check() { + ${postconf_bin} > /dev/null 2>&1 +} +letsencrypt_used() { + ${postconf_bin} | grep -E "^smtpd_tls_cert_file" | grep -q "letsencrypt" +} +main() { + if daemon_found_and_running; then + if letsencrypt_used; then + if config_check; then + debug "Postfix detected... reloading" + systemctl reload postfix + else + error "Postfix config is broken, you must fix it !" + fi else - error "Postfix config is broken, you must fix it !" + debug "Postfix doesn't use Let's Encrypt certificate. Skip." fi else - debug "Postfix doesn't use Let's Encrypt certificate. Skip." + debug "Postfix is not running or missing. Skip." fi -else - debug "Postfix is not running or missing. Skip." -fi +} + +readonly PROGNAME=$(basename "$0") +readonly VERBOSE=${VERBOSE:-"0"} +readonly QUIET=${QUIET:-"0"} + +readonly postconf_bin=$(command -v postconf) + +main diff --git a/certbot/tasks/acme-challenge.yml b/certbot/tasks/acme-challenge.yml index 467b4606..d2fa78ec 100644 --- a/certbot/tasks/acme-challenge.yml +++ b/certbot/tasks/acme-challenge.yml @@ -46,5 +46,5 @@ - name: ACME challenge for HAProxy is installed debug: - msg: "ACME challenge configuration for HAProxy should be configured manually" + msg: "ACME challenge configuration for HAProxy must be configured manually" when: is_haproxy.stat.exists