Ubuntu Server 18.04 – Securing Apache with SSL

How to Install Docker on macOS

Nowadays, it’s a great idea to ensure your organization’s website is encrypted and available over HTTPS. Encrypting your web traffic is not that hard to do and will help protect your organization against common exploits. Utilizing SSL doesn’t protect you from all exploits being used in the wild, but it does offer a layer of protection you’ll want to benefit from. Not only that, but your customers pretty much expect you to secure their communications nowadays. In this section, we’ll look at how to use SSL with our Apache installation. We’ll work through enabling SSL, generating certificates, and configuring Apache to use those certificates with both a single site configuration and with virtual hosts.

By default, Ubuntu’s Apache configuration listens for traffic on port 80, but not port 443 (HTTPS). You can check this yourself by running the following command:

sudo netstat -tulpn |grep apache 

The results will look similar to the following and will show the ports that Apache is listening on, which is only port 80 by default:

tcp6       0      0 :::80     :::*         LISTEN      2791/apache2 

If the server were listening on port 443 as well, we would’ve seen the following output instead:

tcp6       0      0 :::80       :::*    LISTEN      3257/apache2 
tcp6       0      0 :::443      :::*        LISTEN      3257/apache2 

To enable support for HTTPS traffic, we need to first enable the ssl module:

sudo a2enmod ssl 

Next, we need to restart Apache:

sudo systemctl restart apache2 

In addition to the sample website we discussed earlier, Ubuntu’s default Apache implementation also includes another site configuration file, /etc/apache2/sites-available/default-ssl.conf. Unlike the sample site, this one is not enabled by default. This configuration file is similar to the sample site configuration but it’s listening for connections on port 443 and contains additional configuration items related to SSL. Here’s the content of that file, with the comments stripped out in order to save space on this page:

<IfModule mod_ssl.c> 
        <VirtualHost _default_:443> 
                ServerAdmin webmaster@localhost 
                DocumentRoot /var/www/html 
                ErrorLog ${APACHE_LOG_DIR}/error.log 
                CustomLog ${APACHE_LOG_DIR}/access.log combined 
                SSLEngine on 
        SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem 
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key 
                <FilesMatch ".(cgi|shtml|phtml|php)$"> 
                                SSLOptions +StdEnvVars 
                <Directory /usr/lib/cgi-bin> 
                                SSLOptions +StdEnvVars 

We’ve already gone over the ServerAdmin, DocumentRoot, ErrorLog, and CustomLog options earlier in this chapter, but there are additional options in this file that we haven’t seen yet. On the first line, we can see that this virtual host is listening on port 443. We also see _default_ listed here instead of an IP address. The _default_ option only applies to unspecified traffic, which in this case means any traffic coming in to port 443 that hasn’t been identified in any other virtual host. In addition, the SSLEngine on option enables SSL traffic. Right after that, we have options for our SSL certificate file and key file, which we’ll get to a bit later.

We also have a <Directory> clause, which allows us to apply specific options to a directory. In this case, the /usr/lib/cgi-bin directory is being applied to the SSLOptions +StdEnvVars setting, which enables default environment variables for use with SSL. This option is also applied to files that have an extension of .cgi, .shtml, .phtml, or .php through the <FilesMatch> option. The BrowserMatch option allows you to set options for specific browsers, though it’s out of scope for this chapter. For now, just keep in mind that if you want to apply settings to specific browsers, you can.

By default, the default-ssl.conf file is not enabled. In order to benefit from its configuration options, we’ll need to enable it, which we can do with the a2ensite command as we would with any other virtual host:

sudo a2ensite default-ssl.conf 

Even though we just enabled SSL, our site isn’t secure just yet. We’ll need SSL certificates installed in order to secure our web server. We can do this in one of two ways, with self-signed certificates, or certificates signed by a certificate authority. Both are implemented in very similar ways, and I’ll discuss both methods. For the purposes of testing, self-signed certificates are fine. In production, self-signed certificates would technically work, but most browsers won’t trust them by default, and will give you an error when you go to their page. Therefore, it’s a good idea to refrain from using self-signed certificates on a production system. Users of a site with self-signed certificates would need to bypass an error page before continuing to the site and seeing this error may cause them to avoid your site altogether. You can install the certificates into each user’s web browser, but that can be a headache. In production, it’s best to use certificates signed by a vendor.

As we go through this process, I’ll first walk you through setting up SSL with a self-signed certificate so you can see how the process works. We’ll create the certificate, and then install it into Apache. You won’t necessarily need to create a website to go through this process, since you could just secure the sample website that comes with Apache if you wanted something to use as a proof of concept. After we complete the process, we’ll take a look at installing certificates that were signed by a certificate authority.

To get the ball rolling, we’ll need a directory to house our certificates. I’ll use /etc/apache2/certs in my examples, although you can use whatever directory you’d like, as long as you remember to update Apache’s configuration with your desired location and filenames:

sudo mkdir /etc/apache2/certs 

For a self-signed certificate and key, we can generate the pair with the following command. Feel free to change the name of the key and certificate files to match the name of your website:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/apache2/certs/mysite.key -out /etc/apache2/certs/mysite.crt 

You’ll be prompted to enter some information for generating the certificate. Answer each prompt as they come along. Here’s a list of the questions you’ll be asked, along with my responses for each. Change the answers to fit your server, environment, organization name, and location:

Country Name (2 letter code) [AU]:US 
State or Province Name (full name) [Some-State]:Michigan 
Locality Name (eg, city) []:Detroit 
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Company 
Organizational Unit Name (eg, section) []:IT 
Common Name (e.g. server FQDN or YOUR name) []:myserver.mydomain.com 
Email Address []:webmaster@mycompany.com 

Now, you should see that two files have been created in the /etc/apache2/certs directory, mysite.crt and mysite.key, which represent the certificate and private key respectively. Now that these files have been generated, the next thing for us to do is to configure Apache to use them. Look for the following two lines in the /etc/apache2/sites-available/default-ssl.conf file:

SSLCertificateFile       /etc/ssl/certs/ssl-cert-snakeoil.pem 
SSLCertificateKeyFile  /etc/ssl/private/ssl-cert-snakeoil.key 

Comment these lines out by placing a # symbol in front of both:

# SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem 
# SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key 

Next, add the following two lines underneath the lines you just commented out. Be sure to replace the target directories and certificate file names with yours, if you followed your own naming convention:

SSLCertificateFile      /etc/apache2/certs/mysite.crt 
SSLCertificateKeyFile /etc/apache2/certs/mysite.key 

To make Apache benefit from the new configuration, reload the apache2 daemon:

sudo systemctl reload apache2 

With the new configuration in place, we’re not quite done but close. We still have a small bit of configuration left to add. But before we get to that, let’s return to the topic of installing SSL certificates that were signed by a certificate authority. The process for installing signed SSL certificates is pretty much the same, but the main difference is how the certificate files are requested and obtained. Once you have them, you will copy them to your file server and configure Apache the same way as we just did. To start the process of obtaining a signed SSL certificate, you’ll need to create a Certificate Signing Request (CSR). A CSR is basically a request for a certificate in file form that you’ll supply to your certificate authority to start the process of requesting a signed certificate. A CSR can be easily generated with the following command:

openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr 

With the CSR file that was generated, you can request a signed certificate. The CSR file should now be in your current working directory. The entire process differs from one provider to another, but in most cases it’s fairly straightforward. You’ll send them the CSR, pay their fee, fill out a form or two on their website, prove that you are the owner of the website in question, and then the vendor will send you the files you need. It may sound complicated, but certificate authorities usually walk you through the entire process and make it clear what they need from you in order to proceed. Once you complete the process, the certificate authority will send you your certificate files, which you’ll then install on your server. Once you configure the SSLCertificateFile and SSLCertificateKeyFile options in /etc/apache2/sites-available/default-ssl.conf to point to the new certificate files and reload Apache, you should be good to go.

There’s one more additional step we should perform for setting this up properly. At this point, our certificate files should be properly installed, but we’ll need to inform Apache of when to apply them. If you recall, the default-ssl.conf file provided by the apache2 package is answering requests for any traffic not otherwise identified by a virtual host (the <VirtualHost _default_:443> option). We will need to ensure our web server is handling traffic for our existing websites when SSL is requested. We can add a ServerName option to that file to ensure our site supports SSL.

Add the following option to the /etc/apache2/sites-available/default-ssl.conf file, right underneath <VirtualHost _default_:443>:

ServerName mydomain.com:443 

Now, when traffic comes in to your server on port 443 requesting a domain that matches the domain you typed for the ServerName option, it should result in a secure browsing session for the client. You should see the green padlock icon in the address bar (this depends on your browser), which indicates your session is secured. If you’re using self-signed certificates, you’ll probably see an error you’ll have to skip through first, and you may not get the green padlock icon. This doesn’t mean the encryption isn’t working, it just means your browser is skeptical of the certificate since it wasn’t signed by a known certificate authority. Your session will still be encrypted.

If you are planning on hosting multiple websites over HTTPS, you may want to consider using a separate virtual host file for each. An easy way to accomplish this is to use the /etc/apache2/sites-available/default-ssl.conf file as a template and change the DocumentRoot to the directory that hosts the files for that site. In addition, be sure to update the SSLCertificateFile and SSLCertificateKeyFile options to point to the certificate files for the site and set the ServerName to the domain that corresponds to your site. Here’s an example virtual host file for a hypothetical site that uses SSL. I’ve highlighted lines that I’ve changed from the normal default-ssl.conf file:

<IfModule mod_ssl.c> 
        <VirtualHost *:443> 
                ServerName acmeconsulting.com:443 
               ServerAdmin webmaster@localhost 
                DocumentRoot /var/www/acmeconsulting 
                ErrorLog ${APACHE_LOG_DIR}/acmeconsulting.com-error.log 
                CustomLog ${APACHE_LOG_DIR}/acmeconsulting.com-access.log combined 
                SSLEngine on 
        SSLCertificateFile      /etc/apache2/certs/acmeconsulting/acme.crt 
        SSLCertificateKeyFile /etc/apache2/certs/acmeconsulting/acme.key 
                <FilesMatch ".(cgi|shtml|phtml|php)$"> 
                                SSLOptions +StdEnvVars 
                <Directory /usr/lib/cgi-bin> 
                                SSLOptions +StdEnvVars 

Basically, what I did was create a new virtual host configuration file (using the existing default-ssl.conf file as a template). I called this new file acme-consulting.conf and I stored it in the /etc/apache2/sites-available directory. I changed the VirtualHost line to listen for anything coming in on port 443. The line ServerName acmeconsulting.com:443 was added, to make this file responsible for traffic coming in looking for acmeconsulting.com on port 443. I also set the DocumentRoot to /var/www/acmeconsulting. In addition, I customized the error and access logs so that it will be easier to find log messages relating to this new site, since its log entries will go to their own specific files.

In my experience, I find that a modular approach, such as what I’ve done with the sample virtual host file for HTTPS, works best when setting up a web server that’s intended to host multiple websites. With each site, I’ll typically give them their own Document Root, certificate files, and log files. Even if you’re only planning on hosting a single site on your server, using this modular approach is still a good idea, since you may want to host additional sites later on.

So, there you have it. You should now understand how to set up secure virtual hosts in Apache. However, security doesn’t equal redundancy.

Comments are closed.