Improving NGINX Performance with Kernel TLS and SSL_sendfile( )
table.nginx-blog, table.nginx-blog th, table.nginx-blog td {
border: 2px solid black;
border-collapse: collapse;
}
table.nginx-blog {
width: 100%;
}
table.nginx-blog th {
background-color: #d3d3d3;
align: left;
padding-left: 5px;
padding-right: 5px;
padding-bottom: 2px;
padding-top: 2px;
line-height: 120%;
}
table.nginx-blog td {
padding-left: 5px;
padding-right: 5px;
padding-bottom: 2px;
padding-top: 5px;
line-height: 120%;
}
table.nginx-blog td.center {
text-align: center;
padding-bottom: 2px;
padding-top: 5px;
line-height: 120%;
}
Transport Layer Security (TLS) is an extremely popular cryptography protocol. Implementing TLS in the kernel (kTLS) improves performance by significantly reducing the need for copying operations between user space and the kernel.
Combining kTLS and sendfile()
means data is encrypted directly in kernel space, before being passed to the network stack for transmission. This eliminates the need to copy data into user space to be encrypted by TLS libraries and then back into kernel space for transmission. kTLS also enables offload of TLS processing to hardware, including offload of TLS symmetric crypto processing to network devices.
Modern Linux and FreeBSD kernels support offloading TLS to the kernel, and now NGINX Open Source does too! NGINX 1.21.4 introduces support for kTLS when serving static files with SSL_sendfile()
, which can hugely improve performance. As detailed below, both the kernel and OpenSSL must be built with kTLS for NGINX to use SSL_sendfile()
.
In this blog we detail which operating system and OpenSSL versions support kTLS, and show how to build and configure the kernel and NGINX for kTLS. To give you an idea of the performance improvement you can expect from kTLS, we also share the specs and results of our testing on FreeBSD and Ubuntu.
Note: kTLS implementations are quite new and evolving rapidly. This blog describes support for kTLS as of November 2021, but keep an eye out for announcements on nginx.org and the NGINX blog about changes to the information and instructions provided here.
General Requirements
-
Operating system – Either of:
-
FreeBSD 13.0+. As of November 2021, FreeBSD 13.0+ is the only OS that supports kTLS in NGINX without a manual build of NGINX to incorporate OpenSSL 3.0.0+. See Enabling NGINX with kTLS on FreeBSD.
-
A Linux distribution built on Linux kernel version 4.17 or later, though we recommend using those built on version 5.2 or later when possible. (kTLS support is actually available in version 4.13, but OpenSSL 3.0.0 requires kernel header version 4.17 or later.)
-
-
OpenSSL – Version 3.0.0 or later
-
NGINX – Version 1.21.4 or later (mainline)
Operating System Support
OSs That Support kTLS
As of November 2021, of the OSs supported by NGINX Open Source the following support kTLS and the indicated ciphers. For details about cipher support, see TLS Protocol and Cipher Support.
TLSv1.2 ciphers | TLSv1.3 cipher suites |
TLS_CHACHA20_POLY1305_SHA256 cipher |
Linux kernel version | |
---|---|---|---|---|
Amazon Linux 2* | ✅ | ✅ | ❌ | 5.10 |
CentOS 8** | ✅ | ❌ | ❌ | 4.18 |
FreeBSD 13.0 | ✅ | ✅ | ❌ *** | N/A |
RHEL 8 | ✅ | ❌ | ❌ | 4.18 |
SLES 15 SP2 | ✅ | ✅ | ✅ | 5.3 |
Ubuntu 20.04 LTS | ✅ | ❌ | ❌ | 5.4 |
Ubuntu 21.04 | ✅ | ✅ | ✅ | 5.11 |
Ubuntu 21.10 | ✅ | ✅ | ✅ | 5.13 |
* Kernel version must be 5.10, not 4.14; see OSs That Do Not Support kTLS and the Amazon Linux 2 FAQ
** Inherits its kTLS support status from RHEL 8 as its upstream source
*** See the FreeBSD commit log
OSs That Do Not Support kTLS
The following OSs do not support kTLS, for the indicated reason:
- Alpine Linux 3.11–3.14 – Kernel is built with the
CONFIG_TLS=n
option, which disables building kTLS as a module or as part of the kernel. - Amazon Linux 2 – Linux kernel version is 4.14 for the default Amazon Linux 2 AMI (see the Amazon Linux 2 FAQ).
- CentOS 7.4+ – Linux kernel version is 3.10. Inherits its kTLS support status from RHEL 7.4+ as its upstream source.
- Debian 10 and 11 – Kernel is built with the
CONFIG_TLS=n
option (see the Debian bug report logs). - RHEL 7.4+ – Linux kernel version is 3.10.
- SLES 12 SP5+ – Linux kernel version is 4.12.
- Ubuntu 18.04 LTS – Linux kernel version is 4.15.
TLS Protocol and Cipher Support
As detailed above, OSs that support kTLS vary in their support for TLS protocols and ciphers.
With TLSv1.2, the kTLS module supports these ciphers:
AES128-GCM-SHA256
AES256-GCM-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
With TLSv1.3, the kTLS module supports these cipher suites:
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
(only some OSs, as specified in OSs That Support kTLS)
To verify which TLS ciphers supported by OpenSSL are enabled in your NGINX binary, run the openssl-3.0.0/.openssl/bin/openssl
ciphers
command in the directory where you built NGINX (for example, your home directory).
Enabling kTLS in NGINX
As mentioned in the introduction, kTLS improves NGINX performance because all encryption and decryption takes place in the kernel. Data is encrypted directly in kernel space – before being passed to the network stack for transmission – eliminating the need to copy data into user space to be encrypted by TLS libraries and then back into kernel space for transmission.
Loading kTLS in the Kernel
In modern FreeBSD and Linux distributions, kTLS is usually built as a module (with the CONFIG_TLS=m
option). You must explicitly load the kTLS module into the kernel before you start NGINX.
-
On FreeBSD, run these commands as the
root
user:# kldload ktls ocf # sysctl kern.ipc.tls.enable=1
For details about the FreeBSD command options, see the man page for
ktls(4)
. -
On Linux distributions, run this command as the
root
user:# modprobe tls
Enabling NGINX with kTLS on FreeBSD
To enable kTLS support in NGINX on FreeBSD, you may use the same instructions as for Linux distributions. However, we recommend that you perform the following steps to leverage the build of NGINX with kTLS in the openssl-devel tree in the FreeBSD Ports Collection. For more information, including an overview of kTLS, see TLS Offload in the Kernel on the FreeBSD website.
-
Build OpenSSL 3.0 with kTLS support, selecting the appropriate options in the config menu:
# cd /usr/ports/security/openssl-devel & make config & make install
-
Modify /etc/make.conf to use openssl-devel as the default SSL library:
# echo "DEFAULT_VERSIONS+=ssl=openssl-devel >> /etc/make.conf
-
Build NGINX:
# cd /usr/ports/security/openssl-devel & make install
Building NGINX with kTLS on Linux Distributions
Most current Linux distributions include an OpenSSL version earlier than 3.0.0 (commonly, version 1.1). So you need to build NGINX from source with OpenSSL 3.0.0.
The two crucial options on the configure
command that enable kTLS support are:
--with--openssl=../openssl-3.0.0
--with-openssl--opt=enable-ktl
The other configure
options are for the modules included in the official NGINX binary packages available at nginx.org. You may specify a custom set of modules instead. To see the build options used for your current NGINX binary, run nginx
-V
.
To build NGINX with OpenSSL 3.0.0, run the following commands:
$ wget https://nginx.org/download/nginx-1.21.4.tar.gz
$ wget https://www.openssl.org/source/openssl-3.0.0.tar.gz
$ tar xzf openssl-3.0.0.tar.gz
$ cd nginx-1.21.4
$ ./configure
--with-debug
--prefix=/usr/local
--conf-path=/usr/local/etc/nginx/nginx.conf
--error-log-path=/var/log/nginx/error.log
--http-log-path=/var/log/nginx/access.log
--pid-path=/var/run/nginx.pid
--lock-path=/var/run/nginx.lock
--http-client-body-temp-path=/var/cache/nginx/client_temp
--http-proxy-temp-path=/var/cache/nginx/proxy_temp
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp
--http-scgi-temp-path=/var/cache/nginx/scgi_temp
--user=nginx
--group=nginx
--with-compat
--with-file-aio
--with-threads
--with-http_addition_module
--with-http_auth_request_module
--with-http_dav_module
--with-http_flv_module
--with-http_gunzip_module
--with-http_gzip_static_module
--with-http_mp4_module
--with-http_random_index_module
--with-http_realip_module
--with-http_secure_link_module
--with-http_slice_module
--with-http_ssl_module
--with-http_stub_status_module
--with-http_sub_module
--with-http_v2_module
--with-mail
--with-mail_ssl_module
--with-stream
--with-stream_realip_module
--with-stream_ssl_module
--with-stream_ssl_preread_module
--with-openssl=../openssl-3.0.0
--with-openssl-opt=enable-ktls
--with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC'
-with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
$ make –j4
$ make install
Note: The resulting NGINX binary is statically linked with OpenSSL 3.0.0 libraries. If you later need to patch OpenSSL, you must download and unpack the new OpenSSL source archive, then run the commands above to rebuild the NGINX binary.
Configuring NGINX
To enable kTLS, include the ssl_conf_command
directive with the Options
KTLS
parameter in the server{}
context, as in this sample configuration used for our testing:
worker_processes auto;
error_log /var/log/nginx/error.log debug;
events {}
http {
sendfile on;
server {
listen 443 ssl;
ssl_certificate ssl/example.crt;
ssl_certificate_key ssl/example.key;
ssl_conf_command Options KTLS;
ssl_protocols TLSv1.3;
location / {
root /data;
}
}
}
Verifying kTLS is Enabled
To verify that NGINX is using kTLS, enable debugging mode and check for BIO_get_ktls_send
and SSL_sendfile
calls in the error log.
$ grep BIO /var/log/nginx/error.log
2021/11/10 16:02:46 [debug] 274550#274550: *2 BIO_get_ktls_send(): 1
2021/11/10 16:02:49 [debug] 274550#274550: *3 BIO_get_ktls_send(): 1
$ grep SSL_sendfile /var/log/nginx/error.log
2021/11/10 16:02:46 [debug] 274550#274550: *2 SSL_sendfile: 1048576
2021/11/10 16:02:49 [debug] 274550#274550: *3 SSL_sendfile: 1048576
Note: We recommend that you disable debugging mode after making these checks, especially in production environments. Debug logging incurs a performance penalty due to the large volume of write operations; also, debug logs can be huge and quickly exhaust available space on the disk partition.
Performance Improvement with kTLS
When serving static files under heavy load, SSL_sendfile()
can increase throughput by up to 2x compared to user‑space TLS, but the size of the performance boost depends significantly on various factors (disk performance, system load, etc). It is also possible to reduce CPU usage if your network card supports TLS offload.
Testing Performance
To measure the performance boost on your setup, use the following instructions to run a simple one‑thread test. As detailed below, our test results indicate a performance boost of up to nearly 30% without any specific tuning.
Hardware and software used:
-
AWS t3.medium instance with:
- 4 GB RAM
- 20 GB general purpose SSD
- Intel® Xeon® Platinum 8259CL CPU @ 2.50GHz with 2 cores
- FreeBSD 13.0 and Ubuntu 21.10
- TLSv1.3 with the
TLS_AES_256_GCM_SHA384
cipher suite - NGINX 1.21.4, built and configured as specified in Enabling kTLS in NGINX.
To perform the test:
-
Create a large file that fits completely in the disk cache:
# truncate -s 1g /data/1G
-
Run this command to check the throughput; the base command is repeated multiple times for more accurate results. Pipe the output to the
ministat
utility[FreeBSD][Ubuntu] for a basic statistical analysis.# for i in 'seq 1 100'; do curl -k -s -o /dev/null -w '%{speed_download}n' https://localhost/1G | ministat
Results of Performance Testing
In the following results from our tests, presented as output from ministat
, each value is the download speed in kBytes/second.
Throughput for FreeBSD 13.0 without kTLS:
N Min Max Median Avg Stddev
x 10 532225 573348 555616 555155.6 10239.137
Throughput for FreeBSD 13.0 with kTLS:
N Min Max Median Avg Stddev
x 10 629379 723164 717349 708600.4 28304.766
Throughput for Ubuntu 21.10 without kTLS:
N Min Max Median Avg Stddev
x 10 529199 705720 662354 654321.6 48025.103
Throughput for Ubuntu 21.10 with kTLS:
N Min Max Median Avg Stddev
x 10 619105 760208 756278 741848.3 43255.246
In our testing, kTLS boosted performance more with FreeBSD than Ubuntu. The percentage improvement was as follows:
Min | Max | Median | Avg | |
---|---|---|---|---|
FreeBSD 13.0 | 18% | 26% | 29% | 28% |
Ubuntu 21.10 | 16% | 8% | 14% | 13% |
Summary
NGINX 1.21.4 introduces support for kTLS when serving static files with SSL_sendfile()
. Our testing shows that performance improves by between 8% and 29%, depending on the operating system.
We’re interested in hearing about your experiences with kTLS and NGINX, and especially the results of your testing on other OSs! Please share them in the comments section below.
The post Improving NGINX Performance with Kernel TLS and SSL_sendfile( ) appeared first on NGINX.
Source: Improving NGINX Performance with Kernel TLS and SSL_sendfile( )