mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2024-01-19 02:48:50 +00:00
chore: Refactor setup-stack.sh
case SSL_TYPE=letsencrypt
(#2278)
Mostly cleans up the code and documents it better, although there are some minor fixes for handling `SSL_DOMAIN` ENV and additional logging added for spotting issues related to it in future when troubleshooting. Commits are scoped with context messages for easing review if necessary. Overview of changes: Traefik specific: - Logic extracted out into it's own function. - Conditional reworked to assist with debugging. - `SSL_DOMAIN` must not be empty when attempting to extract. - Added additional notes. `SSL_TYPE=letsencrypt` case: - Revised top note block. - Correct handling for `SSL_DOMAIN`. - Removed some unnecessary nesting. - Less repetitive error message for `LETSENCRYPT_DOMAIN`. - Added use of panics where appropriate (kept `return 1` so failures still exit functionality early). - Improved inline docs.
This commit is contained in:
parent
ebb081c80f
commit
bdb35dd19a
|
@ -938,6 +938,38 @@ function _setup_ssl
|
||||||
"${DOVECOT_CONFIG_SSL}"
|
"${DOVECOT_CONFIG_SSL}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 2020 feature intended for Traefik v2 support only:
|
||||||
|
# https://github.com/docker-mailserver/docker-mailserver/pull/1553
|
||||||
|
# Extracts files `key.pem` and `fullchain.pem`.
|
||||||
|
# `_extract_certs_from_acme` is located in `helper-functions.sh`
|
||||||
|
# NOTE: See the `SSL_TYPE=letsencrypt` case below for more details.
|
||||||
|
function _traefik_support
|
||||||
|
{
|
||||||
|
if [[ -f /etc/letsencrypt/acme.json ]]
|
||||||
|
then
|
||||||
|
# Variable only intended for troubleshooting via debug output
|
||||||
|
local EXTRACTED_DOMAIN
|
||||||
|
|
||||||
|
# Conditional handling depends on the success of `_extract_certs_from_acme`,
|
||||||
|
# Failure tries the next fallback FQDN to try extract a certificate from.
|
||||||
|
# Subshell not used in conditional to ensure extraction log output is still captured
|
||||||
|
if [[ -n ${SSL_DOMAIN} ]] && _extract_certs_from_acme "${SSL_DOMAIN}"
|
||||||
|
then
|
||||||
|
EXTRACTED_DOMAIN=('SSL_DOMAIN' "${SSL_DOMAIN}")
|
||||||
|
elif _extract_certs_from_acme "${HOSTNAME}"
|
||||||
|
then
|
||||||
|
EXTRACTED_DOMAIN=('HOSTNAME' "${HOSTNAME}")
|
||||||
|
elif _extract_certs_from_acme "${DOMAINNAME}"
|
||||||
|
then
|
||||||
|
EXTRACTED_DOMAIN=('DOMAINNAME' "${DOMAINNAME}")
|
||||||
|
else
|
||||||
|
_notify 'err' "'setup-stack.sh' | letsencrypt (acme.json) failed to identify a certificate to extract"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_notify 'inf' "'setup-stack.sh' | letsencrypt (acme.json) extracted certificate using ${EXTRACTED_DOMAIN[0]}: '${EXTRACTED_DOMAIN[1]}'"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# TLS strength/level configuration
|
# TLS strength/level configuration
|
||||||
case "${TLS_LEVEL}" in
|
case "${TLS_LEVEL}" in
|
||||||
( "modern" )
|
( "modern" )
|
||||||
|
@ -986,68 +1018,66 @@ function _setup_ssl
|
||||||
( "letsencrypt" )
|
( "letsencrypt" )
|
||||||
_notify 'inf' "Configuring SSL using 'letsencrypt'"
|
_notify 'inf' "Configuring SSL using 'letsencrypt'"
|
||||||
|
|
||||||
|
# `docker-mailserver` will only use one certificate from an FQDN folder in `/etc/letsencrypt/live/`.
|
||||||
|
# We iterate the sequence [SSL_DOMAIN, HOSTNAME, DOMAINNAME] to find a matching FQDN folder.
|
||||||
|
# This same sequence is used for the Traefik `acme.json` certificate extraction process, which outputs the FQDN folder.
|
||||||
|
#
|
||||||
|
# eg: If HOSTNAME (mail.example.test) doesn't exist, try DOMAINNAME (example.test).
|
||||||
|
# SSL_DOMAIN if set will take priority and is generally expected to have a wildcard prefix.
|
||||||
|
# SSL_DOMAIN will have any wildcard prefix stripped for the output FQDN folder it is stored in.
|
||||||
|
# TODO: A wildcard cert needs to be provisioned via Traefik to validate if acme.json contains any other value for `main` or `sans` beyond the wildcard.
|
||||||
|
#
|
||||||
|
# NOTE: HOSTNAME is set via `helper-functions.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.
|
||||||
|
|
||||||
|
_traefik_support
|
||||||
|
|
||||||
# letsencrypt folders and files mounted in /etc/letsencrypt
|
# letsencrypt folders and files mounted in /etc/letsencrypt
|
||||||
local LETSENCRYPT_DOMAIN=""
|
local LETSENCRYPT_DOMAIN
|
||||||
local LETSENCRYPT_KEY=""
|
local LETSENCRYPT_KEY
|
||||||
|
|
||||||
# 2020 feature intended for Traefik v2 support only:
|
# Identify a valid letsencrypt FQDN folder to use.
|
||||||
# https://github.com/docker-mailserver/docker-mailserver/pull/1553
|
if [[ -n ${SSL_DOMAIN} ]] && [[ -e /etc/letsencrypt/live/$(_strip_wildcard_prefix "${SSL_DOMAIN}")/fullchain.pem ]]
|
||||||
# Uses `key.pem` and `fullchain.pem`
|
|
||||||
if [[ -f /etc/letsencrypt/acme.json ]]
|
|
||||||
then
|
then
|
||||||
if ! _extract_certs_from_acme "${SSL_DOMAIN}"
|
LETSENCRYPT_DOMAIN=$(_strip_wildcard_prefix "${SSL_DOMAIN}")
|
||||||
then
|
elif [[ -e /etc/letsencrypt/live/${HOSTNAME}/fullchain.pem ]]
|
||||||
if ! _extract_certs_from_acme "${HOSTNAME}"
|
|
||||||
then
|
|
||||||
_extract_certs_from_acme "${DOMAINNAME}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# first determine the letsencrypt domain by checking both the full hostname or just the domainname if a SAN is used in the cert
|
|
||||||
if [[ -e /etc/letsencrypt/live/${HOSTNAME}/fullchain.pem ]]
|
|
||||||
then
|
then
|
||||||
LETSENCRYPT_DOMAIN=${HOSTNAME}
|
LETSENCRYPT_DOMAIN=${HOSTNAME}
|
||||||
elif [[ -e /etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem ]]
|
elif [[ -e /etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem ]]
|
||||||
then
|
then
|
||||||
LETSENCRYPT_DOMAIN=${DOMAINNAME}
|
LETSENCRYPT_DOMAIN=${DOMAINNAME}
|
||||||
else
|
else
|
||||||
_notify 'err' "Cannot access '/etc/letsencrypt/live/${HOSTNAME}/fullchain.pem' or '/etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem'"
|
_notify 'err' "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
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# then determine the keyfile to use
|
# Verify the FQDN folder also includes a valid private key (`privkey.pem` for Certbot, `key.pem` for extraction by Traefik)
|
||||||
if [[ -n ${LETSENCRYPT_DOMAIN} ]]
|
if [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem ]]
|
||||||
then
|
then
|
||||||
if [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem ]]
|
LETSENCRYPT_KEY='privkey'
|
||||||
then
|
elif [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/key.pem ]]
|
||||||
LETSENCRYPT_KEY="privkey"
|
then
|
||||||
elif [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/key.pem ]]
|
LETSENCRYPT_KEY='key'
|
||||||
then
|
else
|
||||||
LETSENCRYPT_KEY="key"
|
_notify 'err' "Cannot find key file ('privkey.pem' or 'key.pem') in '/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/'"
|
||||||
else
|
dms_panic__misconfigured 'LETSENCRYPT_KEY' "${SCOPE_SSL_TYPE}"
|
||||||
_notify 'err' "Cannot access '/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem' nor 'key.pem'"
|
return 1
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# finally, make the changes to the postfix and dovecot configurations
|
# Update relevant config for Postfix and Dovecot
|
||||||
if [[ -n ${LETSENCRYPT_KEY} ]]
|
_notify 'inf' "Adding ${LETSENCRYPT_DOMAIN} SSL certificate to the postfix and dovecot configuration"
|
||||||
then
|
|
||||||
_notify 'inf' "Adding ${LETSENCRYPT_DOMAIN} SSL certificate to the postfix and dovecot configuration"
|
|
||||||
|
|
||||||
# LetsEncrypt `fullchain.pem` and `privkey.pem` contents are detailed here from CertBot:
|
# LetsEncrypt `fullchain.pem` and `privkey.pem` contents are detailed here from CertBot:
|
||||||
# https://certbot.eff.org/docs/using.html#where-are-my-certificates
|
# https://certbot.eff.org/docs/using.html#where-are-my-certificates
|
||||||
# `key.pem` was added for `simp_le` support (2016): https://github.com/docker-mailserver/docker-mailserver/pull/288
|
# `key.pem` was added for `simp_le` support (2016): https://github.com/docker-mailserver/docker-mailserver/pull/288
|
||||||
# `key.pem` is also a filename used by the `_extract_certs_from_acme` method (implemented for Traefik v2 only)
|
# `key.pem` is also a filename used by the `_extract_certs_from_acme` method (implemented for Traefik v2 only)
|
||||||
local PRIVATE_KEY="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/${LETSENCRYPT_KEY}.pem"
|
local PRIVATE_KEY="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/${LETSENCRYPT_KEY}.pem"
|
||||||
local CERT_CHAIN="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/fullchain.pem"
|
local CERT_CHAIN="/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/fullchain.pem"
|
||||||
|
|
||||||
_set_certificate "${PRIVATE_KEY}" "${CERT_CHAIN}"
|
_set_certificate "${PRIVATE_KEY}" "${CERT_CHAIN}"
|
||||||
|
|
||||||
_notify 'inf' "SSL configured with 'letsencrypt' certificates"
|
_notify 'inf' "SSL configured with 'letsencrypt' certificates"
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
( "custom" ) # (hard-coded path) Use a private key with full certificate chain all in a single PEM file.
|
( "custom" ) # (hard-coded path) Use a private key with full certificate chain all in a single PEM file.
|
||||||
|
|
Loading…
Reference in a new issue