nginxを導入、設定し、運用するにあたって少し問題が起こったので、それについて書きます。
やったことだけを知りたい人向けに書くと、「ssl_protocols
とssl_ciphers
をhttpブロックに書こう」という記事です。
前提
- 先日、nginx(1.10.1)を導入して、ama.ne.jpなどにリバースプロキシを噛ませることにしました。設定にあたっては、各サイトのserverブロックに
ssl_protocols
やssl_ciphers
を加え、つよいSSLサーバを作ると同じプロトコルや暗号スイートを指定しました。 - 設定の結果、SSL Server Test: ama.ne.jpの評価も実際の動作も変わらないようで、僕は大変満足げな表情です。
問題が発覚するまで
ひょんなことから、Wiiのインターネットチャンネルでama.ne.jpを見てみることになりました。
インターネットチャンネルのUAはOpera/9.30 (Nintendo Wii; U; ; 2047-7; ja)
です。
見て分かる通り、Opera 9.30ベースのブラウザが搭載されているようです。Opera 9は、TLS 1.2には非対応とのこと。
つよいSSLサーバを作るではama.ne.jpをTLS 1.2にのみ対応させて良い評価を狙いました。ですからきっとサーバは無慈悲にも接続を中断して、ブラウザは涙で画面を濡らすことでしょう。
というわけで、インターネットチャンネルにhttps://ama.ne.jp
と入力し、アクセスしてみると……
なぜか表示されるんですね。
あれ? どうやってアクセスしてるんだ……? 無理矢理? でも無理矢理ってなんだ?
何が原因なのか
opensslを使って詳しい状況を調査します。
$ openssl s_client -connect ama.ne.jp:443 -tls1
...
SSL-Session:
Protocol : TLSv1
Cipher : ECDHE-RSA-AES256-SHA
...
GET / HTTP/1.1
Host: ama.ne.jp
HTTP/1.1 200 OK
...
<!DOCTYPE html>
<html>
...
</html>
ムムム。ちゃんと接続できて、しかも上流からレスポンスも返ってきている……。
じゃあ、nginxを挟まずに直接接続したらどうなるかな?
$ openssl s_client -connect ama.ne.jp:8083 -tls1
...
SSL routines:ssl3_read_bytes:tlsv1 alert protocol version:s3_pkt.c:1472:SSL alert number 70
SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656:
...
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
おっ、接続が切られた。どうやらnginxに問題があるようです。
ここで、nginxが使用可能としているプロトコルと暗号スイートのリストについても確認しておきます。
$ nmap --script ssl-enum-ciphers -p 443 ama.ne.jp
Starting Nmap 7.12 ( https://nmap.org ) at 2016-08-03 04:12 JST
Nmap scan report for ama.ne.jp (157.7.73.93)
Host is up (0.0079s latency).
rDNS record for 157.7.73.93: hakurei.ama.ne.jp
PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
| compressors:
| NULL
| cipher preference: server
| warnings:
| Key exchange parameters of lower strength than certificate key
|_ least strength: A
Nmap done: 1 IP address (1 host up) scanned in 3.72 seconds
確かにTLS 1.2のみが提示されていますね。
というわけで今回の問題は、自分が提示していない暗号スイートを受け入れるnginxのチャラい挙動に原因があるようです。
nginxの設定を見直す
おそらくnginxの設定に、とりわけssl_protocolsやssl_ciphersに原因がありそうです。
ssl_protocols
とssl_ciphers
はどちらもserverおよびhttpブロックに書くことができますが、変更前はserverブロックのみに個別の値を設定していました。
個別とは名ばかりで実際には全て同じ値だったことと、serverブロックの上位であるhttpブロックに設定がないのが不自然だったことから、これらの設定を全てhttpブロックに移したところ正常に動作しました。
いくつかパターンを変えて調べてみたところ、以下のことが分かりました。
- http.
ssl_protocols
の(http.ssl_ciphers
∩ server.ssl_ciphers
)がサーバが提示する暗号スイートである。 - http.
ssl_protocols
のhttp.ssl_ciphers
が「実際に使用できる暗号スイート」である。実際に使用できる暗号スイートには、サーバが提示していない暗号スイートが含まれている場合がある。 - server.
ssl_protocols
は、実際に使用できる暗号スイートにも、サーバが提示する暗号スイートにも影響を与えない。
なお、ssl_protocols
やssl_ciphers
が存在しないブロックでは、それらにデフォルト値が設定されることに注意します。
これらの挙動によれば、http.ssl_ciphers
よりもserver.ssl_ciphers
を厳しくしている今回のような場合に、サーバが提示する暗号スイートよりも実際に使用できる暗号スイートが多くなってしまうことが分かります。
その後
設定後に再びインターネットチャンネルでama.ne.jpにアクセスしようとしたところ、「ページが見つからない」ということになりました。絶対404エラーではないと思うんだけど。それでいいのか、インターネットチャンネル。
また、opensslでもきちんと接続できなくなっていることを確認しました。
$ openssl s_client -connect ama.ne.jp:443 -tls1
CONNECTED(00000003)
write:errno=104
...
No ALPN negotiated
さらに、SSL Server Test: ama.ne.jpの評価も、
Handshake Simulationのエラーが軒並み"Server closed connection"になり、すっきり気持ちのいい気分です。
before
after
よかったよかった。おやすみなさい。