Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: letsencrypt implicit location discovery #2525

Merged
38 changes: 12 additions & 26 deletions target/scripts/check-for-changes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ do
# Also note that changes are performed in place and are not atomic
# We should fix that and write to temporary files, stop, swap and start

# _setup_ssl is required for:
# manual - copy to internal DMS_TLS_PATH (/etc/dms/tls) that Postfix and Dovecot are configured to use.
# acme.json - presently uses /etc/letsencrypt/live/<FQDN> instead of DMS_TLS_PATH,
# path may change requiring Postfix/Dovecot config update.
if [[ ${SSL_TYPE} == 'manual' ]]
then
# only run the SSL setup again if certificates have really changed.
Expand All @@ -75,9 +79,6 @@ do
|| [[ ${CHANGED} =~ ${SSL_ALT_KEY_PATH:-${REGEX_NEVER_MATCH}} ]]
then
_log_with_date 'debug' 'Manual certificates have changed - extracting certificates'
# we need to run the SSL setup again, because the
# certificates DMS is working with are copies of
# the (now changed) files
_setup_ssl
fi
# `acme.json` is only relevant to Traefik, and is where it stores the certificates it manages.
Expand All @@ -86,34 +87,19 @@ do
elif [[ ${CHANGED} =~ /etc/letsencrypt/acme.json ]]
then
_log_with_date 'debug' "'/etc/letsencrypt/acme.json' has changed - extracting certificates"
_setup_ssl

# This breaks early as we only need the first successful extraction.
# For more details see the `SSL_TYPE=letsencrypt` case handling in `setup-stack.sh`.
#
# NOTE: HOSTNAME is set via `helpers/dns.sh`, it is not the original system HOSTNAME ENV anymore.
# TODO: SSL_DOMAIN is Traefik specific, it no longer seems relevant and should be considered for removal.
FQDN_LIST=("${SSL_DOMAIN}" "${HOSTNAME}" "${DOMAINNAME}")
for CERT_DOMAIN in "${FQDN_LIST[@]}"
do
_log_with_date 'trace' "Attempting to extract for '${CERT_DOMAIN}'"

if _extract_certs_from_acme "${CERT_DOMAIN}"
then
# Prevent an unnecessary change detection from the newly extracted cert files by updating their hashes in advance:
CERT_DOMAIN=$(_strip_wildcard_prefix "${CERT_DOMAIN}")
ACME_CERT_DIR="/etc/letsencrypt/live/${CERT_DOMAIN}"

sed -i "\|${ACME_CERT_DIR}|d" "${CHKSUM_FILE}.new"
sha512sum "${ACME_CERT_DIR}"/*.pem >> "${CHKSUM_FILE}.new"

break
fi
done
# Prevent an unnecessary change detection from the newly extracted cert files by updating their hashes in advance:
local CERT_DOMAIN
CERT_DOMAIN="$(_find_letsencrypt_domain)"
ACME_CERT_DIR="/etc/letsencrypt/live/${CERT_DOMAIN}"

sed -i "\|${ACME_CERT_DIR}|d" "${CHKSUM_FILE}.new"
sha512sum "${ACME_CERT_DIR}"/*.pem >> "${CHKSUM_FILE}.new"
fi

# If monitored certificate files in /etc/letsencrypt/live have changed and no `acme.json` is in use,
# They presently have no special handling other than to trigger a change that will restart Postfix/Dovecot.
# TODO: That should be all that's required, unless the cert file paths have also changed (Postfix/Dovecot configs then need to be updated).

# regenerate postfix accounts
[[ ${SMTP_ONLY} -ne 1 ]] && _create_accounts
Expand Down
79 changes: 47 additions & 32 deletions target/scripts/helpers/ssl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -185,38 +185,10 @@ function _setup_ssl

_traefik_support

# letsencrypt folders and files mounted in /etc/letsencrypt
local LETSENCRYPT_DOMAIN
local LETSENCRYPT_KEY

# Identify a valid letsencrypt FQDN folder to use.
if [[ -n ${SSL_DOMAIN} ]] && [[ -e /etc/letsencrypt/live/$(_strip_wildcard_prefix "${SSL_DOMAIN}")/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=$(_strip_wildcard_prefix "${SSL_DOMAIN}")
elif [[ -e /etc/letsencrypt/live/${HOSTNAME}/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=${HOSTNAME}
elif [[ -e /etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=${DOMAINNAME}
else
_log 'warn' "Cannot find a valid DOMAIN for '/etc/letsencrypt/live/<DOMAIN>/', tried: '${SSL_DOMAIN}', '${HOSTNAME}', '${DOMAINNAME}'"
dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' "${SCOPE_SSL_TYPE}"
return 1
fi

# Verify the FQDN folder also includes a valid private key (`privkey.pem` for Certbot, `key.pem` for extraction by Traefik)
if [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem ]]
then
LETSENCRYPT_KEY='privkey'
elif [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/key.pem ]]
then
LETSENCRYPT_KEY='key'
else
_log 'warn' "Cannot find key file ('privkey.pem' or 'key.pem') in '/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/'"
dms_panic__misconfigured 'LETSENCRYPT_KEY' "${SCOPE_SSL_TYPE}"
return 1
fi
# checks folders in /etc/letsencrypt/live to identify which one to implicitly use:
local LETSENCRYPT_DOMAIN LETSENCRYPT_KEY
LETSENCRYPT_DOMAIN="$(_find_letsencrypt_domain)"
LETSENCRYPT_KEY="$(_find_letsencrypt_key)"
polarathene marked this conversation as resolved.
Show resolved Hide resolved

# Update relevant config for Postfix and Dovecot
_log 'trace' "Adding ${LETSENCRYPT_DOMAIN} SSL certificate to the postfix and dovecot configuration"
Expand Down Expand Up @@ -408,6 +380,49 @@ function _setup_ssl
esac
}


# Identify a valid letsencrypt FQDN folder to use.
function _find_letsencrypt_domain
{
local LETSENCRYPT_DOMAIN

if [[ -n ${SSL_DOMAIN} ]] && [[ -e /etc/letsencrypt/live/$(_strip_wildcard_prefix "${SSL_DOMAIN}")/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=$(_strip_wildcard_prefix "${SSL_DOMAIN}")
elif [[ -e /etc/letsencrypt/live/${HOSTNAME}/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=${HOSTNAME}
elif [[ -e /etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem ]]
then
LETSENCRYPT_DOMAIN=${DOMAINNAME}
else
_log 'warn' "Cannot find a valid DOMAIN for '/etc/letsencrypt/live/<DOMAIN>/', tried: '${SSL_DOMAIN}', '${HOSTNAME}', '${DOMAINNAME}'"
polarathene marked this conversation as resolved.
Show resolved Hide resolved
dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' "_find_letsencrypt_domain [SSL_TYPE=${SSL_TYPE}]"
fi

return "${LETSENCRYPT_DOMAIN}"
polarathene marked this conversation as resolved.
Show resolved Hide resolved
}

# Verify the FQDN folder also includes a valid private key (`privkey.pem` for Certbot, `key.pem` for extraction by Traefik)
function _find_letsencrypt_key
{
local LETSENCRYPT_KEY

local LETSENCRYPT_DOMAIN=${1:-"$(_find_letsencrypt_domain)"}
if [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem ]]
then
LETSENCRYPT_KEY='privkey'
elif [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/key.pem ]]
then
LETSENCRYPT_KEY='key'
else
_log 'warn' "Cannot find key file ('privkey.pem' or 'key.pem') in '/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/'"
polarathene marked this conversation as resolved.
Show resolved Hide resolved
dms_panic__misconfigured 'LETSENCRYPT_KEY' "_find_letsencrypt_key [SSL_TYPE=${SSL_TYPE}]"
fi

return "${LETSENCRYPT_KEY}"
polarathene marked this conversation as resolved.
Show resolved Hide resolved
}

function _extract_certs_from_acme
{
local CERT_DOMAIN=${1}
Expand Down