TLS 1.3 with Nginx and Apache

Sun, 2018-10-21 19:20

The much-awaited TLS 1.3 is now finalized and is a proper IETF standard: RFC 8446. A huge round of applause for everyone involved. This is a massive step forward.

Web servers and cryptographic libraries are catching up, and here is how you can enable TLS 1.3 today (yes, today)!

This site is not running TLS 1.3 yet, and all these tests were done in a different throw-away server.

SSL Labs showing TLS 1.3 support

Prerequisites

OpenSSL 1.1.1

OpenSSL 1.1.1 is out hero here and carries the heavy work with TLS 1.3. All these tests were done on Ubuntu 18.10. It was released on Oct 18 (which happens to be an important day for me, personally too). Ubuntu 18.10 comes with OpenSSL 1.1.1, so you don't have to compile OpenSSL yourself.

I highly recommend you upgrade to Ubuntu 18.10 to get OpenSSL out of Ubuntu repositories because it is too much hassle to compile OpenSSL every time an update is released.

You can check your current OpenSSL version with openssl version command. If you see OpenSSL 1.1.1  11 Sep 2018 or a later version, you are good to go!

Ubuntu 18.10

We choose Ubuntu 18.10 because it comes with OpenSSL 1.1.1. In addition, default repos contain Apache 2.4.34 and Nginx 1.15 at the time of writing.

Enable TLS 1.3 in Nginx

It doesn't take more than a minute to enable TLS 1.3 in Nginx. Nginx shipped TLS 1.3 support a few months back. All we had to do was to wait for a proper OpenSSL release that had TLS 1.3 support. OpenSSL 1.1.1 is the version we were waiting for, and it works like magic!

First, if you have not installed already, install the latest Nginx version.

apt install nginx

If you have already, make sure that the nginx version is at least 1.15:

nginx -v
# Should print "nginx version: nginx/1.15.5 (Ubuntu)" or later

It only takes a single change to enable TLS 1.3 in Nginx. Edit the /etc/nginx/nginx.conf file and locate ssl_protocols directive. You will a list of TLS versions supported. Append TLSv1.3 to the end of this list. After the changes, it should ideally look like this:

...
ssl_protocols TLSv1.2 TLSv1.3;
...

If you want to keep supporting TLS version 1.0 and 1.1, you can keep them in the list.

While you are at it, you can also enable HTTP/2, which I have explained at the post How to enable HTTP/2 support in NGINX.

Finally, all you have left to do is restart nginx, and TLS 1.3 should be working!

service nginx restart

TLS 1.3 support in Firefox
Above is a screenshot from Firefox showing TLS 1.3 support (Firefox 63). Open the Developer Panel (F12 shortcut), and go to the "Network" tab, and click on an HTTPS request that Firefox made to your server. Now, in the right-side pane, click on "Security" to see this information.

Enable TLS 1.3 in Apache

See my updated post - You don't have to compile Apache from source anymore!

Enabling TLS 1.3 in Apache is not that straight forward. Ubuntu ships Apache 2.3.34, which unfortunately does not support TLS 1.3. You can compile Apache with OpenSSL 1.1.1, but Apache will still complain that it doesn't know TLS 1.3 if you try to enable it.

The earliest version to support TLS 1.3 is 2.4.37. It is not properly released and is currently in dev version. For this, we now must compile Apache 2.4.37 from source, along with OpenSSL 1.1.1. If you are reading this post in a later time that Apache 2.4.37 is available in a repository, do not bother with the compilation steps below.

Compile Apache 2.4.37 from source

First, we have to configure APR and apr-utilpackages that Apache depends on. I'm assuming you already know what commands like cd, mkdir and wget works. If you don't, RTFM.

Compiler and Libssl dependencies

apt install build-essential libpcre3-dev libssl1.1 libssl-dev libexpat1-dev

apr

mkdir /usr/src/apache2 # This is we download apr, apr-util, and apache2 source.
cd /usr/src/apache2
wget https://archive.apache.org/dist/apr/apr-1.6.5.tar.bz2 # Lookup the latest version and use it if available https://archive.apache.org/dist/apr/.
# It's also a good idea to compare checksum, but we don't here to keep the post short and to the point.
tar -xvjf apr-1.6.5.tar.bz2
cd apr-1.6.5
./configure --prefix=/usr/local/apr/
make
make install

apt-util

cd /usr/src/apache2
wget https://archive.apache.org/dist/apr/apr-util-1.6.1.tar.bz2 # Lookup the latest version at https://archive.apache.org/dist/apr/
tar -xvjf apr-util-1.6.1.tar.bz2
cd apr-util-1.6.1
./configure --prefix=/usr/local/apr/ --with-apr=/usr/local/apr/
make
make install

Apache server

cd /usr/src/apache2
wget https://httpd.apache.org/dev/dist/httpd-2.4.37.tar.bz2
tar -xvjf httpd-2.4.37.tar.bz2
cd httpd-2.4.37

The next step is to configure Apache before the compilation step. Note that you will need tinker these configuration options to suit your needs. These settings do not correlate 1:1 with the apache2 package you install in a repo, and I do not recommend you use this compiled version in production severs because 2.4.37 is not a proper release yet.

./configure --prefix=/opt/apache2  \
    --with-apr=/usr/local/apr/bin/apr-1-config \
   --with-apr-util=/usr/local/apr/bin/apu-1-config \
   --enable-layout=Debian \
   --enable-mpms-shared=event \
   --enable-mods-shared=all \
   --enable-nonportable-atomics=yes

See if the configuration options throw any errors. You might need to fix them manually before trying again. You can see the config log at config.log file. Note that if SSL requirements are not met, mod_ssl module will not build. Check the config.log file for any signs that the configure script complains about it.

make
make install

This will now install Apache2 in /opt/apache2 directory.

./opt/apache2/bin/httpd -k start

Above command will start the server.

To enable TLS 1.3 in your just-compiled Apache, you can edit /opt/apache2/httpd.conf file and make sure the SSLProtocol is setup correctly.

SSLProtocol all -SSLv3 -TLSv1, -TLSv1.1

Above is my recommended setup that it disables TLS 1.0 and 1.1 along SSL 3, leaving TLS 1.2 and TLS 1.3 active.
You can restart the server to check if Apache can now make TLS 1.3 connections! (You will need to have a proper TLS certificate and the basic TLS setup done too of course).

Similar to Nginx, I will also link to How to enable HTTP/2 support in Apache post if you do not already have HTTP/2 enabled.

TLS 1.3 support in Chrome
TLS 1.3 support in Chrome 70+. By default, this setup will use x25519 for key exchange, and AES_GCM / CHACHA20_POLY1305 for symmetric encryption.