Published on

Renewing Let's Encrypt SSL Certificate Automatically: A Smarter Approach

Authors
  • avatar
    Name
    Ang Yi Shuan
    Twitter

Renewing Let's Encrypt SSL Certificate Automatically: A Smarter Approach

Let's Encrypt SSL certificates play a crucial role in securing websites, and timely renewal is essential to maintain security. In this post, I'll walk you through a smarter approach to automate the renewal process efficiently.

Setting Up Automatic Renewal

Let's start by examining the conventional approach recommended by Let's Encrypt documentation, which will add a cron job to the default crontab:

echo "0 0,12 * * * root $(command -v python3) -c 'import random; \
import time; time.sleep(random.random() * 3600)' && \
sudo $(command -v certbot) renew -q" | \
sudo tee -a /etc/crontab > /dev/null

While this cron job checks for renewal twice a day, it's inefficient since Let's Encrypt only allows renewals within 30 days of expiry. Since the SSL certificates by Let's Encrypt are valid for 90 days, that's 90 - 30 = 60 days of non-action!

Side note: certbot renew will first check if the certificate is due for renewal. If not, it does nothing.

A Better Solution

To optimize the renewal process, I've developed a Python script aux_check_cert_expiry.py. Instead of following the conventional recommendation of checking for renewal twice a day, the solution aims to call certbot renew to renew the certificate only when necessary, i.e., when it's within 30 days of expiry. This optimization minimizes resource usage and ensures efficient management of the renewal process.

aux_check_cert_expiry.py
import ssl
import socket
from datetime import datetime

def check_ssl_expiry(hostname):
    """
    Check the SSL certificate expiration date for a given hostname.

    Args:
        hostname (str): The hostname of the website to check.

    Returns:
        bool: True if the SSL certificate is expiring within 30 days, False otherwise.
    """
    context = ssl.create_default_context()

    with socket.create_connection((hostname, 443)) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert()
            expiration_date_str = cert['notAfter']
            expiration_date = datetime.strptime(expiration_date_str, "%b %d %H:%M:%S %Y %Z").replace(tzinfo=None)
            days_until_expiry = (expiration_date - datetime.now()).days

            return days_until_expiry < 31

if __name__ == '__main__':
    website_hostname = 'shuanang.ddns.net'
    should_renew = check_ssl_expiry(website_hostname)
    print(should_renew)

This script checks if the SSL certificate is expiring within 30 days.

Next, let's look at the shell script aux_renew_cert_certbot.sh:

aux_renew_cert_certbot.sh
#!/bin/zsh
cd /Users/angyishuan/Dropbox/Telegram\ Bot/
PASS=$(security find-generic-password -l "root password" -a root -w|tr -d '\n')

check_ssl_expiry() {
    python_script_output=$(python aux_check_cert_expiry.py)
    if [ "$python_script_output" = "True" ]; then
        echo "SSL certificate has less than 31 days of expiry. Proceeding with renewal."
        return 0
    else
        echo "SSL certificate has more than 31 days of expiry. Skipping renewal."
        return 1
    fi
}

# Check SSL certificate expiry and execute last three lines conditionally
check_ssl_expiry
if [ $? -eq 0 ]; then
	brew services stop nginx
	echo "$PASS" | sudo -S /opt/homebrew/bin/certbot renew 
	brew services restart nginx
    sleep 5
    brew services restart nginx  # first restart seems to not work properly
fi

This script incorporates the Python script to determine whether renewal is needed and then proceeds with the renewal process if necessary.

Automating Renewal

Finally, I've set up a cron job to automate the renewal process:

59 5 * * 6-7 osascript -e 'tell app "Terminal" to do script "source /Users/angyishuan/Dropbox/Telegram[[:space:]]Bot/aux_renew_cert_certbot.sh;exit" activate'

This cron job runs the shell script on Saturdays and Sundays at 5:59 AM, ensuring timely renewal without unnecessary executions.

With this approach, SSL certificate renewal becomes a streamlined and efficient process, ensuring continuous security for your website.