Until now, our domain and subdomain can only be accessed via the unencrypted HTTP protocol. This is not up-to-date at all and certainly not secure. Search engines, for example, are extremely reluctant to suggest pages that do not offer a secure TLS connection.
TLS stands for Transport Layer Security and is the successor to SSL (Secure Sockets Layer). Although SSL is considered an outdated security protocol, the name persists. So when SSL is advertised, the new standard TLS is actually meant.
TLS certificates can be self-signed or purchased free of charge:
Let's Encrypt, free certification authority (free of charge)
commercial certification bodies (fee required)
A certification authority (CA) is subject to strict audits, in which security concepts and technical infrastructure are checked in particular. Despite all this, it is a free decision of the software manufacturer to recognize a certification authority as trustworthy or not.
Self-signed certificates are generally considered untrustworthy, as their authenticity is not confirmed by an independent organization. However, there are also free certification authorities, such as Let's Encrypt, which issue certificates free of charge.
Certificate authorities issue SSL certificates only for domains, not for IP addresses.
In order to obtain certificates from free certification authorities, there are requirements that we fulfill with our cloud server and domain:
root access on the server
a qualified domain name that can be queried via DNS
a website that is already online and accessible via port 80
In this chapter, we will obtain trusted certificates for our domain and subdomain from Let's Encrypt. We will also create a self-signed certificate for our server IP. The validity period of certificates is limited in time, so an automatism is needed that renews the certificates early before they expire. We had already discussed the topic of cronjobs at logrotate.
Check SSL and TLS
In the meantime, we let the online service SSL Labs check our configuration and the status of the encryption. Currently, no certificate is stored, so the result for our domain is correspondingly negative:
We will repeat this test later and hopefully we will get an "A", maybe even an "A+" rating.
Certbot is a free ACME client software (Automatic Certificate Management Environment) that helps us retrieve and renew Let's Encrypt certificates. The software can do more, but that is the main purpose for us.
Install Certbot:
__$ sudo apt install -y certbot
Let's Encrypt certificate for the domain
Before we really get started, I would like to open a spoiler: Certbot can be given some level of control over Nginx or Apache. From experience, this is a convenient solution until the components are out of sync with each other. Maybe when Certbot needs to be updated, but the servers have to wait to update because of ongoing web projects. I try to avoid such dependencies, which usually means that there is extra work in the implementation. I use Certbot to request certificates and renew them automatically. That's all I want it to do. I want to control the configuration of the server services myself.
Basically, the domain or subdomain must be publicly accessible via port 80 for the certificate request. What we have already prepared in the last chapter under server block for a domain.
With the following command we apply for a certificate with Certbot at Let's Encrypt. It should be valid for the domain and the "subdomain" www:
There is a more compact command, where the webroot method and directory are also specified: sudo certbot certonly --webroot -w /var/www/com.linuxserversetup -d linuxserversetup.com -d www.linuxserversetup.com --rsa-key-size 4096
Of course, we have to prove the ownership of the domain. We do this with the "webroot" method. We have already created the webroot for the domain: /var/www/com.linuxserversetup. My input to the above command is:
Place files in webroot directory (webroot): 2
Enter email address: Confirmations, bug reports and announcements from Let's Encrypt go to this address.
Please read the Terms of Service: a
Newsletter: n
Input the webroot for linuxserversetup.com: /var/www/com.linuxserversetup
Select the webroot for www.linuxserversetup.com: 2
Saving debug log to /var/log/letsencrypt/letsencrypt.log
How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Plugins selected: Authenticator webroot, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): tom@linuxservrsetup.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: a
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: n
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for linuxserversetup.com
http-01 challenge for www.linuxserversetup.com
Input the webroot for linuxserversetup.com: (Enter 'c' to cancel): /var/www/com.linuxserversetup
Select the webroot for www.linuxserversetup.com:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Enter a new webroot
2: /var/www/com.linuxserversetup
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/linuxserversetup.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/linuxserversetup.com/privkey.pem
Your cert will expire on 2022-02-23. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
The answer contains the location where the certificates were stored. More precisely, there are links to the actual files:
__$ sudo ls -la /etc/letsencrypt/live/linuxserversetup.com/
The certificates are valid for three months and are automatically renewed within the last month. The cronjob for this was created here:
__$ sudo nano /etc/cron.d/certbot
Output:
/etc/cron.d/certbot
# /etc/cron.d/certbot: crontab entries for the certbot package
#
# Upstream recommends attempting renewal twice a day
#
# Eventually, this will be an opportunity to validate certificates
# haven't been revoked, etc. Renewal will only occur if expiration
# is within 30 days.
#
# Important Note! This cronjob will NOT be executed if you are
# running systemd as your init system. If you are running systemd,
# the cronjob.timer function takes precedence over this cronjob. For
# more details, see the systemd.timer manpage, or use systemctl show
# certbot.timer.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
On our Ubuntu Linux it is rather unlikely that this crontab is taken into account, since here usually an systemd timer controls the timers and crons. Therefore Certbot has created an /lib/systemd/system/certbot.timer that triggers the execution of /lib/systemd/system/certbot.service every 12 hours.
Now, to find out if systemd has control on the system, it is enough to check the existence of /run/system/system/:
__$ ls /run/systemd/system/
About the output by systemctl list-timers, the Certbot timer should be listed:
__$ systemctl list-timers
Output:
NEXT LEFT LAST PASSED UNIT ACTIVATES
...
Wed 2022-01-05 16:40:04 UTC 29min left Wed 2022-01-05 02:55:54 UTC 13h ago certbot.timer certbot.service
...
There we see that the command /usr/bin/certbot -q renew renews the certificates.
After a certificate is renewed, Nginx must be reloaded. The Certbot parameter --deploy-hook is suitable for this task. A bad solution would be to use this over the certbot.service. The complete file with the changed ExecStart line would then look like this:
# renew_before_expiry = 30 days
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/linuxserversetup.com
cert = /etc/letsencrypt/live/linuxserversetup.com/cert.pem
privkey = /etc/letsencrypt/live/linuxserversetup.com/privkey.pem
chain = /etc/letsencrypt/live/linuxserversetup.com/chain.pem
fullchain = /etc/letsencrypt/live/linuxserversetup.com/fullchain.pem
# Options used in the renewal process
[renewalparams]
account = adg4c513fhe12318sfvb76fd387
rsa_key_size = 4096
authenticator = webroot
server = https://acme-v02.api.letsencrypt.org/directory
[[webroot_map]]
linuxserversetup.com = /var/www/com.linuxserversetup
www.linuxserversetup.com = /var/www/com.linuxserversetup
Here we add renew_hook = service nginx reload. We can edit the file as usual with the editor nano or with this one-liner. With sed we search the line [renewalparams] and add the renew_hook part below it:
__$ sudo sed -i '/\[renewalparams]/arenew_hook = service nginx reload' /etc/letsencrypt/renewal/linuxserversetup.com.conf
# renew_before_expiry = 30 days
version = 0.40.0
archive_dir = /etc/letsencrypt/archive/linuxserversetup.com
cert = /etc/letsencrypt/live/linuxserversetup.com/cert.pem
privkey = /etc/letsencrypt/live/linuxserversetup.com/privkey.pem
chain = /etc/letsencrypt/live/linuxserversetup.com/chain.pem
fullchain = /etc/letsencrypt/live/linuxserversetup.com/fullchain.pem
# Options used in the renewal process
[renewalparams]
account = adg4c513fhe12318sfvb76fd387
rsa_key_size = 4096
authenticator = webroot
server = https://acme-v02.api.letsencrypt.org/directory
renew_hook = service nginx reload
[[webroot_map]]
linuxserversetup.com = /var/www/com.linuxserversetup
www.linuxserversetup.com = /var/www/com.linuxserversetup
The .well-known access
For authorization by Let's Encrypt, access to a specific address (http://www.linuxserversetup.com/.well-known/acme-challenge/) must be given. On the server side, the webroot directory, which was previously specified during the application, is used by the letsencrypt service to create a temporary file under /var/www/com.linuxserversetup.com/.well-known/acme-challenge. After the certificate is renewed, this file is also removed. We have to create the .well-known/acme-challenge path ourselves, which we will get to in a moment. A note at this point: the .well-known folder is notorious for hackers placing their malware there after a successful break-in. It is well hidden there and can be accessed from the Internet through the aforementioned share. So it doesn't hurt to look in there from time to time or to create an automatism that empties these folders in certain time intervals.
Apply certificates in the NGINX configuration
We have to change the responsible configuration file (/etc/nginx/sites-available/com.linuxserversetup.conf) for HTTPS accordingly:
After saving we check the new configuration and restart NGINX:
__$ sudo nginx -t
__$ sudo systemctl reload nginx
Check Certbot's automatic renewal
Before going any further, we can check if Certbot is able to renew certificates at all. For this purpose there is the parameter --dry-run. This simulates the process and creates a report afterwards.
__$ sudo certbot renew --dry-run
Check with SSL Labs
If we now repeat the test with SSL Labs, our configuration should be rated "A":
SSL Labs: A-Rating
The rating can be further improved by adding a Diffie-Hellman key. We create one using OpenSSL, the location should be /etc/ssl/certs/:
Attention spoilers: The calculation will take at least 10 minutes on our CPX11:
Generating DH parameters, 4096 bit long safe prime, generator 2
This is going to take a long time
...............................................+..................................
..................................................................................
...................................................................+..............
..................................................................................
..................................................................................
By enabling HSTS, even an "A+" rating is possible. By HSTS the HTTP header Strict-Transport-Security is transmitted, which tells the browser that this address should only be called via HTTPS. The supplied value, is the period in seconds for which the browser should remember this. Usually one year (31536000 seconds).
We enter the path to the PEM certificate and the HSTS header into the NGINX site configuration as before:
Finally, we also specify the allowed encryption methods (ciphers) for a connection with ssl_ciphers, which a browser must support. Here we have to find a good balance between security and sufficient browser compatibility:
The page ssl-config.mozilla.org can help to make further settings. However, it should be noted that there should still be some backward compatibility. Because a too strict TLS configuration will exclude older devices.
Self-signed TLS certificate for the default server block
Let's Encrypt issues TLS certificates only for qualified domain names. Therefore, we have to provide a self-signed certificate for our IP. Usually it is not desired that someone retrieves the server IP directly in the browser. But since it is possible, this exchange should also be encrypted. At this point it is not important whether the certificate is trustworthy. It does what it is supposed to do, namely encrypt. And if that is too insecure, you should not visit the IP.
Using the following OpenSSL command, we create a certificate and key using the RSA encryption method. The complexity should be 2048 bits with a validity period of 365 days:
During the procedure I confirm Country Name with EN and Common Name with the server IP 116.203.69.89. For the rest you can specify or simply by boarding.
gned.key -out /etc/ssl/certs/nginx-selfsigned.crt
Generating a RSA private key
.................................................+++++
......+++++
writing new private key to '/etc/ssl/private/nginx-selfsigned.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:116.203.68.89
Email Address []:
The generated files are:
/etc/ssl/private/nginx-selfsigned.key
/etc/ssl/certs/nginx-selfsigned.crt
__$ sudo ls -la /etc/ssl/private/
__$ sudo ls -la /etc/ssl/certs/
Now we just have to store them in the default configuration above.
Let's open the file for editing:
__$ sudo nano /etc/nginx/sites-available/default
We will add another server block at the beginning of the file. This redirects from HTTP to HTTPS, enforcing an encrypted transfer. The second original block will be supplemented with the two paths leading to the self-signed key (ssl_certificate) and the certificate (ssl_certificate_key).
While we're at it, we can also include the Diffie-Hellman key generated above.
The complete file should have the following content: