4

The certbot command provides two hooks that run after automated renewals, from the docs:

 --post-hook POST_HOOK
                       Command to be run in a shell after attempting to
                       obtain/renew certificates. Can be used to deploy
                       renewed certificates, or to restart any servers that
                       were stopped by --pre-hook. This is only run if an
                       attempt was made to obtain/renew a certificate. If
                       multiple renewed certificates have identical post-
                       hooks, only one will be run. (default: None)
 --deploy-hook DEPLOY_HOOK
                       Command to be run in a shell once for each
                       successfully issued certificate. For this command, the
                       shell variable $RENEWED_LINEAGE will point to the
                       config live subdirectory (for example,
                       "/etc/letsencrypt/live/example.com") containing the
                       new certificates and keys; the shell variable
                       $RENEWED_DOMAINS will contain a space-delimited list
                       of renewed certificate domains (for example,
                       "example.com www.example.com" (default: None)

This issue is outlined in this (now closed) LE thread and is basically about minimising interruption to services. POST_HOOK executes every time an attempt to renew is made even if no certificates were issued, though only once. This makes it possible to unnecessarily restart services. DEPLOY_HOOK runs for each and every successful certificate renewal. If one uses DEPLOY_HOOK, and has multiple certificates, each service may restart multiple times when once is enough. More info on renewal hooks here.

I use an issuance method that does not interrupt my services at all, e.g.:

certbot certonly --webroot ...

or

certbot certonly --dns-PROVIDER ...

I want to restart/reload each dependent service only once, and only if its certificate actually changed.

Walf
  • 501

2 Answers2

6

I was able to overcome this limitation by using both hooks in combination with a simple script (published in this Gist). Instead of each successful renewal immediately triggering a reload/restart, its --deploy-hook/renew_hook only marks the service for an action (by creating temporary files under /run). Then when the --post-hook/post_hook runs, it checks for marked services and performs the necessary actions only once. This guarantees that nothing happens to a service unless a certificate it depends on actually changed, and it only happens once.

For example, to reload nginx, and restart vsftpd when a certificate they share is renewed:

certbot certonly ... \
    --deploy-hook '/usr/local/sbin/read-new-certs-services nginx --restart vsftpd' \
    --post-hook /usr/local/sbin/read-new-certs-services

Another certificate may be web-only, so it would have these hook parameters:

certbot certonly ... \
    --deploy-hook '/usr/local/sbin/read-new-certs-services nginx' \
    --post-hook /usr/local/sbin/read-new-certs-services

The script must be used on both hooks for every certificate issue so that none use a conflicting method of restarting services.

You can change existing certificate renewals to use this method by editing their /etc/letsencrypt/renewal/*.conf files to contain hooks like this in the [renewalparams] section:

renew_hook = /usr/local/sbin/read-new-certs-services nginx -s vsftpd
post_hook = /usr/local/sbin/read-new-certs-services
Walf
  • 501
0

I'll leave this answer because this question was top of my search results and I found a different solution which works for me, but it doesn't fulfill the question's requirement of

only if the cerificate was actually renewed

so any downvotes will be well-deserved.

Background

I use the Hitch TLS proxy to access Home Assistant from outside. I have certbot to renew the certificates.

The certbot service is inactive until the certbot.timer starts it.

Solution

I ran systemctl edit certbot.service which creates the file /etc/systemd/system/certbot.service.d/override.conf.
Inside that file I put the lines:

[Unit]
Conflicts=hitch.service
OnSuccess=hitch.service
OnFailure=hitch.service

Conflicts is a list of unit names to stop before the service will run. OnSuccess and OnFailure contains a list of unit names to start after the service has run.

So, Hitch is stopped before running certbot and then started afterwards. The downtime is about two seconds.

I preferred this solution for my problem because it's only a handful of lines in an override file, which will get backed up as part of /etc, rather than a script in /usr/local/sbin which I'll forget to copy the next time I upgrade the Raspberry Pi running Home Assistant ;-)

Aaron F
  • 101