TLS 1.3 is on its way to a webserver near you, but it may be a while before major sites begin supporting it. It takes a bit of time for a new version of anything to take hold, and even longer if it’s the first new version of a protocol in nearly 10 years.
Fortunately you don’t have to wait to start experimenting with TLS 1.3; all you need is OpenSSL 1.1.1 and open source NGINX 1.15 (currently the mainline version), and you’re good to go.
OpenSSL
OpenSSL 1.1.1 is the first version to support TLS 1.3 and its ciphers:
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
- TLS_AES_128_GCM_SHA256
- TLS_AES_128_CCM_8_SHA256
- TLS_AES_128_CCM_SHA256
Since 1.1.1 is available out-of-the-box in Ubuntu 18.10 Cosmic Cuttlefish (as well as FreeBSD 12.0 and Alpine 3.9), we’ll be using it for this tutorial. Note that 18.10 is not an LTS release, and the decision was made to port to OpenSSL 1.1.1 to 18.04 (Bionic Beaver), but it did not make it in 18.04.2. We like to make things easy on ourselves, and launched a publicly available ubuntu-cosmic-18.10-amd64-server-20181018 AMI in AWS.
NGINX
NGINX hardly needs an introduction, so we’ll skip straight to its support for TLS 1.3, which came all the way back in version 1.13.0 (August 2017), well before the protocol was finalized. Combined with OpenSSL 1.1.1, the current open source version (1.15), NGINX is fully capable of supporting TLS 1.3, including 0-RTT.
Current Browser Support for TLS 1.3
TLS 1.3 will be a moving target for months to come, but as of this writing (February 23, 2018), here’s a view of browser support for it. As you can see, it’s pretty limited at this point, with only the Chrome, Brave, and Firefox browsers capable of establishing a connection with a TLS 1.3-only webserver.
OS | Browser | TLS 1.3 Support | Negotiated Cipher |
---|---|---|---|
macOS 10.14.3 | Chrome 72.0.3626.109 | Yes | TLS_AES_256_GCM_SHA384 |
macOS 10.14.3 | Firefox 65.0.1 | Yes | TLS_AES_256_GCM_SHA384 |
macOS 10.14.3 | Brave 0.59.35 | Yes | TLS_AES_256_GCM_SHA384 |
macOS 10.14.3 | Safari 12.0.3 (14606.4.5) | No | NA |
macOS 10.14.4 | Safari 12.1 | Yes | TLS_AES_256_GCM_SHA384 |
iOS 12.2 (Beta) | Safari | Yes | TLS_AES_256_GCM_SHA384 |
Windows 10.0.17134 | IE 11.345.17134.0 | No | NA |
Windows 10.0.17134 | Edge 17.17134 | No | NA |
Ubuntu 18.10 | curl/7.61.0 | Yes | TLS_AES_256_GCM_SHA384 |
Ubuntu 18.04.2 | curl/7.58.0 | No | NA |
Note: An astute reader might notice iOS 12.2 (currently in Beta) indeed supports TLS 1.3 and our webserver confirms it!
Testing It Out
To test things out, we’ll turn to our favorite automation tool, Ansible and our tls13_nginx_cosmic repository with playbooks.
We happened to use an EC2 instance running Ubuntu 18.10, as well as Let’s Encrypt and Digital Ocean‘s Domain Records API. That’s a fair number of dependencies, but an enterprising DevOps professional should be able to take our example playbooks and scripts and modify them to suit their needs.
Rather than return HTML content (content-type: text/html
), we return text/plain
with interesting information from NGINX itself. This is facilitated by the LUA programming language and LUA NGINX module. The magic is here in our nginx.conf
:
1 2 3 4 5 6 7 8 9 10 |
location / { default_type 'text/plain'; content_by_lua_block { ngx.say("Thanks for connecting to {{ HOSTNAME }}"); ngx.say(""); ngx.say(" Client User Agent: " .. ngx.var.http_user_agent); ngx.say("Client supported ciphers: " .. ngx.var.ssl_ciphers); ngx.say(" Selected cipher: " .. ngx.var.ssl_cipher); } } |
This results in output similar to:
1 2 3 4 5 |
Thanks for connecting to tls13.iachieved.it Client User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 Client supported ciphers: 0x2a2a:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:0x000a Selected cipher: TLS_AES_256_GCM_SHA384 |
In all of our tests thus far, TLS_AES_256_GCM_SHA384 was chosen as the ciphersuite.
Qualys SSL Assessment
Now let’s look at what Qualys SSL Server Test has to say about our site.
Not an A+, but notice in our nginx.conf
we are not configuring HSTS or OCSP. Our standard Let’s Encrypt certificate is also hampering our score here.
Here’s what Qualys has to say about our server configuration:
The highlight here is that TLS 1.3 is supported by our server, whereas TLS 1.2 is not. This was done on purpose to not allow a connecting client to use anything but TLS 1.3. You definitely would not do this in practice as of February 2019, as the Qualys Handshake Simulation shows. Only Chrome 70 was able to connect to our server.
Closing Thoughts
As a DevOps practitioner, and someone who manages dozens of webservers professionally, I’m quite excited about the release and adoption of TLS 1.3. It will, no doubt, take quite some time before a majority of browsers and sites support it.
If you’re interested more about TLS 1.3 in general, there are a lot of great resources out there. Here are just a few:
Wikipedia has a good rundown of TLS 1.3 features and changes from TLS 1.2.
The folks at NGINX recently hosted a webinar on R17, the latest NGINX Plus version. TLS 1.3 and it’s benefits were covered in more detail.
https://www.slideshare.net/Nginx/tls-13-and-other-new-features-in-nginx-plus-r17-and-nginx-open-source
Here’s a great tutorial on deploying modern TLS configurations (including 1.3) from Probely.
And, last but not least, Cloudflare has a number of in-depth TLS 1.3 articles.