#!/usr/bin/env bash # The home-server project produces a multi-purpose setup using Ansible. # Copyright © 2018 Y. Gablin, under the GPL-3.0-or-later license. # Full licensing information in the LICENSE file, or gnu.org/licences/gpl-3.0.txt if the file is missing. RSH=/usr/local/bin/{{DMZ}} ETC_CHANGED_{{hostname}}= ETC_CHANGED_{{DMZ}}= etckeeper_hook() { if [ -n "$ETC_CHANGED_{{hostname}}" ]; then etc_stop_local 'ACME update' fi if [ -n "$ETC_CHANGED_{{DMZ}}" ]; then $RSH "etc_stop_local 'ACME update'" fi } trap etckeeper_hook EXIT deploy_challenge() { local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" printf '%s' "$TOKEN_VALUE" >"$WELLKNOWN/$TOKEN_FILENAME" } clean_challenge() { local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" rm -f "$WELLKNOWN/$TOKEN_FILENAME" } # $1: force|test; $2: KEYFILE; $3: CERTFILE; $4: FULLCHAINFILE; $5: CHAINFILE; $6: TIMESTAMP deploy_exim() { if [ "$1" == test ] \ && $RSH 'test -f /etc/mail/exim.pem && test -f /etc/mail/exim.crt' \ && $RSH 'find /etc/mail/exim.{pem,crt} -mmin -$((1+($(date +%s)-${1})/60)) -printf . | grep -q ..' ${6%.*}; then return 0 fi local copy='cat >$1; chown exim $1; chmod 400 $1; touch -t $(date +%Y%m%d%H%M -d @$2) $1' $RSH "$copy" /etc/mail/exim.pem $6 <"$2" $RSH "$copy" /etc/mail/exim.crt $6 <"$4" systemctl -M {{DMZ}} reload exim.service ETC_CHANGED_{{DMZ}}=1 } # $1: force|test; $2: KEYFILE; $3: CERTFILE; $4: FULLCHAINFILE; $5: CHAINFILE; $6: TIMESTAMP deploy_prosody() { if [ "$1" == test ] \ && $RSH 'test -f /etc/prosody/certs/{{net_soa}}.key && test -f /etc/prosody/certs/{{net_soa}}.crt' \ && $RSH 'find /etc/prosody/certs/{{net_soa}}.{key,crt} -mmin -$((1+($(date +%s)-${1})/60)) -printf . | grep -q ..' ${6%.*}; then return 0 fi local copy='cat >$1; chown prosody $1; chmod 400 $1; touch -t $(date +%Y%m%d%H%M -d @$2) $1' $RSH "$copy" /etc/prosody/certs/{{net_soa}}.key $6 <"$2" $RSH "$copy" /etc/prosody/certs/{{net_soa}}.crt $6 <"$4" systemctl -M {{DMZ}} reload prosody.service ETC_CHANGED_{{DMZ}}=1 } # $1: force|test; $2: KEYFILE; $3: CERTFILE; $4: FULLCHAINFILE; $5: CHAINFILE; $6: TIMESTAMP deploy_haproxy() { if [ "$1" == test ] \ && $RSH 'test -f /etc/haproxy/tls.pem' \ && $RSH 'find /etc/haproxy/tls.pem -mmin -$((1+($(date +%s)-${1})/60)) -printf . | grep -q .' ${6%.*}; then return 0 fi local copy='cat >$1; chmod 400 $1; touch -t $(date +%Y%m%d%H%M -d @$2) $1' cat "$4" "$2" | $RSH "$copy" /etc/haproxy/tls.pem $6 systemctl -M {{DMZ}} reload haproxy.service ETC_CHANGED_{{DMZ}}=1 } deploy_cert() { local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}" deploy_exim force "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" deploy_prosody force "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" deploy_haproxy force "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" } unchanged_cert() { local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="$(find "$2" -printf '%T@')" deploy_exim test "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" deploy_prosody test "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" deploy_haproxy test "$KEYFILE" "$CERTFILE" "$FULLCHAINFILE" "$CHAINFILE" "$TIMESTAMP" } invalid_challenge() { local DOMAIN="${1}" RESPONSE="${2}" printf 'Failed ACME challenge for DOMAIN=%s: RESPONSE=%s\n' "$DOMAIN" "$RESPONSE" >&2 } request_failure() { local STATUSCODE="${1}" REASON="${2}" REQTYPE="${3}" printf 'Failed %s request for ACME: STATUSCODE=%s (%s)\n' "$REQTYPE" "$STATUSCODE" "$REASON" >&2 } exit_hook() { return 0 } HANDLER="$1"; shift if [[ "${HANDLER}" =~ ^(deploy_challenge|clean_challenge|deploy_cert|unchanged_cert|invalid_challenge|request_failure|exit_hook)$ ]]; then "$HANDLER" "$@" fi