Automated TLS and Nginx
If you're following the mini series this is part 2
Encrypt all the things!, it's tempting to leave TLS out when building things on your
internal network, especially at home. Self Signed certificate management is a pain and
rolling your own certificate authority internally is certainly a project but for home it can
be a bit much. Thankfully we can use an external DNS provider, acme.sh and services like
ZeroSSL and LetsEncrypt to give us certs that are already valid of most modern
browsers, operating systems and devices.
Prerequisites
This guide assumes you don't have or want a publicly reachable IP address but the same can be applied if you're running an general web server.
This guide uses Ubuntu 24.04 LTS
You'll need Git installed (and Nginx if you haven't done so)
I'm using Bunny CDN, the acme.sh project is compatible with multiple providers.
A domain! buy a domain from you preferred provider. The none real example I'll be
using for this is 'norefseclan.uk'
Your bunny API Key
Automate with acme.sh
acme.sh is an all shell toolset/project that allows us to easily manage the Automated
Certificate Management Environment (ACME). You can find the project on git.
We'll clone the repository - I put my extras in /opt because I'm old and traditional like that.
cd /opt & git clone https://github.com/acmesh-official/acme.sh.git
Get your Bunny API from the Bunny control panel and set the variable for acme.sh
export BUNNY_API_KEY=70a2xXXXXXXXXXX
Issue a cert and a wildcard cert for our internal hosts. By default acme.sh uses ZeroSSL
if you want to use a different provider you can specify one, in the sample below we
specify letsencrypt.
/acme.sh --issue --dns dns_bunny -d norefseclan.uk -d *.norefseclan.uk --server letsencrypt
You can use acme.sh to 'install' your certificates to your Nginx directories and reload
Nginx to pick up the new certs.
acme.sh --install-cert -d norefseclan.uk --key-file /etc/ssl/private/norefseclan_key.pem --fullchain-file /etc/ssl/certs/norefseclan_cert.pem --reloadcmd "service nginx force-reload"
At this point if you're following along you may not have actually have a valid Nginx config up and this will error but we'll fix that below.
Achme.sh will create a configuration file for your domain based on the input you provided
in the previous section. It's important to note that the reload command you used is
encoded in BASE64
The config should be in /root/.acme.sh/yourdomain_name/yourdomain.conf and will be
similar to below.
Le_Domain='norefseclan.uk'
Le_Alt='*.norefseclan.uk'
Le_Webroot='dns_bunny'
Le_PreHook=''
Le_PostHook=''
Le_RenewHook=''
Le_API='https://acme-v02.api.letsencrypt.org/directory'
Le_Keylength='ec-256'
Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/XXXX'
Le_ReloadCmd='__ACME_BASE64__START_c2VydCCCCCCCClueCBmb3JjZS1yZWxvYWQ=__ACME_BASE64__END_'
To test the renewal you can manually run the command, in this example the --home
value is where our config is, if you have run this as a user it will be in /home/username
instead.
/opt/acme.sh/acme.sh --cron --acme "/root/.acme.sh/"
Crontab
You can add this to crontab in order to automatically renew your certs.
33 4 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
This will run the job everyday and automate your renewals!
Nginx TLS Configuration
Firstly we need a basic server listening on 443, in the example below I have enabled
HTTP2 and included the TLS configuration in a snippet file. I've done this so we can refer
to it in multiple 'servers' if needed.
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/ssl-params.conf;
server_name norefseclan.uk www.norefseclan.uk;
}
SSL Params Snippet
Now let's create the ssl-params.conf file. SSL Labs has an excellent SSL Best Practice
Guide that goes into significant details about best Cipher Suites and Configuration for
your server.
Below is a config that will work for most browsers, it is not necessarily the
best or safest for you.
ssl_protocols TLSv1.3;
# - Choose a protocol, TLS1.2 and 1.3 is recommended
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem; #dhparam file if you need/want them
ssl_ciphers EECDH+AESGCM:EDH+AESGCM; #your chosen ciphers
ssl_ecdh_curve secp384r1; #chosen ecdh curve
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s; #resolvers for your ssl certs
resolver_timeout 5s;
Create a dhparams file. These parameters define how OpenSSL performs the Diffie-
Helman Key Exchange
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
The resolvers I've used in this case are 1.1.1.1 (cloudflare) and 8.8.8.8 (google). Change these to your preferred resolvers.