Florent Peterschmitt

Let's Encrypt, Certbot et Naemon

Ou comment automatiser le renouvellement des certificats avec la supervision.

Naemon est simple donc pour mon petit serveur, pourquoi pas.

En fait peu importe la méthode de supervision : il s’agit en fait de déclencher la génération des certificats en peu en avance, sur détection de l’expiration.

Alors, oui, je sais qu’on peut simplement demander à Certbot de mettre à jour et il va se débrouiller pour savoir si le certificat est sur le point d’expirer. Mais autant les graphes c’est cool, et je m’en fiche, autant un truc qui ne va se mettre en route que quand il y en a vraiment besoin, c’est d’autant plus cool =D

Principe

  • On met de la sup sur son certificat. Chez moi la vérification passe par un plugin qui check directement le service, ici un nginx
  • On configure un event handler sur cette sup
  • L’event handler va lancer certbot, via sudo et relancer les services qui ont besoin de prendre en compte les nouveaux certificats

Conf

/etc/sudoers.d/naemon

Defaults    !requiretty
naemon      ALL=(ALL) NOPASSWD: /opt/cert-renew.sh

/etc/naemon/conf.d/sup.cfg

define command {
  command_name          evth_sudo_cert_renew
  command_line          /opt/evth-cert-renew.sh $SERVICESTATE$
}

define service {
  name                  certbot-domain
  use                   generic-service
  max_check_attempts    1
  check_interval        720 ; conformement a la doc certbot, on check toutes les 12 heures
  event_handler_enabled 1
  event_handler         evth_sudo_cert_renew
  register              0
}

define service {
  service_description   Web: https://peterschmitt.fr
  host_name             localhost
  use                   local-service
  check_command         check_http!-u / -H peterschmitt.fr -S -e 403
}

define service {
  service_description   x509: peterschmitt.fr
  host_name             localhost
  use                   certbot-domain
  check_command         check_ssl_expiry!peterschmitt.fr -p 443 -c 3 -w 4 ; la commande qui vous plaira pour la verif des certificats
}

define servicedependency {
  host_name                     localhost
  service_description           Web: https://peterschmitt.fr

  dependent_host_name           localhost
  dependent_service_description x509: peterschmitt.fr

  execution_failure_criteria    p,w,c,u
}

Scripts

/opt/evth-cert-renew.sh

#!/bin/sh
#echo "evth $0: args: $*" >> /tmp/evth
case "$1" in
    CRITICAL)
        #echo "evth $0: cert-renew.sh" >> /tmp/evth
        sudo /opt/cert-renew.sh 2>&1 >> /tmp/evth
        #echo "evth $0: rcode: $?" >> /tmp/evth
        ;;
esac
exit 0

/opt/cert-renew.sh

#!/bin/sh
certbot renew --force-renewal
cat /etc/letsencrypt/live/peterschmitt.fr/privkey.pem > /etc/nginx/ssl/peterschmitt.fr.key.pem
cat /etc/letsencrypt/live/peterschmitt.fr/fullchain.pem > /etc/nginx/ssl/peterschmitt.fr.crt.pem
systemctl restart nginx

Pour les tests on pourra ajouter --dry-run à la commande certbot renew pour, il me semble, éviter de consommer le nombre max d’essais par jour.

Améliorations possibles

Ici, dès la première erreur, on lance la génération et ça sera seulement au prochain check qu’on saura si tout va bien.

On pourrait plutôt mettre deux retry, se servir de l’état SOFT pour déclencher le renouvellement, et si lors du prochain check on passe en HARD, ce qui veut dire que le renouvellement a échoué, on envoie une notification.

Pour faire ça il faudra se servir de la macro $SERVICESTATETYPE$ dans le script de l’event handler.