Apache server comes with a module named mod_md. We can use this for certificate provisioning via the ACME protocol. This page explains how to install, set up and configure Apache with a mod_md module to secure traffic with Let’s Encrypt free TLS/SSL certificate on Ubuntu 20.04 LTS server.
Tutorial requirements | |
---|---|
Operating system/app | Ubuntu Linux with Apache 2 |
Root privileges required | Yes |
Difficulty | Easy (rss) |
Estimated completion time | 15m |
|
How to Secure Apache with mod_md Let’s Encrypt on Ubuntu 20.04
Let’s Encrypt is a CA that follows the ACME protocol. One can use Let’s Encrypt to issue free TLS/SSL certificates for Apache, Nginx, and other servers. In this tutorial, you will use mod_md to obtain a free TLS/SSL certificate for Apache 2 on Ubuntu 20.4 and set up your certificate to renew automatically too. Our sample set up is as follows:
- Domain – www42.cyberciti.biz
- HTTPS port – 443
- Virtual domain config file – /etc/apache2/sites-available/www42.cyberciti.biz.conf
Make sure Apache installed by following How to install Apache on Ubuntu 20.04 guide.
Step 1 – Installing mod_md for Let’s Encrypt
First, apply updates using the apt command:
sudo apt update
sudo apt upgrade
Then, install the mod_md by typing the following command:
sudo apt install libapache2-mod-md
Enabling mod_md on Ubuntu 20.04 LTS
Turn on mod_md, type:
sudo a2enmod md
Sample outputs:
Enabling module md. To activate the new configuration, you need to run: systemctl restart apache2
Make sure you activate the mod_ssl too, run:
sudo a2enmod ssl
Outputs:
Considering dependency setenvif for ssl: Module setenvif already enabled Considering dependency mime for ssl: Module mime already enabled Considering dependency socache_shmcb for ssl: Enabling module socache_shmcb. Enabling module ssl. See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates. To activate the new configuration, you need to run: systemctl restart apache2
Apache 2 must be reloaded or restarted with the help of the systemctl command”
sudo systemctl reload apache2.service
Step 2 – Set up the SSL certificate
Make sure your Apache 2 is working and listens on port 80. Verify using the ss command or netstat command:
sudo netstat -tulpn | grep ':80'
## or ##
sudo ss -tulpn | grep ':80'
Sample outputs:
tcp LISTEN 0 128 *:80 *:* users:(("apache2",pid=2550,fd=4),("apache2",pid=2549,fd=4),("apache2",pid=2548,fd=4))
All clients must connect to your server over port 80. Otherwise, you will not get validated for Let’s Encrypt certificate. From your desktop, run:
curl -I http://www42.cyberciti.biz
curl command outputs validting that we can connect to the port TCP port 80:
HTTP/1.1 200 OK Date: Wed, 06 May 2020 19:30:43 GMT Server: Apache/2.4.41 (Ubuntu) Last-Modified: Wed, 06 May 2020 19:15:29 GMT ETag: "15e-5a4ff965902a3" Accept-Ranges: bytes Content-Length: 350 Vary: Accept-Encoding Connection: close Content-Type: text/html
Let us edit the /etc/apache2/sites-available/www42.cyberciti.biz.conf, enter:
sudo nano /etc/apache2/sites-available/www42.cyberciti.biz.conf
At the top of file add the following three mod_md directives:
## Secure Apache with mod_md Let's Encrypt directives ##
ServerAdmin webmaster@cyberciti.biz
MDCertificateAgreement accepted
MDomain www42.cyberciti.biz
MDPrivateKeys RSA 4096
Where,
- ServerAdmin webmaster@cyberciti.biz : mod_md will use this email address when registering your domains at Let’s Encrypt.
- MDCertificateAgreement accepted : You must accept the Terms of Service conditions as set by Let’s Encrypt.
- MDomain www42.cyberciti.biz : Declare a domain named that should be manged by mod_md to issue and renew certificates. You can use full domain name such as www.cyberciti.biz or cyberciti.biz or www42.cyberciti.biz. Make sure it matches to ServerName.
- MDPrivateKeys RSA 4096 : Set type and size of the private keys generated.
Here is my complete config file :
## Apache with mod_md Let's Encrypt ## ## mod_md config for Let's Encrypt ## ServerAdmin webmaster@cyberciti.biz MDCertificateAgreement accepted MDomain www42.cyberciti.biz MDPrivateKeys RSA 4096 ## HTTP port 80 config ## <VirtualHost *:80> ServerAdmin webmaster@cyberciti.biz ServerName www42.cyberciti.biz DocumentRoot /home/cyberciti.biz/html DirectoryIndex index.html ErrorLog ${APACHE_LOG_DIR}/www42.cyberciti.biz-error.log CustomLog ${APACHE_LOG_DIR}/www42.cyberciti.biz-access.log combined # Redirect all http requests to HTTPS (uncomment the following two lines when HTTPS issued) # RewriteEngine On # RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] </VirtualHost> ## HTTPS Config ## <VirtualHost *:443> SSLEngine on ServerAdmin webmaster@cyberciti.biz ServerName www42.cyberciti.biz DocumentRoot /home/cyberciti.biz/html DirectoryIndex index.html ErrorLog ${APACHE_LOG_DIR}/www42.cyberciti.biz-ssl-error.log CustomLog ${APACHE_LOG_DIR}/www42.cyberciti.biz-ssl-access.log combined # Turn on HTTP/2 Protocols h2 http/1.1 # Set HTTP Strict Transport Security Header always set Strict-Transport-Security "max-age=63072000" </VirtualHost> ## Only enable TLS v1.3 and avoid older protocols ## SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2 SSLHonorCipherOrder off SSLSessionTickets off ## Turn on OCSP Stapling ## SSLUseStapling On SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" ## Permission for our DocumentRoot ## <Directory /home/cyberciti.biz/html> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>
Turn on Apache’s mod_rewrite and mod_headers under Ubuntu
We have already declared a rewrite rule in our config, so we need mod_rewrite. Hence, type the following command:
sudo a2enmod rewrite
Enabling module rewrite.
To activate the new configuration, you need to run:
systemctl restart apache2
Turn on mod_headers too:
sudo a2enmod headers
Enabling module headers.
To activate the new configuration, you need to run:
systemctl restart apache2
Check for config errors, run:
sudo apache2ctl configtest
Syntax OK
Step 3 – Open HTTPS (TCP port 443) using the UFW firewall on Ubuntu
Run the following ufw command to open TCP port 443 for everyone:
sudo ufw allow 443/tcp comment 'accept secure Apache connections'
Verify it rules:
sudo ufw status
See “How To Configure Firewall with UFW on Ubuntu 20.04 LTS” for more info.
Step 4 – Obtaining an SSL certificate using mod_md
So far, we installed mod_md for Apache on Ubuntu, turned on all essential modules, and open required TCP ports using a firewall. It is time to restart our Apache 2 server to obtain the free TLS/SSL certificate using Let’s Encrypt certificate authority (CA). Therefore, restart the Apache 2 server:
sudo systemctl restart apache2.service
As soon as Apache 2 restarted mod_md will contact Let’s Encrypt and request a certificate for your domain. Typically it takes up to one minute. You can check the server error log or Apache’s mod_status page to see if the request was successful or not. Here is what you will see in erro.log file:
sudo tail -f /var/log/apache2/error.log
One can use the grep command too:
sudo grep 'The Managed Domain' /var/log/apache2/error.log
Sample outputs indicating that LE has issued us a free TLS/SSL certificate:
[Wed May 06 20:17:38.112849 2020] [md:notice] [pid 21777:tid 139807872861952] AH10059: The Managed Domain www42.cyberciti.biz has been setup and changes will be activated on next (graceful) server restart.
Of course we can visit server-status url too. For example:
http://www42.cyberciti.biz/server-status
http://your-public-ip-here/server-status
sudo systemctl reload apache2.service
Step 5 – Test secure Apache 2 connection
All you have to do is type the following command or use a web browser such as Firefox/Chrome to make sure you are getting HTTPS connection:
curl -I https://www42.cyberciti.biz
curl command in action verify that our HTTPS connection is working and traffic is secured and encrypted
My secure Apache server with Let’s Encrypt TLS/SSL running on Ubuntu 20.04 LTS and verified by SSL Lab’s test
Step 6 – Automatically renewing an SSL certificate using mod_md and watchdog_module
The mod_md uses mod_watchdog module, which provides programmatic hooks for other modules to run tasks such as renewing TLS/SSL certificates and more periodically. In other words, auto-renew mode requires mod_watchdog to be active in your server. Hence, verify that mod_watchdog is activated using the following command:
sudo apache2ctl -M
sudo apache2ctl -M | grep -i watchdog
Loaded Modules:
core_module (static)
so_module (static)
watchdog_module (static)
http_module (static)
unixd_module (static)
access_compat_module (shared)
alias_module (shared)
auth_basic_module (shared)
authn_core_module (shared)
....
..
...
ssl_module (shared)
status_module (shared)
Step 7 – Monitoring certificate status
Now we set up Apache with mod_md and got a free TLS/SSL from Let’s Encrypt. It is time to monitor status of our certificate. There are two ways. First open /server-status URL:
https://www42.cyberciti.biz/server-status
https://your-public-ip-here/server-status
sudo nano /etc/apache2/sites-available/www42.cyberciti.biz.conf
Append the following:
<Location "/md-status">
SetHandler md-status
</Location>
Save and close the file. Restart the server and run:
sudo systemctl restart apache2.service
curl https://www42.cyberciti.biz/md-status
Sample outputs:
{ "version": "2.0.10", "managed-domains": [ { "name": "www42.cyberciti.biz", "domains": [ "www42.cyberciti.biz", "www43.cyberciti.biz" ], "contacts": [ "mailto:webmaster@cyberciti.biz" ], "transitive": 1, "ca": { "proto": "ACME", "url": "https://acme-v02.api.letsencrypt.org/directory", "agreement": "accepted" }, "state": 2, "renew-mode": 1, "renew-window": "33%", "warn-window": "10%", "must-staple": false, "cert": { "valid-from": "Wed, 06 May 2020 19:17:37 GMT", "valid-until": "Tue, 04 Aug 2020 19:17:37 GMT", "serial": "040E339A0A7D2224819A550BBB4596279F67", "sha256-fingerprint": "d78933fa946cb71810111876049defa4feb6820c319c69918ba925b463bbd11c" }, "renew": false } ] }
Where are my TLS/SSL certificate files located on disk?
You need to cd into the /etc/apache2/md directory:
$ sudo -i
# cd /etc/apache2/md/
# ls -l
We will see file as follows:
total 28 drwxr-xr-x 3 root root 4096 Nov 13 13:02 accounts drwx------ 3 root root 4096 Nov 13 13:02 archive drwxr-xr-x 2 www-data root 4096 Nov 13 13:02 challenges drwx------ 3 root root 4096 Nov 13 13:02 domains -rw------- 1 root root 116 Nov 13 12:52 md_store.json drwxr-xr-x 2 www-data root 4096 Nov 13 13:02 staging drwx------ 2 root root 4096 Nov 13 13:02 tmp
Make sure you look into the domains directory:
# cd domains
# ls -l
# cd www42.cyberciti.biz
# ls -l
Here are TLS/SSL files for your domain:
total 16 -rw------- 1 root root 3997 Nov 13 13:02 job.json -rw------- 1 root root 492 Nov 13 13:04 md.json -rw------- 1 root root 3272 Nov 13 13:02 privkey.pem -rw------- 1 root root 3916 Nov 13 13:02 pubcert.pem
Issuing www and non-www domains
Want TLS/SSL certificate for both cyberciti.biz and www.cyberciti.biz domain? Try it as follows (note MDomain and ServerAlias directives added/updated):
ServerAdmin webmaster@cyberciti.biz MDCertificateAgreement accepted # www and non-www TLS certicate # MDomain cyberciti.biz www.cyberciti.biz MDPrivateKeys RSA 4096 ## HTTP port 80 config ## <VirtualHost *:80> ServerAdmin webmaster@cyberciti.biz ServerName www.cyberciti.biz ServerAlias cyberciti.biz DocumentRoot /home/cyberciti.biz/html DirectoryIndex index.html ErrorLog ${APACHE_LOG_DIR}/cyberciti.biz-error.log CustomLog ${APACHE_LOG_DIR}/cyberciti.biz-access.log combined # Redirect all http requests to HTTPS (uncomment the following two lines when HTTPS issued) # RewriteEngine On # RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L] </VirtualHost> ## HTTPS Config ## <VirtualHost *:443> SSLEngine on ServerAdmin webmaster@cyberciti.biz ServerName www.cyberciti.biz ServerAlias cyberciti.biz DocumentRoot /home/cyberciti.biz/html DirectoryIndex index.html ErrorLog ${APACHE_LOG_DIR}/cyberciti.biz-ssl-error.log CustomLog ${APACHE_LOG_DIR}/cyberciti.biz-ssl-access.log combined # Turn on HTTP/2 Protocols h2 http/1.1 # Set HTTP Strict Transport Security Header always set Strict-Transport-Security "max-age=63072000" </VirtualHost> ## Only enable TLS v1.3 and avoid older protocols ## SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2 SSLHonorCipherOrder off SSLSessionTickets off ## Turn on OCSP Stapling ## SSLUseStapling On SSLStaplingCache "shmcb:logs/ssl_stapling(32768)" ## Permission for our DocumentRoot ## <Directory /home/cyberciti.biz/html> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>
A note about ‘auto’ directive
The above approach work for one or two subdomains. But what if you have 4 or 5 domains? Such as cyberciti.biz, www.cyberciti.biz, forum.cyberciti.biz, webmail.cyberciti.biz? Then we can use the auto keyword as follows:
MDomain cyberciti.biz auto
<VirtualHost *:443>
ServerName cyberciti.biz
ServerAlias www.cyberciti.biz
ServerAlias forum.cyberciti.biz
ServerAlias webmail.cyberciti.biz
...
</VirtualHost>
Whenever you add more ServerAlias names to this virtual host with ‘auto’ keyword, they will be added as well to the Managed Domain. If you prefer to explicitly declare all the domain names, use ‘manual’ mode.
Multiple domains
Here is how to set up nixcraft.com and cyberciti.biz domains:
MDomain cyberciti.biz auto MDomain nixcraft.com auto # cyberciti.biz # <VirtualHost *:80> ServerName cyberciti.biz ServerAlias www.cyberciti.biz ... ..... </VirtualHost> <VirtualHost *:443> ServerName cyberciti.biz ServerAlias www.cyberciti.biz ... </VirtualHost> # nixcraft.com # <VirtualHost *:80> ServerName nixcraft.com ServerAlias www.nixcraft.com ... </VirtualHost> <VirtualHost *:443> ServerName nixcraft.com ServerAlias www.nixcraft.com ... </VirtualHost>
Conclusion
In this tutorial, you learned how to secure Apache with mod_md Let’s Encrypt module to issue and auto-renew free TLS/SSL certificate on Ubuntu 20.04 LTS Linux server. For more information, see mod_md documentation here. Keep reading read of the series:
- How to install Apache on Ubuntu 20.04 LTS
- Secure Apache with mod_md Let’s Encrypt on Ubuntu 20.04 LTS
🐧 Get the latest tutorials on Linux, Open Source & DevOps via:
- RSS feed or Weekly email newsletter
- Share on Twitter • Facebook • 12 comments... add one ↓
Category | List of Unix and Linux commands |
---|---|
File Management | cat |
Firewall | Alpine Awall • CentOS 8 • OpenSUSE • RHEL 8 • Ubuntu 16.04 • Ubuntu 18.04 • Ubuntu 20.04 |
Network Utilities | dig • host • ip • nmap |
OpenVPN | CentOS 7 • CentOS 8 • Debian 10 • Debian 8/9 • Ubuntu 18.04 • Ubuntu 20.04 |
Package Manager | apk • apt |
Processes Management | bg • chroot • cron • disown • fg • jobs • killall • kill • pidof • pstree • pwdx • time |
Searching | grep • whereis • which |
User Information | groups • id • lastcomm • last • lid/libuser-lid • logname • members • users • whoami • who • w |
WireGuard VPN | Alpine • CentOS 8 • Debian 10 • Firewall • Ubuntu 20.04 |
I am getting the following error when installing Let’s Encrypt SSL on my domain:
Waiting for verification…
Challenge failed for domain linuxbuz.com
dns-01 challenge for linuxbuz.com
Cleaning up challenges
Some challenges have failed.
Are you using dns challenge? This tutorial only works with http challenge.
Thanks a lot, and great tutorial, anyway I’m not sure to understand about the renew. I mean, is this automatic renw ? even without any crontab ?
Thanks
yes, watchdog_module will do auto renewal. There is no cron job since Apache with watchdog_module always running on port 80 and 443.
Thanks Vivek !
Hi,
I am working with a PHP app. This post was a lifesaver. One question, how can I get A+ score in the test? Anyone willing to share httpd.conf settings?
Hi,
where does mod_md save the certificate? I couldn’t, for the life of me, figure out where it saves it following documentation and using locate(1).
Best regards,
vhns
The page has been updated with location of files. Thanks for the feedback.
Your vhost example is only working for me, when setting MDMembers to “manual”. Else I’m getting:
[Wed Nov 18 15:26:15.651155 2020] [mpm_prefork:notice] [pid 6864] AH00169: caught SIGTERM, shutting down
[Wed Nov 18 15:26:15.756163 2020] [md:error] [pid 6962] (22)Invalid argument: CA url for [MYDOMAIN] invalid (missing uri scheme): accepted
[Wed Nov 18 15:26:15.756217 2020] [md:error] [pid 6962] (22)Invalid argument: AH10073: synching 1 mds to registry
AH00016: Configuration Failed
I had to remove the *:80 part from the particular vhost config and do a general 80 to 443 redirect in 000-default.conf to get it working.
Hi, thanks for Your Tutorial, keep your excellent work.
Quick question:
How can issue an cert for both example.com and http://www.example.com?
I’m getting “certificate valid only for http://www.example.com“
Update config as follows:
Make sure VirtualHost has entries as follows:
I added section too to deal with both www and non-www domains.
Excellent tutorial, however I did not receive a certificate until I published the site
a2ensite mysite
I am not so familiar with Ubuntu, so maybe it sometimes is not necessary?