Certbot, Nginx, and me

Nginx is the reverse-proxy for nearly all content I host. It lives on its own VM, and I use certbot - Let's Encypt's CLI Tool - to secure all urls that I host.


NGINX Configuration

NGINX Server Configuration

While I've read that the sites-enabled/sites-available configuration is deprecated, it still functions, and provides a sane way to maintain configuration files before placing them live. Here's the example of my nginx.conf file:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
	server_names_hash_bucket_size 64;
	include /etc/nginx/sites-enabled/*;
}

Nginx Site Configuration

Each domain gets its own site configuration. You'll see that certbot is already doing its thing. Certbot's nginx plugin maintains the lines it comments on. Here is what the configuration file for this blog looks like:

server {
    if ($host = blog.noloc.net) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen       80;
    server_name  blog.noloc.net;

    location / {
            proxy_pass http://192.168.1.3;
        }
    
    location ^~ /.well-known/acme-challenge/ {
 
	default_type "text/plain";
    	root /letsencrypt/www;
}

}

server {
    listen       443 ssl;
    server_name  blog.noloc.net;
    ssl_certificate /letsencrypt/certs/live/blog.noloc.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /letsencrypt/certs/live/blog.noloc.net/privkey.pem; # managed by Certbot


    location / {
            proxy_pass http://192.168.1.3;
            proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header        X-Forwarded-Proto  https;
    	    proxy_set_header        X-Forwarded-Server $host;
    	    proxy_set_header        X-Forwarded-Host   $host:443;
        }


}

This configuration file is in /etc/nginx/sites-available/blog.noloc.net, and is symbolically linked to /etc/nginx/sites-enabled/

ln -s /etc/nginx/sites-available/blog.noloc.net /etc/nginx/sites-enabled/blog.noloc.net

Certbot configuration

Certbot allows for a cli.ini file to be created that defines how all future interactions with the utility work.

One thing to note is that there are several integrators of Let's Encrypt, offering different functionality and/or limits from Let's Encrypt. I use ZeroSSL, specifically for the higher rate limit for certificate re-creation. If you are tearing down your infrastructure and needing new certificates for the same domain, you'll likely run into Let's Encrypt's limits quickly.

Create cli.ini

I placed the cli.ini file at the Let's Encrypt root (/etc/letsencrypt), but you can do it wherever. I have all certificates created on a custom directory (/letsencrypt); the example below shows this custom directory.

# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Certbot with
# "--help" to learn more about the available options.
#
# Note that these options apply automatically to all use of Certbot for
# obtaining or renewing certificates, so options specific to a single
# certificate on a system with several certificates should not be placed
# here.

# Use ECC for the private key
key-type = ecdsa
elliptic-curve = secp384r1
config-dir = /letsencrypt/certs/
cert-path = /letsencrypt/certs/
key-path = /letsencrypt/certs/
fullchain-path = /letsencrypt/certs/

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

# Uncomment and update to register with the specified e-mail address
email = [email protected]

# Uncomment to use the standalone authenticator on port 443
# authenticator = standalone

# Uncomment to use the webroot authenticator. Replace webroot-path with the
# path to the public_html / webroot folder being served by your web server.
authenticator = nginx
webroot-path = /letsencrypt/www
# Uncomment to automatically agree to the terms of service of the ACME server
agree-tos = true

# An example of using an alternate ACME server that uses EAB credentials
server = https://acme.zerossl.com/v2/DV90
eab-kid = {ObtainFromProvider}
eab-hmac-key = {ObtainFromProvider}

After creating the file, run the following command to ensure certbot uses it:

certbot --config /path/to/cli.ini

Request a certificate

Requesting a certificate for an existing nginx site can be done with the following command:

certbot run -d blog.noloc.net --nginx

Configuring AutoRenewal

Let's Encrypt certificates last for 90 days, so we need to have a process to renew them. I use Amazon Linux 2023 in my infrastructure, which deprecated cron, so we use systemctl and timers. Here's how I created the auto-renewal systemctl timer for certbot:

Create three files:

  1. certbot-renewal.service
  2. certbot-renewal.timer
  3. certbot-renewal-installer.sh

Set the content of certbot-renewal.service to the following:

# /etc/systemd/system/scripts/setup-systemctl.sh
[Unit]
Description=Periodic Renewal for certbot 
StartLimitIntervalSec=60s
StartLimitBurst=5

[Service]
EnvironmentFile=-/etc/sysconfig/cert-sync
ExecStart=/usr/bin/certbot renew
TimeoutStartSec=60s


[Install]
WantedBy=multi-user.target

Set the content of certbot-renewal.timer to the following:

# /etc/systemd/system/certbot-renewal.timer
[Unit]
Description="Periodic Renewal for certbot"

[Timer]
# Runs 'certbot-renewal.service' every 1 hour.
OnCalendar= *:0:00
# Explicitly declare service that this timer is responsible for
Unit=certbot-renewal.service

[Install]
WantedBy=timers.target

Set the contents of certbot-renewal-installer.sh to the following:

touch /etc/systemd/system/certbot-renewal.{timer,service}
cp certbot-renewal.service /etc/systemd/system/certbot-renewal.service
cp certbot-renewal.timer /etc/systemd/system/certbot-renewal.timer
systemctl daemon-reload
systemctl enable certbot-renewal.service
systemctl start certbot-renewal.service
systemctl enable certbot-renewal.timer
systemctl start certbot-renewal.timer

This script assumes that both the certbot-renewal.service and certbot-renewal.timer files are in your current directory, so adjust lines 2 and 3 if necessary.

Change the permissions of certbot-renewal-installer.sh to include execute:

chmod +x certbot-renewal-installer.sh

Run the installer script:

sudo ./certbot-renewal-installer.sh

If you're ever in need of renewal logs, consult jourtnalctl:

journalctl -u certbot-renewal.service -r