== new certificate == /usr/bin/certbot --nginx certonly -d yourdomain.com --deploy-hook /usr/local/sbin/push_to_confiared.sh == add new for nginx == ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; listen 443 ssl http2; listen [::]:443 ssl http2; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; == renew for apache == /usr/bin/certbot --apache renew --deploy-hook /usr/local/sbin/push_to_confiared.sh --post-hook "/etc/init.d/apache2 reload" == renew for nginx == /usr/bin/certbot --nginx renew --deploy-hook /usr/local/sbin/push_to_confiared.sh --post-hook "/etc/init.d/nginx reload" == /usr/local/sbin/push_to_confiared.sh to push for IPv4 reverse proxy == #!/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 == cron == 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