Let’s Encrypt SSL Certificates
Let’s Encrypt provides users with free SSL certificates with a limitation that the certificates have to be renewed every 90 days. Because of this limitation it is recommended that an automated renewal process be utilized to automate the renewal process. ACME Clients that are recommended for obtaining and renewing certificat include CertBot or acme.sh.
Let’s Encrypt Validation Process
Let’s Encrypt requires user validation when issuing and renewing certificates. Let’s Encrypt needs to validate that control of the domain(s) for which are requesting certificates. Let’s Encrypt authenticates using challenges. “Challenge Types” include either HTTP challenge of DNS challenge.
HTTP challenge requires a file to be temporarily written/read on the requesting webserver. DNS challenge requires a temporary TXT record be added to your domain’s DNS records. Various DNS providers are compatible with Let’s Encypt. Of these two methods, I have found DNS-challenge to be more robust.
This guide will provide instructions on how to obtain SSL certificates through Let’s Encrypt. The DNS-challenge method will be used with Cloudflare as the DNS provider. acme.sh will be utilized as the ACME client. Additionally example systemd timer and service files will be provided if users would like to use this method to automated system renewals. Crontab or Fcron entries could be utilized as alternatives.
Let’s Encrypt Validation with DNS Challenge
For each domain requiring validaton, a corresponding A or CNAME record is required. Usually the base level domain is considered the A record. The A record is linked to an IP address. The CNAME records are linked to the A record. If utilizing a setup where one WAN IP Address is hosting multiple domains/subdomains, usually the A record is the basedomain and the CNAME recods are the subdomains of the basedomain which point to the A record and the same IP addresss.
For example:
| Type | Name | Content |
|---|---|---|
| A | example.org | 555.555.555.555 |
| CNAME | subdomain1.example.org | example.org |
| CNAME | subdomain2.example.org | example.org |
For every domain or subdomain that requires validation, there must be an A or CNAME entry.
For the domains listed above, it is possible to:
- Obtain 3 separate SSL certificates. Each SSL certificate contains only the name of itsh respective domain.
- Obtain 1 SSL certificate which contains the name of all three domains within the same certificate
Some refer to option 2 as obtaining a “SAN Certificate”. Whether you obtain multiple certificates or one certificate associated with multiple domains is a matter of personal preference.
If following this guide for Authelia setup, you must at least have either 2 certs, or 1 cert with 2 listed domains. The domains should be <domain.com> and authelia.<domain.com>
Please note that each subdomain does not need to be publicly accessible. It is possible to use subdomain2.example.org internally and for this particular domain to never be reachable from outside the Local Area Network.
Acme.sh Setup
– ROOT WARNING: Please perform commands listed below as the root user (su/sudo)
– VARIABLE SUBSTITUTION WARNING: Please substitute all enclosed <varible_names> with the appropriate value. For example: <email_address> would become joe@gmail.com
Official acme.sh installation instructions.
apt install git
pacman -S git cronie
$ cd ~
$ git clone https://github.com/acmesh-official/acme.sh.git
$ cd ~/acme.sh
$ ./acme.sh —upgrade —auto-upgrade
$ ./acme.sh —install —accountemail <email_address>
$ cd ~/.acme.sh
Acme.sh and Cloudflare
When working with Cloudflare, acme.sh requires either:
- The Cloudflare Global API Key Option —-OR—-
- The Cloudflare API Token Method Option
| Cloudflare Global API Key Option | Cloudflare API Token Method Option |
|---|---|
| Cloudflare Key -> CF_KEY | Cloudflare Token -> CF_Token |
| Cloudflare Email -> CF_EMAIL | Cloudflare Account ID -> CF_Account_ID |
You need criteria from either of the two columns. The Cloudfare Global API Key Option is the Legacy Option whereas the Cloudflare API Token Method Option is considered the newer option as options may be more finely tuned per user app.
When logged in Cloudflare and after selecting your particular domain, these criteria can be found at the following locations:
The Cloudflare Key is also known as the Cloudflare Global API Key. This is found under the My Profile -> API Tokens -> Global API Key Menu.
The Cloudflare Email is the email that was used when registering with CloudFlare when the account was created.
The Cloudflare Account ID can be found either:
- At the Bottom Right Column under “Account ID” when visiting the Overview Page for the specific DOMAIN
- Within the Browser URL bar – It the long alphanumeric code listed between https://dash.cloudflare.com/abkdkd-long-alpha-numeric-code-here-akk23939/<domain_name>
The Cloudflare Token Needs to be Generated:
- Goto My Profile -> API Tokens
- Select Create Token
- Give the Token a Name
- 2 Permissions are Needed:
- Zone Zone Read
- Zone DNS Edit
- Zone Resources - Include All Zones
- Copy the resultant long number given as it will not be able to retrieved again
You will need to export either pair of criteria as environmental variables for acme.sh to utilize their values.
$ export CF_Key="<Cloudflare Global API Key>"
$ export CF_Email="<Cloudflare Email>"
$ export CF_Token="<Cloudflare Token>"
$ export CF_Account_ID="<Cloudflare Account ID>"
Let’s Encrypt Certificate Generation with acme.sh and DNS Challenge with Cloudflare
For illustrative purposes, the Let’s Encrypt Certificates will be stored in /etc/letsencrypt/<domain_name>. Please modify instructions below for your setup.
$ export LE_DIR="/etc/letsencrypt/<domain_name>"
$ mkdir -p ${LE_DIR}
$ cd ~/.acme.sh
$ ./acme.sh --issue --dns dns_cf -d <domain_name>
$ ./acme.sh --install-cert -d <domain_name> \
> --cert-file ${LE_DIR}/cert.pem \
> --ca-file ${LE_DIR}/chain.pem \
> --key-file ${LE_DIR}/privkey.pem \
> --fullchain-file ${LE_DIR}/fullchain.pem \
> --reloadcmd "<reload command>"
The reloadcmd is optional. In many cases however after renewing certificates you may want to reload an application utilizing the certificates. In this particular application, ngnix is the reverse proxy utilizing the certifcates for the various domains. To automatically reload the new certificates within Nginx, the reloadcmd would be --reloadcmd "nginx -s reload". If multiple reload commands are needed, put them in a shell script and have the reloadcmd call the shell script.
Let’s Encrypt Certificate Auto Renewal
By default, acme.sh will create an entry into the root crontab. Certificate renewal will be attempted nightly. The crontab entry will appear similar to:
$ crontab -l #<---(Use fcrontab -l if using fcron as cron implementation rather than vixie,cronie)
2020-03-18 01:51:47 INFO listing root's fcrontab
22 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
Optional Renewal Implementation with Systemd Timers rather than using crontab
I’ve personally attempted to convert most of my crontab entries into corresponding systemd unit files and timers.
I first commented out the acme entry contained within the crontab file, and then created two files:
- acme_letsencrypt@.service
- acme-letsencrypt@.timer
Both these files for me were placed within /etc/systemd/system however please check your distribution for the exact location.
[Unit]
Description=Renew Let's Encrypt certificates using acme.sh for %I
After=network-online.target
[Service]
Type=oneshot
ExecStart="/root/.acme.sh"/acme.sh --home "/root/.acme.sh" --cron --issue --dns dns_cf -d %i --log
[Unit]
Description=Daily renewal of Let's Encrypt's certificates
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
With this setup, the timer will start the service file to renew the certificate(s).
Timers can be enable to start at boot and started by:
systemd enable acme_letsencrypt@<domain_name>.timer
systemd start acme_letsencrypt@<domain_name>.timer
So for example if domain_name=example.org
systemd enable acme_letsencrypt@example.org.timer
systemd start acme_letsencrypt@example.org.timer
Use systemctl list-timers to see status of timers, last run, etc.