これからの nginx のイケてる TLS 設定

f:id:aptpod_tech-writer:20201209151232p:plain

はじめに

Advent Calendar 2020 10 日目を担当する、SRE チームの柏崎です。

nginx をお使いのみなさま、TLS 導入が当然となっている現代、どんな設定をされていますでしょうか。
NGINX ConfigSSL Server Test のようなサイトを利用し、セキュリティに気を配っている方も多いと思います。

そんな nginx ユーザのみなさま、最近 nginx 1.19.4 がリリースされ、 イケてる TLS 設定ができるようになった事はご存じでしょうか?

ChaCha20-Poly1305 について

まず前提知識として、ChaCha20-Poly1305 について、ほんの少し書いておきます。

ChaCha20-Poly1305 は、ストリーム暗号である ChaCha20 とメッセージ認証符号である Poly1305 を組み合わせた、認証付き暗号です。
RFC 7905 (2016/06) で TLS における利用が標準化され、TLS 的には比較的新しい暗号方式となっています。
OpenSSL だと 1.1.0 以降で利用可能です。

これまで、現実的に使える暗号方式として一択だった AES-GCM に加え、ChaCha20-Poly1305 が加わった形になります。

クライアントによる 2 方式の性能差

ChaCha20-Poly1305 はソフトウェア処理に向いた、簡潔なアルゴリズムです。
一方 AES-GCM は、ソフトウェア処理ではあまり性能が出ませんが、ハードウェア処理が利用できる環境ではとても高速です。

Intel や AMD の CPU には AES をハードウェア処理できる AES-NI という拡張機能が載っていますが、ARM 等には載っていません。
つまり、AES-GCM と ChaCha20-Poly1305 のどちらが性能が高いかは、環境によって違ってくるということです。

Android スマートフォンや Raspberry Pi、最近発売された M1 Mac など、ARM で動くデバイスは意外と多いです。
特に、弊社の製品のエッジ向けミドルウェア intdash Edge は、Raspberry Pi を含め様々なデバイスで利用されるので、この性能差は気になるところです。

実際に、openssl speed コマンドを使って、8192 バイトブロックにおける、AES-GCM と ChaCha20-Poly1305 の暗号化・復号化の性能を測ってみました。
ざっくりとした性能差を知るのが目的のため、それぞれ 1 回ずつの計測です。

利用したコマンドは下記です。

### aes-128-gcm の暗号化
$ openssl speed -evp aes-128-gcm

### aes-256-gcm の暗号化
$ openssl speed -evp aes-256-gcm

### chacha20-poly1305 の暗号化
$ openssl speed -evp chacha20-poly1305

### aes-128-gcm の復号化
$ openssl speed -evp aes-128-gcm -decrypt

### aes-256-gcm の復号化
$ openssl speed -evp aes-256-gcm -decrypt

### chacha20-poly1305 の復号化
$ openssl speed -evp chacha20-poly1305 -decrypt

f:id:aptpod_tech-writer:20201208211128p:plain

f:id:aptpod_tech-writer:20201208211142p:plain

確かに、Intel では AES-GCM の方が高性能ですが、ARM では逆転して ChaCha20-Poly1305 が高性能ですね。

クライアントの暗号スイートリスト

TLS クライアントによっては、ネゴシエーション時の暗号スイートリストが、前述の性能差を考慮したものになっています。

クライアントの暗号スイートリストは Qualys SSL Labs の SSL Client Test で確認することができるので、実際に Intel MacBook と M1 MacBook で比較をしてみました。

f:id:aptpod_tech-writer:20201208164039p:plain
Intel 系 MacBook 上の Chrome

f:id:aptpod_tech-writer:20201208164213p:plain
M1 MacBook 上の M1 用 Chrome

確かに、Intel MacBook では AES-GCM が優先されているのに対し、M1 MacBook では ChaCha20-Poly1305 が優先されていますね。

これまでの課題

TLS のネゴシエーションにおける暗号スイートは、クライアントとサーバの暗号スイートリストを比較することで、1 つに決定されます。
しかし、一般的な TLS サーバは、「サーバ側の暗号スイートリストを優先して」暗号スイートを決定するような設定がされています。
つまり、TLS クライアントが適切な優先度で暗号スイートリストを送ってきたとしても、サーバ側の優先度により決まった一つに決定されてしまうのです。

例えば、Raspberry Pi は ChaCha20-Poly1305 を使ってラクしたいのに、限られたリソースのなか苦手な AES-GCM で頑張らないといけない、みたいな事が起きてしまいますね。

nginx 1.19.4 でどうなる?

nginx 1.19.4 では、 ssl_conf_command ディレクティブ を使って、OpenSSL の設定を行うことができるようになりました。
下記の設定を追加すると、「クライアントが ChaCha20 の暗号スイートを優先している場合はサーバも優先する」という動作をさせることができます。 イケてる!

ssl_conf_command Options PrioritizeChaCha;

この設定は、僕が勝手に nginx の実験場にしている アプトポッドのコーポレートサイト にも適用しています。
Chrome でアクセスして開発ツールを開くと、下記の画像のように環境によって適切な暗号方式でつながるはずです。

f:id:aptpod_tech-writer:20201208164320p:plain
Intel 系 MacBook 上の Chrome

f:id:aptpod_tech-writer:20201208164343p:plain
M1 MacBook 上の M1 用 Chrome

さいごに

ちょっとマニアックになってしまいましたが、nginx の TLS 設定についてお話をしました。

ちなみに M1 Macbook でのスクショは、同僚に協力していただきました。ありがとうございます!M1 羨ましい!

nginx 1.19.4 は mainline のバージョンなので、intdash のサービス用サーバでは、まだこの設定を適用できておりません。
stable 版の 1.20.0 が待ち遠しいですね。