I’ve followed the instructions from Bitnami for installing Let’s Encrypt certificate on Amazon Lightsail instance with Bitnami WordPress preloaded. The guide is very user-friendly and easy to follow. However, there is room for improvement in step 5 in certificate renewal.
Bitnami have proposed the following script for certificate renewal. It stops Apache, renew the certificate with lego, and start Apache.
#!/bin/bash sudo /opt/bitnami/ctlscript.sh stop apache sudo /usr/local/bin/lego --email="EMAIL-ADDRESS" --domains="DOMAIN" --path="/etc/lego" renew sudo /opt/bitnami/ctlscript.sh start apache
And the following entry in crontab
0 0 1 * * /etc/lego/renew-certificate.sh 2> /dev/null
There are few issue with this implementation
- Certificate update will be triggered on 00:00 on the first day of a month. If the server was not running at that time, or the Let’s Encrypt service is busy because a large number of sites have this type of implementation, the certificate renewal may fail.
- If an renewal was failed, the next renewal will be triggered in the next month.
- Apache stop before the renewal and start after
lego
. This may increase unnecessary downtime.
And here is my implementation – it will renew the certificate only if the certificate is going to expire in 7 days. Change EMAIL-ADDRESS
and DOMAIN
respectively and write to /home/bitnami/cert-update
#!/usr/bin/env ruby # encoding: utf-8 require "openssl" LE_EMAIL = "EMAIL-ADDRESS" CERT_DOMAIN = "DOMAIN" CERT_PATH = "/etc/lego/certificates/#{CERT_DOMAIN}.crt" RENEW_DAYS_BEFORE_EXPIRE = 7 raw = File.read(CERT_PATH) certificate = OpenSSL::X509::Certificate.new(raw) expire_time = certificate.not_after now = Time.now seconds_remaining = expire_time - now days_remaining = seconds_remaining/60/60/24 $stdout.puts "Current time: #{now}" $stdout.puts "Expiration: #{expire_time}" $stdout.puts "Expire in #{days_remaining.round(1)} day(s)" if days_remaining < RENEW_DAYS_BEFORE_EXPIRE $stdout.puts "Starting certificate renew" system("/opt/bitnami/ctlscript.sh stop apache") system("/usr/local/bin/lego --email='#{LE_EMAIL}' --domains='#{CERT_DOMAIN}' --path='/etc/lego' renew") system("/opt/bitnami/ctlscript.sh restart apache") else $stdout.puts "Do nothing" end
Make the file executable and install Ruby
chmod +x /home/bitnami/cert-update sudo apt-get install ruby
And run crontab -e
then append the following line. It will run the script twice a day, between 00:00~12:00 and 12:00~24:00. The perl script randomises the execution time (I learned this from the certbot) and use flock
to prevent concurrent execution.
0 */12 * * * perl -e 'sleep int(rand(43200))' && flock /home/bitnami/cert-update.lock sudo /home/bitnami/cert-update > /home/bitnami/cert-update.log 2>&1