This is my first attempt at renewing Let’s Encrypt certificates via Certbot. After carefully reading the Certbot user guide I created two post hook scripts like this:
[email protected]:~# ls -l /etc/letsencrypt/renewal-hooks/post total 8 -rwxr-xr-x 1 root root 697 Aug 29 16:35 10-setup-courier.sh -rwxr-xr-x 1 root root 377 Aug 29 16:32 20-restart-services.sh
I then ran the renewal process manually on the command line (i.e. not via cron). This was successful in renewing the certificates, but it failed to execute the above post hook scripts. Here is the relevant output:
[...] Running post-hook command: /etc/letsencrypt/renewal-hooks/post/10-setup-courier.sh Hook command "/etc/letsencrypt/renewal-hooks/post/10-setup-courier.sh" returned error code 127 Error output from 10-setup-courier.sh: /bin/sh: /etc/letsencrypt/renewal-hooks/post/10-setup-courier.sh: not found Running post-hook command: /etc/letsencrypt/renewal-hooks/post/20-restart-services.sh Hook command "/etc/letsencrypt/renewal-hooks/post/20-restart-services.sh" returned error code 127 Error output from 20-restart-services.sh: /bin/sh: /etc/letsencrypt/renewal-hooks/post/20-restart-services.sh: not found [...]
I have no idea why this happens. I double-checked:
- The script files exist
- The script files are executable
- I can run the scripts manually (with the environment variables
RENEWED_LINEAGEset and exported) and they do their job as expected
One other thing that I probably should mention is that I run Certbot within a Docker image because I am working with wildcard certificates. My DNS provider is Cloudflare. Here’s the command line that I am using to start the renewal process:
docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/dns-cloudflare renew
The Docker image runs Certbot version 0.25.0. The system is Debian 9 (stretch), recently upgraded from Debian 8 (jessie).
Any clues what the problem could be?
EDIT: As requested, here is the content of the two files, slightly edited to replace my domain with “example.com”:
[email protected]:~# cat /etc/letsencrypt/renewal-hooks/post/10-setup-courier.sh #!/bin/bash # Exit immediately if a command exits with non-zero status set -e case $RENEWED_DOMAINS in # Courier runs only under a example.com subdomain example.com) # We don't care about file permissions because we know that the # filesystem folder where we generate the file is not generally # accessible cat "$RENEWED_LINEAGE/fullchain.pem" "$RENEWED_LINEAGE/privkey.pem" >"$RENEWED_LINEAGE/courier.cert-and-key.unsecure" ;; esac [email protected]:~# cat /etc/letsencrypt/renewal-hooks/post/20-restart-services.sh #!/bin/bash # Exit immediately if a command exits with non-zero status set -e case $RENEWED_DOMAINS in # Courier and Exim run only under a example.com subdomain *example.com*) systemctl restart courier-imap.service systemctl restart exim4.service systemctl restart apache2.service ;; # Apache has vhosts for all domains. Unfortunately the daemon is # restarted several times if several certificates are renewed. *) systemctl restart apache2.service ;; esac
Your shell scripts use a shebang
#!/bin/bash, meaning they are to be executed with that program, but the Docker container in which they run doesn’t include bash. This is why
/bin/sh reports the confusing
not found error when calling these obviously present scripts. It is not the scripts that are not found, but the bash interpreter that you asked to run them with.
You can resolve the problem by changing the script interpreter to
/bin/sh and removing any bash-isms from the scripts (probably quick and easy), or by installing bash in the container (probably messy).
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.