Let's Encrypt SSL Certificate
Let's Encrypt is a non-profit certificate authority run by Internet Security Research Group that provides X.509 certificates for Transport Layer Security encryption at no charge. The certificate is valid for 90 days, during which renewal can take place at any time.
This section will describe how to obtain a certificate and automate the renewal process.
Prerequisites
- A registered domain name such as mydomain.com
- Two Host A records on your authoritative DNS server for your domain such as:
Code: Select all
Type A, mydomain.com, 216.70.70.70
Type A, mysite.mydomain.com, 216.70.70.70
- Web server (such as Apache)
- A Virtual Host configuration file
Install Certbot
To install from the Ubuntu repository:
Code: Select all
sudo apt install certbot python3-certbot-apache
LetsEncrypt Permission Fix
NOTE: This is due to the umask setting in ~/.bashrc
While testing version 0.31.0 thru 0.40.0, I found that I kept getting a "client lacks sufficient authorization" error message. The problem is that during the authorization phase, it re-directs the Virtual Host configuration to
/var/lib/letsencrypt/http_challenges but the parent folder of /var/lib/letsencrypt has too restrictive of permissions set. The owner is set to root:root and permissions of 700 which means the web service (www-data) cannot view anything under that directory. The solution is to fix the permission on that folder so "other" users (www-data) can see the contents. NOTE: This folder only exists once you try to obtain your 1st SSL certification in the next section below.
Code: Select all
sudo mkdir -p /var/lib/letsencrypt/http_challenges
sudo chmod 755 /var/lib/letsencrypt
sudo chmod 755 /var/lib/letsencrypt/http_challenges
Obtain the SSL Certificate
Be sure to use the FQDN for all domain names that will be used to access this site.
Code: Select all
sudo certbot --apache -d mydomain.com -d mysite.mydomain.com
We use -d to specify each name we would like the certificate to be valid for.
Upon the first time creating the certificate, you will be prompted for various information.
Once the information gathering is complete, Certbot will ask how to configure the HTTPS settings such as not forcing a redirect of HTTP to HTTPS or letting it add a redirect to force all HTTP traffic to HTTPS.
You can answer however you like but I like to do my own redirection (especially if this is already handled on a load balancer).
The new files will be placed here by default:
Code: Select all
/etc/letsencrypt/live/mydomain.com/fullchain.pem
/etc/letsencrypt/live/mydomain.com/privkey.pem
Look at the modified Virtual Host configuration file:
Code: Select all
sudo vi /etc/apache2/sites-available/mysite.mydomain.com.conf
The modified file now looks something like this:
Code: Select all
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerName mysite.mydomain.com
ServerAlias mysite.mydomain.com
ServerAdmin webmaster@localhost
DocumentRoot /var/www/mysite.mydomain.com
ErrorLog ${APACHE_LOG_DIR}/mysite.mydomain.com-error.log
CustomLog ${APACHE_LOG_DIR}/mysite.mydomain.com-access.log combined
SSLEngine on
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
</IfModule>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/mysite.mydomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mysite.mydomain.com/privkey.pem
</VirtualHost>
</IfModule>
Schedule For SSL Certificate Auto-Renew
Let's Encrypt certificates are only valid for 90 days. This encourages admins to automate their certificate renewal process.
You can force a simulated update to ensure the process will work using the dry-run option below. If you see "success" messages, then you should be OK when it comes time for the renew to run for real:
Starting with Ubuntu 18.04 LTS, we now have a systemd timer. When enabled, it will check for renewals twice per day.
Code: Select all
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
Check the status with this command (which also shows when it last ran and when the next run will be):
Example Output:
Code: Select all
● certbot.timer - Run certbot twice daily
Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: en
Active: active (waiting) since Tue 2020-05-19 09:14:23 CDT; 6h ago
Trigger: Tue 2020-05-19 20:05:09 CDT; 4h 20min left
May 19 01:00:00 srv-apache systemd[1]: Started Run certbot twice daily.
You can check the journal logs for certbot-related events:
Example Output:
Code: Select all
May 19 09:14:23 srv-apache systemd[1]: Started Run certbot twice daily.
May 19 09:16:47 srv-apache sudo[5618]: root : TTY=pts/1 ; PWD=/etc/apache2/sites-available ; USER=root ; COMMAND=/usr/bin/certbot --apache -d mysite.mydomain.com
May 19 09:18:12 srv-apache sudo[5690]: root : TTY=pts/1 ; PWD=/etc/apache2/sites-available ; USER=root ; COMMAND=/usr/bin/certbot --apache -d mysite.mydomain.com
May 19 12:00:01 srv-apache CRON[6946]: (root) CMD (test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew)
Migrating to New Server
If you are moving all your sites to a new server (such as upgrading to new OS), you will need to take certificates with you. Here is the process for moving from an Ubuntu 18.04 to Ubuntu 20.04 server.
On the old server, create an archive of the certificates, renewal account and renewal config:
Code: Select all
sudo tar -chvzf /tmp/le-files.tar.gz /etc/letsencrypt/archive /etc/letsencrypt/accounts/acme-v02.api.letsencrypt.org/directory /etc/letsencrypt/renewal
Transfer the archive from the old server to the new server:
Code: Select all
scp /tmp/le-files.tar.gz administrator@srv-ubuntu:/tmp/.
On the new server, extract the archives:
Code: Select all
sudo tar -xvf /tmp/le-files.tar.gz -C /
Now we have to re-create the symlinks in the "live" folder which is what the Apache config files point to.
As time goes by, the certificates are renewed and they create new files. Make sure you are pointing each website's certificate to the latest cert.
Example: This particular website has renewed the certificate 5 times so the latest set of files to use has the number "5" in them which is what we link to from the "live" folder.
Code: Select all
ls -l /etc/letsencrypt/archive/games.mydomain.com
-rw------- 1 root root 1952 Jul 23 2019 cert1.pem
-rw-r--r-- 1 root root 1952 Sep 21 2019 cert2.pem
-rw-r--r-- 1 root root 1952 Nov 21 01:20 cert3.pem
-rw-r--r-- 1 root root 1952 Jan 20 07:43 cert4.pem
-rw-r--r-- 1 root root 1952 Mar 20 09:36 cert5.pem
-rw------- 1 root root 1647 Jul 23 2019 chain1.pem
-rw-r--r-- 1 root root 1647 Sep 21 2019 chain2.pem
-rw-r--r-- 1 root root 1647 Nov 21 01:20 chain3.pem
-rw-r--r-- 1 root root 1647 Jan 20 07:43 chain4.pem
-rw-r--r-- 1 root root 1647 Mar 20 09:36 chain5.pem
-rw------- 1 root root 3599 Jul 23 2019 fullchain1.pem
-rw-r--r-- 1 root root 3599 Sep 21 2019 fullchain2.pem
-rw-r--r-- 1 root root 3599 Nov 21 01:20 fullchain3.pem
-rw-r--r-- 1 root root 3599 Jan 20 07:43 fullchain4.pem
-rw-r--r-- 1 root root 3599 Mar 20 09:36 fullchain5.pem
-rw------- 1 root root 1708 Jul 23 2019 privkey1.pem
-rw------- 1 root root 1704 Sep 21 2019 privkey2.pem
-rw------- 1 root root 1704 Nov 21 01:20 privkey3.pem
-rw------- 1 root root 1708 Jan 20 07:43 privkey4.pem
-rw------- 1 root root 1704 Mar 20 09:36 privkey5.pem
Since I have a lot of websites, I created a script to help me automate this process in a consistent manner (once or more in test and again when performing the migration to production). You need to modify it to fit your needs or manually create the links yourself.
Modify the script below by passing the website folder and the certificate number to use.
/var/scripts/prod/le-symlink-create.sh (
GitHub Download)
Code: Select all
#!/bin/bash
#############################################################
## Name : le-symlink-create.sh
## Version : 1.2
## Date : 2022-08-10
## Author : LHammonds
## Purpose : Recreate Let's Encrypt symlinks to
## certificates after a server migration.
## Compatibility : Verified on Ubuntu Server 18.04 thru 22.04 LTS
## Run Frequency : Once after migration to new machine.
## Parameters : None
## Exit Codes : None
###################### CHANGE LOG ###########################
## DATE VER WHO WHAT WAS CHANGED
## ---------- --- --- ---------------------------------------
## 2020-05-19 1.0 LTH Created script.
## 2020-10-05 1.1 LTH "If (not) exist" logic added.
## 2022-08-10 1.2 LTH Better automation. Only need to set BaseDir.
#############################################################
BaseDir="/etc/letsencrypt"
ArchiveDir="${BaseDir}/archive"
LiveDir="${BaseDir}/live"
function f_remove () {
if [ -e ${1} ]; then
rm ${1}
fi
} ## f_remove() ##
function f_fixkey () {
## Param 1 = Web Folder
## Param 2 = Filename
## Param 3 = Keyname
f_remove ${LiveDir}/${1}/${3}
ln -s ${ArchiveDir}/${1}/${2} ${LiveDir}/${1}/${3}
} ## f_fixkey() ##
function f_getlatest () {
## Param 1 = Directory ##
## Param 2 = File pattern ##
## Output = Newest file matching pattern ##
unset -v latest
for file in ${1}/${2}; do
[[ $file -nt $latest ]] && latest=$file
done
## Remove parent path from filename and return value ##
printf "${latest#${1}/}"
} ## f_getlatest() ##
## Loop through every folder in BaseDir ##
find ${ArchiveDir} -maxdepth 1 -mindepth 1 -type d | while read CurrentDir; do
## Remove the parent path from the current directory ##
WebDir=${CurrentDir#${ArchiveDir}/}
if [ ! -d ${LiveDir}/${WebDir} ]; then
mkdir -p ${LiveDir}/${WebDir}
fi
printf "Fixing certs for ${WebDir}\n"
f_fixkey "${WebDir}" "$(f_getlatest "${ArchiveDir}/${WebDir}" "cert*.pem")" "cert.pem"
f_fixkey "${WebDir}" "$(f_getlatest "${ArchiveDir}/${WebDir}" "chain*.pem")" "chain.pem"
f_fixkey "${WebDir}" "$(f_getlatest "${ArchiveDir}/${WebDir}" "fullchain*.pem")" "fullchain.pem"
f_fixkey "${WebDir}" "$(f_getlatest "${ArchiveDir}/${WebDir}" "privkey*.pem")" "privkey.pem"
done
Once the certs are on the new server, make sure everything works by doing a dry run (no changes will be made):
Code: Select all
sudo certbot renew --force-renewal --apache --dry-run
If there were no failures, go ahead and force a renewal to make double-certain everything will work right when it needs to do so the next time it tries to automate the process:
Code: Select all
sudo certbot renew --force-renewal --apache
Now you can take a look at all the certificates and info (which should all have the same expiry date around 89 days from now):
Code: Select all
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
Certificate Name: mysite.mydomain.com
Domains: mysite.mydomain.com
Expiry Date: 2020-08-22 15:32:53+00:00 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/mysite.mydomain.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/mysite.mydomain.com/privkey.pem
Certificate Name: forum.mydomain.com
Domains: forum.mydomain.com.com
Expiry Date: 2020-08-22 15:33:00+00:00 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/forum.mydomain.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/forum.mydomain.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certificate Deletion / Removal
If you ever need to delete a certificate and all its related files to ensure the automatic renewal does not try renew a dead site, run this command:
It will then present you with a list of certificates. Type the number next to the certificate you want to delete.
Example:
Code: Select all
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Which certificate(s) would you like to delete?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: nextcloud.mydomain.com
2: video.mydomain.com
3: forum.mydomain.com
4: wiki.mydomain.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): c
User ended interaction.