Nginx で TLS 1.3 対応化させる

スポンサーリンク

はじめに

 HTTPS で接続する際の暗号化経路を確立する際に使用されるプロトコルとしては現状 TLS が主流となっており、そのバージョンは TLS 1.2 迄となっていた。これが 2018/8/10、正式に RFC 8446 として公開された。
 そして Firefox 63 や Chrome/Chromium 70 と言った次期バージョンでは TLS 1.3 の正式版に対応するとのことで筆者運用のサーバーでも対応させようかなと作業をしてみた。
 尚、現行の Firefox や Chrome/Chromium でも TLS 1.3 に対応しているが、それは正式版ではなく Draft 23 版の対応に留まっている為、本記事で行う対応を実際に確認する為には Firefox 63 や Chrome/Chromium 70 のリリースを待つか、ベータ版を用いるかもしくは OpenSSL 1.1.1 を TLS 1.3 対応としたバイナリにて s_client を用いた動作確認を行う必要がある点には注意が必要だ。

TLS 1.3 対応化のメリット

 端的に言えば TLS 1.2 よりもセキュア且つパフォーマンスも上がるという事にある。
 パフォーマンスアップというのは TLS 1.3 では TLS 1.2 と比べてハンドシェイクに於けるやり取り (ラウンドトリップ) の回数が少なく済むのでその分だけ短時間で接続を確立してデータのプッシュが出来るようになると言うこと。
 あとは単に新しい技術を使って満足感が得られるところ。

TLS 1.3 に対応する Nginx のビルド

 筆者は Nginx の更新がある度にモジュールを組み込む関係上、ソースからビルドをしているので今回もソースからのビルドを行う。
 OpenSSL 1.1.1 のソースも必須なので先にダウンロードして展開しておく。

 以下作業ディレクトリは筆者例として記述する。

mkdir ~/src
cd ~/src
curl -LO https://www.openssl.org/source/openssl-1.1.1.tar.gz
tar xzvf openssl-1.1.1.tar.gz

curl -LO https://nginx.org/download/nginx-1.15.5.tar.gz
tar xzvf nginx-1.15.5.tar.gz
cd nginx-1.15.5

 流れとしては OpenSSL 1.1.1 をダウンロードして展開だけ行ったら Nginx のソースもダウンロードして展開、カレントをソースに移す。

 そして Nginx をビルドしていく為に先ず configure コマンドを実行する。
 引数としては Nginx の RPM に準拠させつつ prefix を弄って居る感じだ。
 また、筆者が組み込んでいるモジュールに関してはこの際関係無いので省く。

./configure \
--prefix=/usr/local/nginx \
--sbin-path=/usr/local/sbin/nginx \
--modules-path=/etc/nginx/modules \
--conf-path=/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-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-threads \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' \
--with-ld-opt=' -Wl,-E' \
--with-openssl=../openssl-1.1.1 \
--with-openssl-opt=enable-tls1_3

 ここでポイントとなるのは --with-openssl=../openssl-1.1.1 として OpenSSL のソースの存在するパスを明示する事と OpenSSL 自体のビルドオプションとして --with-openssl-opt=enable-tls1_3 を指定する事。これが無いと TLS 1.3 に対応出来ない。
 configure が正常に完了したら make していく。ビルドに際してライブラリ不足等でエラーが出たら適時対応で問題は無い。
 ビルド完了後は sudo make install してインストールする。

Nginx の設定

 TLS 1.3 への対応に必要な記述を抜粋して以下に示すがお約束的にドメイン名は expamle.com に置換している。
 筆者は Let’s Encrypt の証明書を使用しているのでパスはそのままとしている。

server {
    listen 443 ssl http2 default_server;
    server_name example.com;
    root /var/www/html;
    index index.html;

    charset UTF-8;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_session_tickets off;
    ssl_dhparam /etc/nginx/dhparams-4096.pem;

    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_ciphers 'TLS13+AESGCM+AES128:EECDH+AES128';
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X25519:sect571r1:secp521r1:secp384r1;
    ssl_stapling         on;
    ssl_stapling_verify  on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    add_header Strict-Transport-Security 'max-age=15768000; preload';
}

 ssl_protocols で喋らせる暗号化の為のプロトコルを指定する。TLSv1.3 TLSv1.2 とすることで 1.2 と 1.3 の両対応にしておかないといけない。
 ssl_ciphers に関しては現状これで良いかなとは思っているけどもう少し精査してみようかなと思う所。

 同一 IP アドレスで複数の https なホストを Listen している (SNI を用いる) 場合、ssl_protocols が評価されて有効となるのは default_server の設定のみとなる為に要注意。
 各ホスト個別に対応させたい TLS バージョンを設定する事は出来ない。個別に設定した場合は別の IP アドレスで Listen させる必要がある。

 ssl_dhparam に示される鍵交換で用いられるファイルは次の様に生成しておくと良い。4096bit 長で生成するので割と時間が掛かった。

openssl dhparam -out dhparams-4096.pem 4096
sudo cp dhparams-4096.pem /etc/nginx/

設定反映

 次の様にして設定内容をチェックした後にリロードさせて反映する。

sudo /usr/local/sbin/nginx -t && sudo /usr/local/sbin/nginx -s reload

動作確認

OpenSSL コマンド

 OpenSSL 1.1.1 をビルドしてインストールしているのであれば次のコマンドで確認出来る。
 また、検証環境など表に顔を出していないホストならこちらの方法で。

/usr/local/openssl/bin/openssl s_client -connect example.com -port 443 -tls1_3
CONNECTED(00000005)
... (snip
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)

SSLLABS で確認

 SSLLABS の SSL Test ならブラウザ上で各種テストを行ってくれるので楽で便利だし TLS 1.3 正式版でのテストに対応しているので丁度良い。

SSL Server Test (Powered by Qualys SSL Labs)
A comprehensive free SSL test for your public web servers.

 Hostname を入力して Submit をクリックすると SSL 接続テストが行われる。少し待つと結果が表示されるので、その内容を確認する。
 次の様に「This server supports TLS 1.3 (RFC 8446)」と表示されていれば OK。

 すこし下の方へスクロールしていくと TLS 1.2/1.3 の両方に対応している事が確認出来る。

 ハンドシェイクシミュレーションを見ると OS とブラウザ毎にどのプロトコルで接続出来たかの確認が出来る。
 現状、Chrome 70 でのみ TLS 1.3 が使われた。

ブラウザで確認

 ブラウザで確認といっても現行対応しているリリース版は無いので Google Chrome 70 のベータ版をダウンロードしてインストールした。

Chrome ベータ版
Google Chrome ベータ版で新しい機能を体験

 OpenSSL 1.1.1 をビルドしてインストールしたくない/出来ないホストで尚かつ表に顔を出していないホストでも LAN 内からアクセスは最低限出来る様にしてあるマシンはあると思うのでこの方法もあり。

 Google Chrome 70 beta を起動して対象ホストにアクセス。Ctrl + Shift + I を押して DevTools を起動して Security タブを開くと直ぐ確認出来る。
 以下スクリーンショットは既に対応化済の当サイトを開いたところ。

おわりに

 TLS 1.3 対応化は急務という訳でも無いけど対応ブラウザは速ければ今月 10 月には Firefox がリリース予定だし今のうちに対応化させておくのも手だろうかと思う。
 技術的興味があったりや新しい物好きだとかよりセキュアにと考えるなら少し楽しめる部分でもあるかなと思う。
 また、今回の作業にあたりデフォルトサーバー内の ssl_protocols しか効いてこない事に気付くのがかなり遅くて試行錯誤する無駄な時間をかなり過ごしてしまったという間抜けな事もあった。SNI を用いた複数の https なサイトを運営しているのであればこの点は本当に注意。SNI という物が完全に頭から抜けていたという。

スポンサーリンク

コメント

タイトルとURLをコピーしました