LoginSignup
30
22

More than 5 years have passed since last update.

nginxでServer Name Indication(sni)

Last updated at Posted at 2012-12-26

この記事は最終更新から1年以上経過しています。 気をつけてね。

名前ベースのSSLを。

nginxでsniが有効になっているか確認

コンパイル時に--with-http_ssl_module をつけつつ、OpenSSLのライブラリが対応していればよい。
パッケージ版でも有効なので大体の環境では問題ないでしょう。

CLI_output
$ nginx -V
nginx version: nginx/1.2.1
TLS SNI support enabled

TLS SNI support enabledとなってればOK。

これでSSLのClientHelloにくっついてきたサーバ名を拾ってくれる。

nginxの設定

nginxにインクルードするコンフィグの例、本家ドキュメント案内通り、普通に設定してよい。
ファイルを用意するのが面倒なのでHttpLuaModuleでコンテンツを返却しています。

nginx_sni.conf
server {
        listen 443;
        server_name cert1.example.com;

        root html;
        index index.html index.htm;

        ssl on;
        ssl_certificate certs/cert1.example.com.cert;
        ssl_certificate_key certs/cert1.example.com.key;

        location / {
                default_type 'text/plain';
                content_by_lua 'ngx.say(ngx.var.server_name)';
        }
}

server {
        listen 443;
        server_name cert2.example.com;

        root html;
        index index.html index.htm;

        ssl on;
        ssl_certificate certs/cert2.example.com.cert;
        ssl_certificate_key certs/cert2.example.com.key;

        location / {
                default_type 'text/plain';
                content_by_lua 'ngx.say(ngx.var.server_name)';
        }
}

server {
        listen 443 default;
        server_name cert3.example.com;

        root html;
        index index.html index.htm;

        ssl on;
        ssl_certificate certs/cert3.example.com.cert;
        ssl_certificate_key certs/cert3.example.com.key;

        location / {
                default_type 'text/plain';
                content_by_lua 'ngx.say(ngx.var.server_name)';
        }
}

OpenSSLのクライアントでテスト

-servername cert*.example.com を渡して証明書の使い分けをテスト。

CLI_output
# cert1 が使われている
$ openssl s_client -quiet -connect localhost:443 -servername cert1.example.com
depth=0 /C=JP/ST=Osaka/L=Osaka-shi/O=HiganWorks LLC./CN=cert1.example.com

# cert2 が使われている
$ openssl s_client -quiet -connect localhost:443 -servername cert2.example.com
depth=0 /C=JP/ST=Osaka/L=Osaka-shi/O=HiganWorks LLC./CN=cert2.example.com

# cert3 が使われている
$ openssl s_client -quiet -connect localhost:443 -servername cert3.example.com
depth=0 /C=JP/ST=Osaka/L=Osaka-shi/O=HiganWorks LLC./CN=cert3.example.com

# 未設定ならデフォルトが使われる
$ openssl s_client -quiet -connect localhost:443 -servername cert4.example.com
depth=0 /C=JP/ST=Osaka/L=Osaka-shi/O=HiganWorks LLC./CN=cert3.example.com

それぞれのリクエストに対して、期待通りのサーバ証明書が選択されている。

Hostを渡してコンテンツのチェック

名前ベースで別コンテンツを返している。

CLI_output
$ curl -k https://localhost:443/ -H 'Host: cert1.example.com'
cert1.example.com

$ curl -k https://localhost:443/ -H 'Host: cert2.example.com'
cert2.example.com

$ curl -k https://localhost:443/ -H 'Host: cert3.example.com'
cert3.example.com

※これらcurlで使われているサーバ証明書はすべてデフォルトのCN: cert3.example.comのものになる、理由は後述。

ClientHello中サーバ名の確認

SNIではTCPセッション確立後の1発目、ClientHelloにサーバ名を混ぜることで暗号化前にサーバ名を設定できるようにしている。
というよりこのタイミングしか無いので仕方ない。

$ openssl s_client -connect localhost:443 -servername cert3.example.com -debug
CONNECTED(00000003)
write to 0x1461ff0 [0x1463700] (121 bytes => 121 (0x79))
-- snip --
0060 - 14 00 00 11 63 65 72 74-33 2e 65 78 61 6d 70 6c   ....cert3.exampl
0070 - 65 2e 63 6f 6d 00 23                              e.com.#
-- snip --

デバッグ出力から一つ目のWrite(ClientHello)のダンプを見るとサーバ名が含まれていた。

curlのSNI対応

curlもsniに対応しているが、ClientHelloで渡す文字列が、

curl https://{server_name}/

{server_name}部分になっていた。

とういことで、下記のパターンではlocalhostが使われるのでコンテンツは名前ベースのものが返ってくるけど証明書はデフォルトで指定しているものだけが使われてしまう。

curl https://localhost/ -H "Host: cert1.example.com"

curlで動作確認するならhostsに書いてコモンネームをURL部分に含めます。

30
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
22