Evoacme: refactoring

* add a lot of variables, to reduce possible typos
* add a lot of debug statements
* add many comments and line breaks for readability
* extract functions for complex openssl commands
* explode the big certbot command into multiple lines
* allow certbot to make test certs (for API query limits)
* allow certbot to run in "dry run" mode
* regroup some lines together when they do related things
This commit is contained in:
Jérémy Lecour 2017-10-12 00:29:21 +02:00
parent 1091dfeeed
commit 0d0c21f908

View file

@ -28,16 +28,36 @@ error() {
exit 1 exit 1
} }
mkconf_apache() { change_cert_path_for_apache() {
debug "Apache detected... first configuration" vhost=$1
[ -f "/etc/apache2/ssl/${vhost}.conf" ] && sed -i "s~^SSLCertificateFile.*$~SSLCertificateFile $CRT_DIR/${vhost}/live/fullchain.pem~" "/etc/apache2/ssl/${vhost}.conf" vhost_full_path="/etc/apache2/ssl/${vhost}.conf"
apache2ctl -t cert_path=$2
debug "Apache detected... first configuration in ${vhost_full_path}"
[ -f "${vhost_full_path}" ] && sed -i "s~^SSLCertificateFile.*$~SSLCertificateFile ${cert_path}~" "${vhost_full_path}"
${APACHE2CTL_BIN} -t
} }
mkconf_nginx() { change_cert_path_for_nginx() {
debug "Nginx detected... first configuration" vhost=$1
[ -f "/etc/nginx/ssl/${vhost}.conf" ] && sed -i "s~^ssl_certificate[^_].*$~ssl_certificate $CRT_DIR/${vhost}/live/fullchain.pem;~" "/etc/nginx/ssl/${vhost}.conf" vhost_full_path="/etc/nginx/ssl/${vhost}.conf"
nginx -t cert_path=$2
debug "Nginx detected... first configuration in ${vhost_full_path}"
[ -f "${vhost_full_path}" ] && sed -i "s~^ssl_certificate[^_].*$~ssl_certificate ${cert_path};~" "${vhost_full_path}"
${NGINX_BIN} -t
}
x509_verify() {
file=$1
${OPENSSL_BIN} x509 -noout -modulus -in "${file}" >/dev/null
}
x509_enddate() {
file=$1
${OPENSSL_BIN} x509 -noout -enddate -in "${file}"
} }
main() { main() {
@ -51,87 +71,168 @@ main() {
[ -z "${SELF_SIGNED_DIR}" ] && SELF_SIGNED_DIR=/etc/ssl/self-signed [ -z "${SELF_SIGNED_DIR}" ] && SELF_SIGNED_DIR=/etc/ssl/self-signed
[ -z "${DH_DIR}" ] && DH_DIR=etc/ssl/dhparam [ -z "${DH_DIR}" ] && DH_DIR=etc/ssl/dhparam
# misc verifications
[ "$1" = "-h" ] || [ "$1" = "--help" ] && usage && exit 0 [ "$1" = "-h" ] || [ "$1" = "--help" ] && usage && exit 0
which openssl >/dev/null || error "openssl command not installed" # check arguments
which certbot >/dev/null || error "certbot command not installed"
[ ! -d $ACME_DIR ] && error "$ACME_DIR is not a directory"
[ ! -d $CSR_DIR ] && error "$CSR_DIR is not a directory"
[ ! -d $LOG_DIR ] && error "$LOG_DIR is not a directory"
[ "$#" -ge 3 ] || [ "$#" -le 0 ] && error "invalid argument(s)" [ "$#" -ge 3 ] || [ "$#" -le 0 ] && error "invalid argument(s)"
[ "$#" -eq 2 ] && [ "$1" != "--cron" ] && error "invalid argument(s)" [ "$#" -eq 2 ] && [ "$1" != "--cron" ] && error "invalid argument(s)"
[ "$#" -eq 1 ] && vhost=$(basename "$1" .conf) && CRON=NO [ "$#" -eq 1 ] && VHOST=$(basename "$1" .conf) && CRON=NO
[ "$#" -eq 2 ] && vhost=$(basename "$2" .conf) && CRON=YES [ "$#" -eq 2 ] && VHOST=$(basename "$2" .conf) && CRON=YES
# check for important programs
OPENSSL_BIN=$(command -v openssl)
if [ "$?" -eq 0 ]; then
error "openssl command not installed"
fi
CERTBOT_BIN=$(command -v certbot)
if [ "$?" -eq 0 ]; then
error "certbot command not installed"
fi
APACHE2CTL_BIN=$(command -v apache2ctl)
NGINX_BIN=$(command -v nginx)
# double check for directories
[ ! -d "${ACME_DIR}" ] && error "${ACME_DIR} is not a directory"
[ ! -d "${CSR_DIR}" ] && error "${CSR_DIR} is not a directory"
[ ! -d "${LOG_DIR} "] && error "${LOG_DIR} is not a directory"
#### CSR VALIDATION
# verify .csr file # verify .csr file
[ ! -f "$CSR_DIR/${vhost}.csr" ] && error "$CSR_DIR/${vhost}.csr absent" CSR_FILE="${CSR_DIR}/${VHOST}.csr"
[ ! -r "$CSR_DIR/${vhost}.csr" ] && error "$C´SR_DIR/${vhost}.csr is not readable" debug "Using CSR file: ${CSR_FILE}"
openssl req -noout -modulus -in "$CSR_DIR/${vhost}.csr" >/dev/null || error "$CSR_DIR/${vhost}.csr is invalid" [ ! -f "${CSR_FILE}" ] && error "${CSR_FILE} absent"
debug "Using CSR file: $CSR_DIR/${vhost}.csr" [ ! -r "${CSR_FILE}" ] && error "${CSR_FILE} is not readable"
x509_verify "${CSR_FILE}" || error "${CSR_FILE} is invalid"
# Hook for evoadmin-web in cluster mode : check master status # Hook for evoadmin-web in cluster mode : check master status
if [ -f "/home/${vhost}/state" ]; then evoadmin_state_file="/home/${VHOST}/state"
grep -q "STATE=master" "/home/${vhost}/state" || exit 0 if [ -f "${evoadmin_state_file}" ]; then
grep -q "STATE=master" "${evoadmin_state_file}"
if [ "$?" != 0 ]; then
debug "We are not the master of this evoadmin cluster. Quit!"
exit 0
fi
fi fi
if [ -n "$SSL_EMAIL" ]; then #### INIT OR RENEW?
emailopt="-m $SSL_EMAIL"
else
emailopt="--register-unsafely-without-email"
fi
DATE=$(date "+%Y%m%d") LIVE_DIR="${CRT_DIR}/${VHOST}/live"
[ ! -n "$DATE" ] && error "invalid date" LIVE_CERT="${LIVE_DIR}/cert.crt"
LIVE_FULLCHAIN="${LIVE_DIR}/fullchain.pem"
LIVE_CHAIN="${LIVE_DIR}/chain.pem"
# If live symlink already exists, it's not our first time...
# If live link already exists, it's not our first time... if [ -h "${LIVE_DIR}" ]; then
if [ -h "$CRT_DIR/${vhost}/live" ]; then # we have a live symlink
openssl x509 -noout -modulus -in "$CRT_DIR/${vhost}/live/cert.crt" >/dev/null || error "$CRT_DIR/${vhost}/live/cert.crt is invalid" # let's see if there is a cert to renew
x509_verify "${LIVE_CERT}" || error "${LIVE_CERT} is invalid"
# Verify if our certificate will expire # Verify if our certificate will expire
crt_end_date=$(openssl x509 -noout -enddate -in "$CRT_DIR/${vhost}/live/cert.crt" | cut -d= -f2) crt_end_date=$(x509_enddate "${LIVE_CERT}" | cut -d= -f2)
date_renew=$(date -ud "$crt_end_date - $SSL_MINDAY days" +"%s") date_renew=$(date -ud "${crt_end_date} - ${SSL_MINDAY} days" +"%s")
date_today=$(date +'%s') date_today=$(date +'%s')
[ "$date_today" -lt "$date_renew" ] && debug "Cert $CRT_DIR/${vhost}/live/cert.crt expires at $crt_end_date => more than $SSL_MINDAY days: thxbye." && exit 0 if [ "${date_today}" -lt "${date_renew}" ]; then
debug "Cert ${LIVE_CERT} expires at ${crt_end_date} => more than ${SSL_MINDAY} days: kthxbye."
exit 0
fi
else else
which apache2ctl >/dev/null && mkconf_apache # We don't have a live symlink yet
which nginx >/dev/null && mkconf_nginx # Let's start from scratch and configure our web server(s)
if [ -n "${APACHE2CTL_BIN}" ]; then
change_cert_path_for_apache "${VHOST}" "${LIVE_FULLCHAIN}"
fi
if [ -n "${NGINX_BIN}" ]; then
change_cert_path_for_nginx "${VHOST}" "${LIVE_FULLCHAIN}"
fi
fi fi
# renew certificate with certbot #### CERTIFICATE CREATION WITH CERTBOT
[ -d "$CRT_DIR/${vhost}/${DATE}" ] && error "$CRT_DIR/${vhost}/${DATE} directory already exists, remove it manually."
mkdir -pm 755 "$CRT_DIR/${vhost}/${DATE}" ITERATION=$(date "+%Y%m%d")
chown -R acme: "$CRT_DIR/${vhost}/${DATE}" [ -z "${ITERATION}" ] && error "invalid iteration (${ITERATION})"
[ "$CRON" = "YES" ] && CERTBOT_OPTS="--quiet"
sudo -u acme certbot certonly $CERTBOT_OPTS --webroot --csr "$CSR_DIR/${vhost}.csr" --webroot-path "$ACME_DIR" -n --agree-tos --cert-path="$CRT_DIR/${vhost}/${DATE}/cert.crt" --fullchain-path="$CRT_DIR/${vhost}/${DATE}/fullchain.pem" --chain-path="$CRT_DIR/${vhost}/${DATE}/chain.pem" "$emailopt" --logs-dir "$LOG_DIR" 2>&1 | grep -v "certbot.crypto_util" NEW_DIR="${CRT_DIR}/${VHOST}/${ITERATION}"
[ -d "${NEW_DIR}" ] && error "${NEW_DIR} directory already exists, remove it manually."
mkdir -pm 755 "${NEW_DIR}"
chown -R acme: "${NEW_DIR}"
debug "New cert will be created in ${NEW_DIR}"
NEW_CERT="${NEW_DIR}/cert.crt"
NEW_FULLCHAIN="${NEW_DIR}/fullchain.pem"
NEW_CHAIN="${NEW_DIR}/chain.pem"
CERTBOT_MODE=""
[ "$TEST" = "YES" ] && CERTBOT_MODE="${CERTBOT_MODE} --test-cert"
[ "$CRON" = "YES" ] && CERTBOT_MODE="${CERTBOT_MODE} --quiet"
[ "$DRY_RUN" = "YES" ] && CERTBOT_MODE="${CERTBOT_MODE} --dry-run"
CERTBOT_REGISTRATION="--agree-tos"
if [ -n "${SSL_EMAIL}" ]; then
debug "Registering at certbot with ${SSL_EMAIL} as email"
CERTBOT_REGISTRATION="${CERTBOT_REGISTRATION} -m ${SSL_EMAIL}"
else
debug "Registering at certbot without email"
CERTBOT_REGISTRATION="${CERTBOT_REGISTRATION} --register-unsafely-without-email"
fi
# create a certificate with certbot
sudo -u acme ${CERTBOT_BIN} \
certonly \
${CERTBOT_MODE} \
${CERTBOT_REGISTRATION} \
--non-interactive \
--webroot \
--csr "${CSR_FILE}" \
--webroot-path "${ACME_DIR}" \
--cert-path "${NEW_CERT}" \
--fullchain-path "${NEW_FULLCHAIN}" \
--chain-path "${NEW_CHAIN}" \
--logs-dir "$LOG_DIR" \
2>&1 \
| grep -v "certbot.crypto_util"
# verify if all is right # verify if all is right
openssl x509 -noout -modulus -in "$CRT_DIR/${vhost}/${DATE}/cert.crt" >/dev/null || error "new $CRT_DIR/${vhost}/${DATE}/cert.crt is invalid" x509_verify "${NEW_CERT}" || error "${NEW_CERT} is invalid"
openssl x509 -noout -modulus -in "$CRT_DIR/${vhost}/${DATE}/fullchain.pem" >/dev/null || error "new $CRT_DIR/${vhost}/${DATE}/fullchain.pem is invalid" x509_verify "${NEW_FULLCHAIN}" || error "${NEW_FULLCHAIN} is invalid"
openssl x509 -noout -modulus -in "$CRT_DIR/${vhost}/${DATE}/chain.pem" >/dev/null || error "new $CRT_DIR/${vhost}/${DATE}/chain.pem is invalid" x509_verify "${NEW_CHAIN}" || error "${NEW_CHAIN} is invalid"
#### CERTIFICATE ACTIVATION
# link dance # link dance
[ -h "$CRT_DIR/${vhost}/live" ] && rm "$CRT_DIR/${vhost}/live" if [ -h "${LIVE_DIR}" ]; then
ln -s "$CRT_DIR/${vhost}/${DATE}" "$CRT_DIR/${vhost}/live" rm "${LIVE_DIR}"
openssl x509 -noout -modulus -in "$CRT_DIR/${vhost}/live/cert.crt" >/dev/null || error "new $CRT_DIR/{vhost}/live/cert.crt is invalid" debug "Remove ${LIVE_DIR} link"
fi
ln -s "${NEW_DIR}" "${LIVE_DIR}"
debug "Link ${NEW_DIR} to ${LIVE_DIR}"
# verify final path
x509_verify "${LIVE_CERT}" || error "${LIVE_CERT} is invalid"
# reload apache or nginx # disable error catching
# below this point anything can break
set +e set +e
pidof apache2 >/dev/null
if [ "$?" -eq 0 ]; then # reload apache if present
apache2ctl -t 2>/dev/null if [ -n "$(pidof apache2)" ]; then
${APACHE2CTL_BIN} -t 2>/dev/null
if [ "$?" -eq 0 ]; then if [ "$?" -eq 0 ]; then
debug "Apache detected... reloading" && service apache2 reload debug "Apache detected... reloading"
service apache2 reload
else else
error "Apache config is broken, you must fix it !" error "Apache config is broken, you must fix it !"
fi fi
fi fi
pidof nginx >/dev/null
if [ "$?" -eq 0 ]; then # reload nginx if present
nginx -t 2>/dev/null if [ -n "$(pidof nginx)" ]; then
${NGINX_BIN} -t 2>/dev/null
if [ "$?" -eq 0 ]; then if [ "$?" -eq 0 ]; then
debug "Nginx detected... reloading" && service nginx reload debug "Nginx detected... reloading"
service nginx reload
else else
error "Nginx config is broken, you must fix it !" error "Nginx config is broken, you must fix it !"
fi fi