TLS(Transport Layer Security)は、非常に有名な暗号プロトコルです。TLSをkernelに実装する(kTLS)ことで、ユーザー空間とkerne l空間のコピー動作の必要性を大幅に減らし、パフォーマンスを向上させることができます。
kTLSとsendfile()
を組み合わせることで、データ転送でネットワークスタックに渡される前に、kernel空間で直接暗号化されます。これにより、データをユーザー空間にコピーしTLS ライブラリで暗号化し、その後kernel空間に戻す必要が無くなります。又、kTLS は TLS 処理をハードウェアにオフロードすることができ、TLS暗号処理をネットワークデバイスにオフロードすることもできます。
最近のLinuxとFreeBSDのカーネルはTLSをカーネルにオフロードすることをサポートしていますが、NGINXオープンソースもサポートしています。NGINX 1.21.4では、SSL_sendfile()
で静的ファイルを配信する際にkTLSのサポートを導入しており、パフォーマンスを大幅に向上させることができます。以下に詳述するように、NGINXがSSL_sendfile()を使用するには、カーネルとOpenSSLの両方がkTLSでビルドされていることが必要です。
このブログでは、どのオペレーティングシステムとOpenSSLのバージョンがkTLSをサポートしているかを詳しく説明し、kTLS用にカーネルとNGINXを構築し設定する方法を紹介します。また、kTLSによって期待できるパフォーマンスの向上について、FreeBSDとUbuntuで行ったテストの仕様と結果も紹介します。
注意:kTLSの実装は非常に新しく、急速に進化しています。本ブログでは2021年11月現在のkTLSのサポートについて説明していますが、ここで提供する情報や手順の変更については、nginx.orgやNGINXブログでのアナウンスを確認してください。
一般的な要件
-
いずれかのオペレーティングシステム
-
FreeBSD 13.0+. 2021年11月現在、FreeBSD 13.0+は、OpenSSL 3.0.0+を組み込むためにNGINXを手動でビルドしなくてもNGINXでkTLSをサポートする唯一のOSです。FreeBSDでNGINXをkTLSで有効にするを参照してください。
-
Linuxカーネルバージョン4.17以降で構築されたLinuxディストリビューション、ただし可能な限りバージョン5.2以降で構築されたものを使用することを推奨します。(kTLSのサポートは実際にはバージョン4.13で利用可能ですが、OpenSSL 3.0.0はカーネルヘッダーバージョン4.17以降が必要です)。
-
-
OpenSSL – バージョン3.0.0またはそれ以降
-
NGINX – バージョン 1.21.4 または以降 (mainline)
対応オペレーティングシステム
kTLSをサポートするOS
2021年11月現在、NGINX Open SourceがサポートするOSのうち、以下はkTLSと表記されている暗号をサポートしています。暗号のサポートの詳細については、「TLS プロトコルと暗号のサポート」を参照してください。
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 |
* カーネルのバージョンは4.14ではなく5.10である必要があります。kTLSをサポートしないOS及びAmazon Linux 2 FAQを参照してください。
** kTLSのサポート状況は、アップストリームのRHEL 8から継承しています。
*** FreeBSD のコミットログを参照してください。
kTLSをサポートしないOS
以下のOSは、以下の理由により、kTLSをサポートしていません。
- Alpine Linux 3.11-3.14 – カーネルは
CONFIG_TLS=n
オプションでビルドされており、モジュールまたはカーネルの一部としての kTLS のビルドは無効になっています。 - Amazon Linux 2 – Linux カーネルバージョンは、デフォルトの Amazon Linux 2 AMI では 4.14 です (Amazon Linux 2 FAQ を参照してください)。
- CentOS 7.4+ – Linuxカーネルバージョンは3.10です。アップストリームソースとしてRHEL 7.4+からそのkTLSサポートステータスを継承しています。
- Debian 10 and 11 – カーネルは CONFIG_TLS=n オプションでビルドされています (Debian のバグ報告ログを参照してください)。
- RHEL 7.4+ – Linux カーネルのバージョンは 3.10 です。
- SLES 12 SP5+ – Linuxカーネルバージョンは4.12です。
- Ubuntu 18.04 LTS – Linuxカーネルバージョンは4.15です。
TLS プロトコルと暗号のサポート
上記のように、kTLSをサポートしているOSは、TLSのプロトコルや暗号のサポートに違いがあります。
TLSv1.2において、kTLSモジュールは以下の暗号をサポートしています。
AES128-GCM-SHA256
AES256-GCM-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-AES256-GCM-SHA384
TLSv1.3では、kTLSモジュールは以下の暗号スイートをサポートしています。
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
(一部のOSのみ。kTLSをサポートしているOSで指定されています。)
OpenSSLによってサポートされているTLS暗号がNGINXバイナリで有効になっているか確認するには、NGINXを構築したディレクトリ(たとえば、ホームディレクトリ)でopenssl-3.0.0/.openssl/bin/openssl
ciphers
コマンドを実行してください。
NGINXでのkTLSの有効化
冒頭で述べたように、kTLSはすべての暗号化と復号化がカーネルで行われるため、NGINXのパフォーマンスを向上させます。データ転送の為にネットワークスタックに渡される前に、カーネル空間で直接暗号化されます。これにより、データをユーザー空間にコピーしてTLSライブラリで暗号化し、転送の為にカーネル空間に戻す必要がなくなります。
カーネルでのkTLSのロード
最近のFreeBSDやLinuxのディストリビューションでは、kTLSは通常モジュールとして(CONFIG_TLS=m
オプションで)ビルドされています。NGINXを起動する前に、明示的にkTLSモジュールをカーネルにロードする必要があります。
-
FreeBSDでは、これらのコマンドを
root
ユーザーで実行します。# kldload ktls ocf # sysctl kern.ipc.tls.enable=1
reeBSDのコマンドオプションの詳細については、
ktls(4)
のマニュアルページを参照してください。 -
Linuxディストリビューションでは、
root
ユーザでこのコマンドを実行します。# modprobe tls
FreeBSDでNGINXをkTLSで使用可能にする
FreeBSDのNGINXでkTLSサポートを有効にするには、Linuxディストリビューションと同じ手順を使用することができます。ただし、FreeBSD Ports Collectionのsecurity/openssl-develポートにあるkTLS付きNGINXのビルドを活用するために、以下の手順を実行することをお勧めします。kTLSの概要を含む詳細については、FreeBSDウェブサイトの「TLS Offload in the Kernel」を参照してください。
-
kTLSをサポートするOpenSSL 3.0を、configメニューで適切なオプションを選択しながらビルドします。
# cd /usr/ports/security/openssl-devel && make config && make install
-
openssl-devel をデフォルトのSSLライブラリとして使用するように /etc/make.conf を変更します。
# echo "DEFAULT_VERSIONS+=ssl=openssl-devel >> /etc/make.conf
-
NGINXをビルドします。
# cd /usr/ports/www/nginx-devel && make install
ディストリビューションでkTLSを使用したNGINXの構築
現在ほとんどのLinuxディストリビューションには、3.0.0より前のバージョンのOpenSSL(一般的には、バージョン1.1)が含まれています。そのため、NGINXはOpenSSL 3.0.0を使用してソースからビルドする必要があります。
kTLSサポートを有効にするconfigure
コマンドの重要なオプションは、以下の2つです。
--with-openssl=../openssl-3.0.0
--with-openssl-opt=enable-ktls
その他のconfigure
オプションは、nginx.orgで公開されているNGINXの公式バイナリパッケージに含まれるモジュールに対応したものです。代わりにカスタムモジュールのセットを指定することもできます。現在使用しているNGINXバイナリに使用されているビルドオプションを確認するには、nginx
-V
を実行してください。
OpenSSL 3.0.0を使用してNGINXをビルドするには、以下のコマンドを実行します。
$ 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
注:NGINXバイナリは、OpenSSL 3.0.0ライブラリと静的にリンクされています。OpenSSLにパッチを適用する必要がある場合は、新しいOpenSSLソースアーカイブをダウンロードして解凍し、上記のコマンドを実行してNGINXバイナリをリビルドする必要があります。
NGINXの設定
kTLSを有効にするには、テストに使用したこのサンプル構成のように、server{}
コンテキストにOptions
KTLS
パラメータを持つssl_conf_command
ディレクティブを含めます。
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;
}
}
}
kTLSが有効であることを確認する
NGINXがkTLSを使用していることを確認するには、デバッグモードを有効にして、エラーログにBIO_get_ktls_send()
とSSL_sendfile()
があるかどうかを確認します。
$ 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
注:これらのチェックを行った後、特に実稼働環境では、デバッグモードを無効にすることをお勧めします。デバッグ・ログは、大量の書き込み操作を行うため、パフォーマンス・ペナルティが発生します。また、デバッグ・ログは巨大になりやすく、ディスク・パーティションの空き容量をすぐに使い果たしてしまいます。
kTLSによるパフォーマンス向上
高負荷下で静的ファイルを提供する場合、SSL_sendfile()
はユーザー空間のTLSと比較して最大2倍までスループットを向上させることができますが、性能向上の大きさは様々な要因(ディスク性能、システム負荷など)に大きく依存します。また、ネットワークカードがTLSオフロードをサポートしていれば、CPU使用率を下げることが可能です。
性能テスト
以下の手順で1スレッドテストを実施することで、お使いのセットアップでのパフォーマンス向上を測定することができます。以下のテスト結果によると、特別なチューニングを行わなくても、最大で約30%の性能向上が確認されています。
使用したハードウェアとソフトウェア
- 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.
テストを実行するには
-
ディスクキャッシュに完全に収まるような大きなファイルを作成します。
# truncate -s 1g /data/1G
-
このコマンドを実行してスループットを確認します。より正確な結果を得るために、ベースとなるコマンドを複数回繰り返し実行します。出力を
ministat
ユーティリティ[FreeBSD][Ubuntu]にパイプし、基本的な統計分析に使用します。# for i in 'seq 1 100'; do curl -k -s -o /dev/null -w '%{speed_download}\n' https://localhost/1G | ministat
パフォーマンステスト結果
以下のテスト結果は、ministat
の出力として表示され、各値はkBytes/secondでのダウンロード速度です。
FreeBSD 13.0、kTLS使用しない場合のスループット。
N Min Max Median Avg Stddev
x 10 532225 573348 555616 555155.6 10239.137
FreeBSD 13.0、kTLS使用した場合のスループット。
N Min Max Median Avg Stddev
x 10 629379 723164 717349 708600.4 28304.766
Ubuntu 21.10でkTLSを使用しない場合のスループット。
N Min Max Median Avg Stddev
x 10 529199 705720 662354 654321.6 48025.103
Ubuntu 21.10でkTLSを使用した場合のスループット。
N Min Max Median Avg Stddev
x 10 619105 760208 756278 741848.3 43255.246
我々のテストでは、kTLSはUbuntuよりもFreeBSDでより性能を向上させました。改善率は以下の通りです。
Min | Max | Median | Avg | |
---|---|---|---|---|
FreeBSD 13.0 | 18% | 26% | 29% | 28% |
Ubuntu 21.10 | 16% | 8% | 14% | 13% |
概要
NGINX 1.21.4では、SSL_sendfile()
を使用して静的ファイルを配信する際のkTLSのサポートが導入されました。当社のテストでは、オペレーティングシステムによって8%から29%、パフォーマンスが向上することが確認されています。
kTLSとNGINXの使用経験、特に他のOSでのテスト結果についてお聞かせください。特に他のOSでのテスト結果をお待ちしています!