How to Secure Apache with Let's Encrypt on CentOS 7
This tutorial will show you how to set up a TLS/SSL certificate from Let’s Encrypt on a CentOS 7 server running Apache as a web server. Additionally, we will cover how to automate the certificate renewal process using a cron job.
SSL certificates are used within web servers to encrypt the traffic between server and client, providing extra security for users accessing your application. Let’s Encrypt provides an easy way to obtain and install trusted certificates for free.
In order to complete this guide, you will need:
- A CentOS 7 server with a non-root sudo user, which you can set up by following our Initial Server Setup guide
- At least one domain name configured for your server using A or CNAME records. The exact procedure depends on your domain name registrar or hosting service. If you have purchased a domain name and wish to use it with the DigitalOcean name servers, you can read our guide on setting up your hostname with DigitalOcean to set up the correct records.
For the purpose of this guide, we will install a Let’s Encrypt certificate for the domain
example.com. This will be referenced throughout the guide, but you should substitute it with your own domain while following along.
When you are ready to move on, log into your server using your
Step 1: Install the Required Software
Before we can install the
certbot Let’s Encrypt client and generate the SSL certificate, we need to install the Apache web server if it is not already available. We will also need to install the
mod_ssl module to correctly serve encrypted traffic. Finally, we will need to enable the EPEL repository, which provides additional packages for CentOS, including the
certbot package we need.
Enable the EPEL repository first by typing:
- sudo yum install epel-release
Now that you have access to the extra repository, install all of the required packages by typing:
- sudo yum install httpd mod_ssl python-certbot-apache
You should now have all of the packages you need to secure your site.
Step 2: Configure Access to Apache
Before we can request a certificate, we need to make sure Apache is running on our server and is accessible to the outside world.
To make sure Apache is up and running, type:
- sudo systemctl start httpd
Verify that Apache is running by checking the status of the service:
- systemctl status httpd
Output● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled) Active: active (running) since Thu 2017-01-05 16:47:06 UTC; 1h 7min ago Docs: man:httpd(8) man:apachectl(8) Main PID: 9531 (httpd) Status: "Total requests: 10; Current requests/sec: 0; Current traffic: 0 B/sec" CGroup: /system.slice/httpd.service ├─9531 /usr/sbin/httpd -DFOREGROUND ├─9532 /usr/sbin/httpd -DFOREGROUND ├─9533 /usr/sbin/httpd -DFOREGROUND ├─9534 /usr/sbin/httpd -DFOREGROUND ├─9535 /usr/sbin/httpd -DFOREGROUND └─9536 /usr/sbin/httpd -DFOREGROUND Jan 05 16:47:05 centos-512mb-nyc3-01 systemd: Starting The Apache HTTP Server... Jan 05 16:47:05 centos-512mb-nyc3-01 httpd: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using ::1. Set the 'ServerName' directive globally to suppress this message Jan 05 16:47:06 centos-512mb-nyc3-01 systemd: Started The Apache HTTP Server.
It should say
active towards the top.
Next, make sure port 80 and 443 are open in your firewall. If you are not running a firewall, you can skip ahead.
If you have a firewalld firewall running, you can open these ports by typing:
- sudo firewall-cmd --add-service=http
- sudo firewall-cmd --add-service=https
- sudo firewall-cmd --runtime-to-permanent
If have an iptables firewall running, the commands you need to run are highly dependent on your current rule set. For a basic rule set, you can add HTTP and HTTPS access by typing:
- sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT
- sudo iptables -I INPUT -p tcp -m tcp --dport 443 -j ACCEPT
Check that your site is reachable using curl:
- curl example.com
The HTML for your main page should be displayed. Since the
mod_ssl package configures a self-signed SSL certificate by default, you can check your domain with HTTPS as well if you use the
-k flag to allow untrusted certificates:
- curl -k https://example.com
This should allow you to view the same output and verify that the SSL port is open.
Step 3: Request an SSL Certificate from Let's Encrypt
Now that Apache is ready, we can request an SSL certificate for our domain.
Generating the SSL Certificate for Apache using the
certbot Let’s Encrypt client is fairly straightforward. The client will automatically obtain and install a new SSL certificate that is valid for the domains provided as parameters.
If you want to install a single certificate that is valid for multiple domains or subdomains, you can pass them as additional parameters to the command. The first domain name in the list of parameters will be the base domain used by Let’s Encrypt to create the certificate, and for that reason we recommend that you pass the bare top-level domain name as first in the list, followed by any additional subdomains or aliases:
- sudo certbot --apache -d example.com -d www.example.com
For this example, the base domain will be
To execute the interactive installation and obtain a certificate that covers only a single domain, run the
certbot command with:
- sudo certbot --apache -d example.com
certbot utility can also prompt you for domain information during the certificate request procedure. To use this functionality, call
certbot without any domains:
- sudo certbot --apache
You will be presented with a step-by-step guide to customize your certificate options. You will be asked to provide an email address for lost key recovery and notices. If you did not specify your domains on the command line, you will be prompted for that as well. If your Virtual Host files do not specify the domain they serve explicitly using the
ServerName directive, you will be asked to choose the Virtual Host file (the default
ssl.conf file should work).
You will also be able to choose between enabling both
https access or forcing all requests to redirect to
https. For better security, it is recommended to choose the Secure option if you do not have any special need to allow unencrypted connections.
When the installation is successfully finished, you should see a message similar to this:
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/example.com/fullchain.pem. Your cert will expire on 2016-04-21. To obtain a new version of the certificate in the future, simply run Let's Encrypt again. - If you lose your account credentials, you can recover through e-mails sent to firstname.lastname@example.org. - Your account credentials have been saved in your Let's Encrypt configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Let's Encrypt so making regular backups of this folder is ideal. - If you like Let's Encrypt, 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 generated certificate files should be available within a subdirectory named after your base domain in the
Before we check our SSL certificate, we should modify CentOS's default SSL configuration to make it more secure.
Step 4: Select More Secure SSL Settings for Apache
The default SSL configuration shipped with CentOS's version of Apache is a bit dated and as such, it is vulnerable to some more recent security issues.
To configure more secure SSL-related options, open the
ssl.conf file (or whichever Virtual Host file you selected when prompted during the Let's Encrypt request process):
- sudo nano /etc/httpd/conf.d/ssl.conf
Inside, we should first find the
SSLCipherSuite lines and either delete them or comment them out. The configuration we be pasting in a moment will offer more secure settings than the default included with CentOS's Apache:
. . . # SSLProtocol all -SSLv2 . . . # SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA
Next, to set up Apache SSL more securely, we will be using the recommendations by Remy van Elst on the Cipherli.st site. This site is designed to provide easy-to-consume encryption settings for popular software. You can read more about his decisions regarding the Apache choices here.
Note: The suggested settings on the site linked to above offer strong security. Sometimes, this comes at the cost of greater client compatibility. If you need to support older clients, there is an alternative list that can be accessed by clicking the link on the page labelled "Yes, give me a ciphersuite that works with legacy / old software." That list can be substituted for the items copied below.
The choice of which config you use will depend largely on what you need to support. They both will provide great security.
For our purposes, we can copy the provided settings in their entirety. We will just make two small changes.
Take a moment to read up on HTTP Strict Transport Security, or HSTS, and specifically about the "preload" functionality. Preloading HSTS provides increased security, but can have far reaching consequences if accidentally enabled or enabled incorrectly. In this guide, we will not preload the settings, but you can modify that if you are sure you understand the implications.
The other change we will make is to comment out the
SSLSessionTickets directive since this isn't available in the version of Apache shipped with CentOS 7.
Paste in the settings from the site AFTER the end of the
. . . </VirtualHost> . . . # Begin copied text # from https://cipherli.st/ # and https://raymii.org/s/tutorials/Strong_SSL_Security_On_Apache2.html SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder On # Disable preloading HSTS for now. You can use the commented out header line that includes # the "preload" directive if you understand the implications. #Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains" Header always set X-Frame-Options DENY Header always set X-Content-Type-Options nosniff # Requires Apache >= 2.4 SSLCompression off SSLUseStapling on SSLStaplingCache "shmcb:logs/stapling-cache(150000)" # Requires Apache >= 2.4.11 # SSLSessionTickets Off
When you are finished making these changes, you can save and close the file.
Next, check your configuration for syntax errors by typing:
- sudo apachectl configtest
Output. . . Syntax OK
As long as the last line reads
Syntax OK, you are safe to continue. If you do not see this, check the file for typos before continuing.
Now, restart the Apache service by typing:
- sudo systemctl restart httpd
Your server should now be configured to serve your pages using secure SSL settings.
Step 5: Check your Certificate Status
You can verify the status of your SSL certificate with the following link (don’t forget to replace example.com with your base domain):
You should now be able to access your website using a
https prefix. At the time of this writing, these settings give an A+ rating.
Step 6: Set Up Auto Renewal
Let’s Encrypt certificates are valid for 90 days, but it’s recommended that you renew the certificates every 60 days to allow a margin of error. The
certbot Let's Encrypt client has a
renew command that automatically checks the currently installed certificates and tries to renew them if they are less than 30 days away from the expiration date.
To trigger the renewal process for all installed domains, you should run:
- sudo certbot renew
Because we recently installed the certificate, the command will only check for the expiration date and print a message informing that the certificate is not due to renewal yet. The output should look similar to this:
OutputSaving debug log to /var/log/letsencrypt/letsencrypt.log ------------------------------------------------------------------------------- Processing /etc/letsencrypt/renewal/example.com.conf ------------------------------------------------------------------------------- Cert not yet due for renewal The following certs are not due for renewal yet: /etc/letsencrypt/live/example.com/fullchain.pem (skipped) No renewals were attempted.
Notice that if you created a bundled certificate with multiple domains, only the base domain name will be shown in the output, but the renewal should be valid for all domains included in this certificate.
A practical way to ensure your certificates will not get outdated is to create a cron job that will periodically execute the automatic renewal command for you. Since the renewal first checks for the expiration date and only executes the renewal if the certificate is less than 30 days away from expiration, it is safe to create a cron job that runs every week or even every day, for instance.
Let's edit the crontab to create a new job that will run the renewal command every day. To edit the crontab for the root user, run:
- sudo crontab -e
Include the following content, all in one line:
crontab. . . 30 2 * * * /usr/bin/certbot renew --renew-hook "systemctl restart httpd" >> /var/log/le-renew.log
Save and exit. This will create a new cron job that will execute the
certbot renew command every day at 2:30 am. The output produced by the command will be piped to a log file located at
/var/log/le-renewal.log. Since the command checks the expiration on the certificates locally, this does not stress the Let's Encrypt infrastructure, but allows you to update your certificates when they are within 30 days of expiring. Apache will be automatically restarted if any certificates are renewed.
Note: For more information on how to create and schedule cron jobs, you can check our How to Use Cron to Automate Tasks in a VPS guide.
In this guide, we saw how to install a free SSL certificate from Let’s Encrypt in order to secure a website hosted with Apache, on a CentOS 7 server. We recommend that you check the official Let’s Encrypt blog for important updates from time to time.