Modern Internet is full of encryption. In many ways, using encryption is
still optional, although non-encrypted communication of any form is getting
rarer every day. There are factors that contribute to this trend. As a
specific example, some top-level domains, like .app
or .dev
, the
mainstream browsers ship with the forced HTTPS mode hard-coded for these
selected TLDs.
To make HTTPS work, a browser and a webserver first need to perform a key exchange. This exchange is referred to as a TLS handshake. After a successful handshake, the browser and the webserver can communicate securely, meaning anyone eavesdropping on the communication can only see garbage, unless they can actually decrypt the communication.
For a webserver to be able to perform the TLS handshake, it needs a certificate, which is used for a public key encryption. The certificate was traditionally bought from a Certificate Authority (CA), until the non-profit CA called LetsEncrypt started providing certificates for free, so we all can have nice things (secure communication with the webserver).
ACME and Certbot
ACME stands for Automated Certificate Management Environment and provides a protocol enabling any webserver sitting under an actual domain name to obtain the certificate from LetsEncrypt at no cost.
The official client implementing the ACME protocol is called Certbot and is written in Python. It's a powerful client, but it has it's share of issues as well. Because it is a sort of a swiss-knife, it tries to handle many tasks. By it's nature, it is a little bit heavy on the dependencies. I specifically do not like it adds lines into Nginx configuration files by default. Another problem I had was on Ubuntu machine. When 20.04 came out, the repositories was slower to catch up and I had to do manual patches of the certbot's code, which is not a pleasant experience. This is also the reason I am experimenting with Arch as a server.
Certbot is not the only available client speaking the ACME protocol. Heck, the ACME protocol is available as RFC8555 and anyone can even obtain the certificate from LetsEncrypt manually by following it. There are also many 3rd party clients that automate the process available already.
Enter acme.sh
One of such clients is called acme.sh an as it's name suggest is a Shell script with (almost) no dependencies. This fact alleviates the problem of slow repository update almost entirely, because one can always just use git to obtain the latest version, regardless of where the host operating system repositories do. Acme.sh page cites:
It's probably the easiest & smartest shell script to automatically issue & renew the free certificates from Let's Encrypt.
Let's see if this statement holds onto it's message.
Setup
Start by setting-up the DNS record type A or CNAME for sub.example.com
pointing to the public IP address of the host where these steps are going
to be applied. DNS records can be set any time, but it can take time till
nameservers to propagate the changes, so it is better to do it first.
This guide assumes becoming a superuser:
su -
Install acme.sh and Nginx, or alternatively nginx-mainline
:
pacman -S --needed acme.sh nginx
Make sure there is nothing listening on port 443 used for HTTPS:
ss -tuna | grep :443
If there is something running there already, stop it.
Issue the certificate
The next step makes use of the Application-Layer Protocol Negotiation
(ALPN), which is the initial part of the TLS handshake mentioned above.
Acme.sh is capable of issuing a certificate using ALPN mode. The
certificates are installed into /root/.acme.sh/sub.example.com/
:
acme.sh --issue --alpn -d sub.example.com
Now pick or choose a location where you put your certificates, examples:
/etc/ssl/certs
is used by OpenSSL/etc/letsencrypt/live
is used by Certbot/etc/nginx/ssl
preferred by some users
If the certs are being used solely by Nginx, the /etc/nginx/ssl
is a good
choice:
mkdir /etc/nginx/ssl
It is important to set the right permissions for this folder to protect the private key:
chmod 700 /etc/nginx/ssl
The folder has to be owned by the root user.
Configure Nginx
Now copy the generated certificates there, pay attention to reloadcmd
:
acme.sh --install-cert -d sub.example.com \
--key-file '/etc/nginx/ssl/sub.example.com.key' \
--fullchain-file '/etc/nginx/ssl/sub.example.com.cer' \
--reloadcmd "systemctl force-reload nginx"
Important: make sure to check the permissions. The /etc/nginx/ssl
folder should have 700, .cer
files should have 644 and .key
file should
have 600. Everything should be owned by root. Note that the ownership and
permissions are preserved automatically when the certificates are renewed.
find /etc/nginx/ssl -printf "%m %f\n"
700 ssl
600 sub.example.com.key
644 sub.example.com.cer
Add the relevant data under the server
block in the Nginx config. Not all
configuration directives are offered in the example below, just the most
relevant ones. Consider consulting the Nginx
documentation
on HTTPS.
server {
listen 443 ssl;
server_name sub.example.com;
ssl_certificate /etc/nginx/ssl/sub.example.com.cer;
ssl_certificate_key /etc/nginx/ssl/sub.example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}
Lastly, start and enable Nginx service:
systemctl enable nginx.service --now
Now access the page via the browser to check if HTTPS is working.
Note: when HTTPS served via Nginx works, consider switching to obtaining the certificate via Nginx mode, because certificate renewal via ALPN will not work anymore as Nginx is already listening on the port 433. The reason it was used in the first place is that it does not require any dependencies as opposed to standalone mode requiring socat). The second reason is that the Nginx validation mode may require a working Nginx configuration in the first place, so it is better to start safe.
acme.sh --issue --nginx -d sub.example.com
This is a 41th post of #100daystooffload.
Links
- https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
- https://github.com/acmesh-official/acme.sh
- https://serverfault.com/a/216480/505241
- https://serverfault.com/a/259307/505241
- https://stackoverflow.com/a/1796163/1972509
- https://wiki.archlinux.org/index.php/Acme.sh
- https://www.howtoforge.com/getting-started-with-acmesh-lets-encrypt-client/