#!/bin/bash
#RENEWED_LINEAGE=/etc/letsencrypt/live/site.com
# Pushes a renewed certificate to the Confiared reverse-proxy, so the edges that terminate
# IPv4 stop serving the previous one. Called by certbot as renew_hook/deploy_hook.
# --insecure is deliberate: this can run while the certificate of api.confiared.com itself
# is the one being repaired.
if [ ! -f "${RENEWED_LINEAGE}/cert.pem" ]
then
echo "${RENEWED_LINEAGE}/cert.pem was not found, abort" > /var/log/last_letsencrypt_confiared_api.log
echo "${RENEWED_LINEAGE}/cert.pem was not found, abort" >> /var/log/last_letsencrypt_confiared_api.err
exit 255
fi
# The api answers 400 + a json explaining why when it refuses a certificate. curl exits 0 on
# a 400, so testing $? alone reports success for a certificate that was in fact rejected: that
# is how a stale certificate stayed on the edges until it expired. Test the http code instead.
# Not -f: -f would throw away the json body, which is the only place the reason is written.
#
# The class of the code is what says whether retrying can ever help:
# 200 accepted, nothing more to do
# 4xx the api understood the request and refused it. The refusal only depends on the three
# files posted (expired, self signed, private key not matching the certificate...), so
# posting the same three again can only be refused again. Retrying anyway is what turned
# a single expired lineage into 100 refusals a day for three months: 06:09 to 22:39 every
# day, which is exactly 1 push plus 99 sleeps of 600s.
# other 000 when curl could not reach the api at all, or a 5xx: that one can heal by itself.
push()
{
local code
code=$(/usr/bin/curl --insecure --silent --max-time 120 -w '%{http_code}' \
--data-urlencode "certificate=$(cat ${RENEWED_LINEAGE}/cert.pem)" \
--data-urlencode "chain=$(cat ${RENEWED_LINEAGE}/chain.pem)" \
--data-urlencode "privatekey=$(cat ${RENEWED_LINEAGE}/privkey.pem)" \
https://api.confiared.com/reverse-proxy/upload-certificate \
-o /var/log/last_letsencrypt_confiared_api.log)
case "$code" in
200)
return 0
;;
4??)
# said at the first attempt and not 16h later, and the reason is copied here because the
# .log above is overwritten by the next certificate this host pushes
echo "`date -Is` ${RENEWED_LINEAGE} refused with http ${code}, nothing to retry: $(head -c 300 /var/log/last_letsencrypt_confiared_api.log | tr '\n' ' ')" >> /var/log/last_letsencrypt_confiared_api.err
return 2
;;
*)
return 1
;;
esac
}
push
case $? in
0) exit 0;;
2) exit 1;;
esac
# The api did not answer, or answered 5xx. Only that is worth retrying, for ~16h.
for i in {1..99}
do
sleep 600
push
case $? in
0) exit 0;;
2) exit 1;;
esac
done
echo "`date -Is` ${RENEWED_LINEAGE} still not pushed after 99 tries, see /var/log/last_letsencrypt_confiared_api.log" >> /var/log/last_letsencrypt_confiared_api.err
exit 1
0 3 * * * sleep ${RANDOM:0:3}m;[ `ps aux | grep nginx | grep -v -F grep | wc -l` -gt 0 ] && /usr/bin/certbot --nginx renew --deploy-hook /usr/local/sbin/push_to_confiared.sh --post-hook "/etc/init.d/nginx reload" > /var/log/letsencrypt.log 2>&1
0 3 * * * sleep ${RANDOM:0:3}m;[ `ps aux | grep apache2 | grep -v -F grep | wc -l` -gt 0 ] && /usr/bin/certbot --apache renew --deploy-hook /usr/local/sbin/push_to_confiared.sh --post-hook "/etc/init.d/apache2 reload" > /var/log/letsencrypt.log 2>&1