HOWTO
WWW
INSTALL APACHE HTTPS

Published: 20200929

Tested on:
* Ubuntu Server 20.04.1 on a VMware VM.
* Ubuntu for IoT 20.04.1 (arm64) on a Raspberry Pi 4i Model B 4GB.

-

INDEX
01. Requirements
02. Install Apache and setup an HTTP (unencrypted) website
03. Setup a "Virtual Host"
04. Install a certificate (with Let's Encrypt)
05. Setup an HTTPS (encrypted) website
06. Setup a 301 redirect
07. Setup HSTS

-

  1. Requirements

    In this example I'll set it up dual-stack, but just using one of the addresses will work.

    In this article I will assume that the following is already setup:

  1. Install Apache and setup an HTTP (unencrypted) website

    $ sudo apt -y install apache2
    

    There! Your webserver is finished.
    If you surf to it you will be greeted with the "Apache Ubuntu Default Page" (located in /var/www/html/index.html on your server).

  1. Setup a "Virtual Host"

    The strength with Apache (and many other webservers) is it's ability to serve multiple homepages from the same IP-address. These different pages are called "Virtual Hosts". We will setup a specific page (Virtual Host configuration) for our FQDN test.example.com.

    Create a dir for your website:

    $ sudo mkdir -p /home/www/test.example.com
    

    Create a virtual host configuration for your website:

    $ sudo vi /etc/apache2/sites-available/test.example.com.conf
    

    Put this inside the file:

    <VirtualHost *:80>
      ServerAdmin   webmaster@example.com
      DocumentRoot  /home/www/test.example.com
      ServerName    test.example.com
      CustomLog     /var/log/apache2/test.example.com-access.log combined
      ErrorLog      /var/log/apache2/test.example.com-error.log
      <Directory /home/www/test.example.com>
        AllowOverride all
        Require all granted
      </Directory>
    </VirtualHost>
    

    Create some content for your website:

    $ sudo vi /home/www/test.example.com/index.html
    

    Put this inside the file:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>testpage</title>
      </head>
      <body>
        <p>
          You've reached test.example.com via http
        </p>
      </body>
    </html>
    

    Activate your site with:

    $ sudo a2ensite test.example.com
    Enabling site test.example.com.
    To activate the new configuration, you need to run:
      systemctl reload apache2
    $ sudo systemctl reload apache2
    $ 
    

    Your new website should now be working.

    If you surf to http://test.example.com/ you will reach your new page.
    If you surf to http://[2001:db8::5]/ or http://192.0.2.5/ you will reach the Apache Ubuntu Default Page.

    If you bump into problems, you might get clues with this command:

    $ sudo systemctl status apache2
    

    Important! Make sure that there are no collisions (in /etc/hosts) between the machine name and the FQDN of the site you're setting up. I don't recommend using the machine name for a virtual host. If you do anyway, you have to really know what you're doing.

    Another common problem is file or dir permissions. The webserver will access the filesystem as a specific user. In the case of Ubuntu this, user is called www-data with UID 33 and is in group www-data with GID 33. The webserver can be joined in other groups as well. If the files you are creating (i.e. the index.html) is not readable or reachable by the webserver, things will not work. You might see something like: "Forbidden You don't have permission to access this resource" on your webpage.

  1. Install a certificate (with Let's Encrypt)

    You don't have to use Let's Encrypt.
    You can use manually installed keys and certificates (i.e. bought from an SSL Certificate vendor).
    You could also create your own CA

    Install certbot

    $ sudo apt -y install certbot python3-certbot-apache
    

    Run certbot to generate a certificate for your website.

    Certbot will contact the Let's Encrypt servers and temporarily do some changes to apache. This is in order to verify that you are the owner (in control) of the FQDN test.example.com.

    Yes, this can be abused by someone in between you and your ISP.
    If you really care about security over https you need to look into certificate pinning, or maybe skip http/https altogether.

    Anyhow, let's continue:

    $ sudo certbot certonly --apache -d test.example.com -m webmaster@example.com
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Plugins selected: Authenticator apache, Installer apache
         
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    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: y
    Obtaining a new certificate
    Performing the following challenges:
    http-01 challenge for test.example.com
    Enabled Apache rewrite module
    Waiting for verification...
    Cleaning up challenges
         
    IMPORTANT NOTES:
     - Congratulations! Your certificate and chain have been saved at:
       /etc/letsencrypt/live/test.example.com/fullchain.pem
       Your key file has been saved at:
       /etc/letsencrypt/live/test.example.com/privkey.pem
       Your cert will expire on 2020-12-27. 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"
     - Your account credentials have been saved in your Certbot
       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 Certbot so
       making regular backups of this folder is ideal.
     - 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
         
    $ 
    

    If you're using IPv6-only, the command above might fail.
    If that happens, try running the exact same command again. It often works, but at the time of writing this, I don't know why.

    We are almost done.
    We need to setup a small cronjob that automatically renews the certificates:

    $ sudo mkdir /scripts
    $ sudo vi /scripts/run_certbot.sh
    

    Enter this into /scripts/run_certbot.sh

    #!/bin/sh
    certbot renew
    

    Make sure that the script works:

    $ sudo chmod 0700 /scripts/run_certbot.sh
    $ sudo /scripts/run_certbot.sh
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
         
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Processing /etc/letsencrypt/renewal/test.example.com.conf
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Cert not yet due for renewal
         
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
         
    The following certs are not due for renewal yet:
      /etc/letsencrypt/live/test.example.com/fullchain.pem expires on 2020-12-27 (skipped)
    No renewals were attempted.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    $ 
    

    Enable the script on crontab:

    $ sudo crontab -e
    

    Enter this at the bottom on the crontab file you just opened:

    0 */12 * * * /scripts/run_certbot.sh
    

    Save the file and verify that your script is now in the crontab:

    $ sudo crontab -l
    # Edit this file to introduce tasks to be run by cron.
    # 
    # Each task to run has to be defined through a single line
    # indicating with different fields when the task will be run
    # and what command to run for the task
    # 
    # To define the time you can provide concrete values for
    # minute (m), hour (h), day of month (dom), month (mon),
    # and day of week (dow) or use '*' in these fields (for 'any').
    # 
    # Notice that tasks will be started based on the cron's system
    # daemon's notion of time and timezones.
    # 
    # Output of the crontab jobs (including errors) is sent through
    # email to the user the crontab file belongs to (unless redirected).
    # 
    # For example, you can run a backup of all your user accounts
    # at 5 a.m every week with:
    # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
    # 
    # For more information see the manual pages of crontab(5) and cron(8)
    # 
    # m h  dom mon dow   command
    0 */12 * * * /scripts/run_certbot.sh
    $ 
    

    We now have the certificate files we need to setup HTTPS.

  1. Setup an HTTPS (encrypted) website

    Enable the SSL module:

    $ sudo a2enmod ssl
    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
    $ sudo systemctl restart apache2
    $
    

    From the perspective of the network and the webserver (Apache), the http website and the https website are two different websites. They respond on different TCP ports and they can be configured to be very different. You can have the same content and the even point to the same dir; but as we will see soon: it is beneficiary to have two separate dirs.

    Create a dir for your https website:

    $ sudo mkdir -p /home/wwws/test.example.com
    

    Create a virtual host configuration for your website:

    $ sudo vi /etc/apache2/sites-available/test.example.com-ssl.conf
    

    Put this inside the file:

    <VirtualHost *:443>
      ServerAdmin   webmaster@example.com
      DocumentRoot  /home/wwws/test.example.com
      ServerName    test.example.com
         
      SSLEngine on
      SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
      SSLCertificateFile      /etc/letsencrypt/live/test.example.com/cert.pem
      SSLCertificateKeyFile   /etc/letsencrypt/live/test.example.com/privkey.pem
      SSLCACertificateFile    /etc/letsencrypt/live/test.example.com/fullchain.pem
         
      CustomLog     /var/log/apache2/test.example.com-ssl-access.log combined
      ErrorLog      /var/log/apache2/test.example.com-ssl-error.log
      <Directory    /home/wwws/test.example.com>
        AllowOverride all
        Require all granted
      </Directory>
    </VirtualHost>
    

    Create some content for your website:

    $ sudo vi /home/wwws/test.example.com/index.html
    

    Put this inside the file:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>testpage</title>
      </head>
      <body>
        <p>
          You've reached test.example.com via https
        </p>
      </body>
    </html>
    

    Activate your site with:

    $ sudo a2ensite test.example.com-ssl.conf 
    Enabling site test.example.com-ssl.
    To activate the new configuration, you need to run:
      systemctl reload apache2
    $ sudo systemctl reload apache2
    $ 
    

    Your new website should now be working.

    If you surf to https://test.example.com/ you will reach your new page. You can press the little padlock next to the url and view your certificate.

    If you surf to https://[2001:db8::5]/ or https://192.0.2.5/ you will be greated with a broken cert page.
    This is because your virtual host of the https page is setup for test.example.com. If you surf to the server using a different address the server will try to respond, but it will respond with what it has. In this case with the certificate of test.example.com and the content you've setup for this page. The certificate simply doesn't match the address you used.

  1. Setup a 301 redirect.

    It can be confusing to have both an http site and an https site at the same time. But we haven't wasted our time setting up both. The http site is a good way to setup a permanent redirection to our https site.

    This means that anyone who tries to reach our http homepage will always be redirected to the right one.

    So we will add a small file to our http page, that will tell all web browsers and search engine crawlers where they can find our real page.

    We will add a so called "301 redirect" aka permanent redirection.

    First we enable the rewrite engine module:

    $ sudo a2enmod rewrite
    Enabling module rewrite.
    To activate the new configuration, you need to run:
      systemctl restart apache2
    $ sudo systemctl restart apache2
    $ 
    

    Then we add/edit a small file:

    $ sudo vi /home/www/test.example.com/.htaccess
    

    Put this inside the file:

    RewriteEngine on
    Redirect 301 / https://test.example.com/
    

    Done!
    The redirect is now in place.

  1. Setup HSTS

    We will also add HSTS (HTTP Strict Transport Security) to the https site.

    This is a small configuration, that inserts a header of information, into the web traffic of your https page. This header tells all visitors to not accept unencrypted http traffic, but to only accept encrypted https traffic. This setting will be cached into all the visitors' web browsers.

    The benefit of this is to mitigate (prevent) downgrade attacks.

    In this type of attack, a malicious hacker tries to poison the web traffic, to trick the users to allow unencrypted http web traffic together with your encrypted https page. This way they can snoop and attack the users.

    So, let's add the HSTS header!

    First we enable the headers module:

    $ sudo a2enmod headers
    Enabling module headers.
    To activate the new configuration, you need to run:
      systemctl restart apache2
    $ sudo systemctl restart apache2
    $ 
    

    Then we add/edit a small file:

    $ sudo vi /home/wwws/test.example.com/.htaccess
    

    Put this inside the file:

    Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    

    Do note the max-age value. In this case I've set it to 1 year.

    If you give it a low value (like an hour or a day), it will be easy to fix mistakes (if you are uncertain if https is for you). But when you're doing something critical (and most encryption projects are critical) you want this value to be high. If the value is low, a malicious hacker can just wait out your max-age value and then change the HSTS settings and perform a downgrade attack.

    Done!

    After you've surfed to your https page, it will have stored this setting. So future attempts to visit the http page will just not work, your browser will recognize the cached HSTS settings of your page and just go to the https page instead.