NGINX SSL Termination


NGINX SSL Termination

この章では、nginxでHTTPSサーバを設定する方法を説明します。

HTTPSサーバを構築するには、serverブロックの中にlistenディレクティブと一緒にsslパラメータをnginx.cnofファイルの中で設定します。それから、サーバ証明書と秘密キーファイルの場所を設定します。

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

サーバ証明書は公開されているものです。サーバに接続する全てのクライアントに送信されます。秘密キーは秘密の存在で、アクセスが制限されたファイルに保存されていなければなりません。しかしながら、nginxのマスタープロセスはこのファイルを読めなければなりません。秘密キーは証明書として同じファイルの中に交互に保存されているかもしれません:

    ssl_certificate     www.example.com.cert;
    ssl_certificate_key www.example.com.cert;

この場合も、このファイルのアクセス権は制限されていなければなりません。この場合、証明書とキーは一つのファイルの中に保存されていますが、証明書だけがクライアントに送られます。

ssl_protocolsssl_ciphers ディレクティブは接続がSSL/TLSの強力なバージョンと暗号だけを含むように制限するために使うことができます。バージョン1.0.5から、NGINXはデフォルトでssl_protocols SSLv3 TLSv1ssl_ciphers HIGH:!aNULL:!MD5を使います。そのため、明示的にそれらを設定することは初期のNGINXのバージョンのみで意味があります。バージョン1.1.13と1.0.12から、NGINX はデフォルトでssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 を使います。

CBCモードのcipherは大量攻撃、特にBEAST攻撃(CVE-2011-3389を見てください)に脆弱性があるかも知れない事に注意してください。しかしながら、cipherの設定はRC4-SHAを好むように調整することができます:

    ssl_ciphers RC4:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

HTTPS サーバの最適化

SSL の操作は余分なCPUリソースを消費します。もっともCPUに厳しい操作はSSLハンドシェイクです。クライアントあたりのこれらの操作の数を最小化するには二つの方法があります:

  • Keepalive接続を有効にして、一つの接続を経由して幾つかのリクエストを送ります。
  • 並列や連続する接続のためのSSLハンドシェイクを避けるために、SSLセッションパラメータを再利用します。

セッションは、workerプロセス間で共有されてssl_session_cacheで設定される、SSLセッションキャッシュ内に保存されます。1メガバイトのキャシュには約4000セッションが含まれます。キャッシュのタイムアウトのデフォルトは5分です。このタイムアウトは ssl_session_timeout ディレクティブを使って増やすことができます。下記は、10メガバイトの共有セッションキャッシュを使ったマルチコアシステムに最適化した設定の例です:

worker_processes auto;

http {
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    server {
        listen              443 ssl;
        server_name         www.example.com;
        keepalive_timeout   70;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key;
        ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ...

SSL 証明書の連結

あるブラウザではよく知られた認証局の署名について苦情が言われるかも知れませんし、一方で他のブラウザでは問題なく証明書を受け入れるかも知れません。これは、発行局が、あるブラウザに配布されているよく知られている認証局には無い中間認証を使ってサーバ証明書に署名を行ったために起こります。この場合、認証局は、署名されたサーバ証明書に結びつけられる必要がある連鎖証明書のひとかたまりを提供します。サーバ証明書は連結されたファイルの中で連鎖証明書よりも前に現れなければなりません:

$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt

結果ファイルはssl_certificateディレクティブの中で使われなければなりません:

server {
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.chained.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

もしサーバ証明書とその束が間違った順番で結びつけられていた場合、NGINXは起動に失敗し、次のエラーメッセージを表示するでしょう:

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed
   (SSL: error:0B080074:x509 certificate routines:
    X509_check_private_key:key values mismatch)

NGINXがサーバ証明書の代わりに秘密キーを束の最初の証明書に使おうとしてエラーが起きます。

ブラウザが中間証明書を受け取ってそれらが信頼できる証明局で署名されている場合、ブラウザは通常それらを保持します。そのため、活発に使われているブラウザは要求される中間証明書をすでに持っていて、連鎖していない証明書に苦情を言わないかも知れません。サーバが確実に完全な証明書の鎖を送るようにするには、opensslのコマンドラインユーティリティが使われるでしょう:

$ openssl s_client -connect www.godaddy.com:443
...
鎖状証明書
 0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
     /1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc
     /OU=MIS Department/CN=www.GoDaddy.com
     /serialNumber=0796928-7/2.5.4.15=V1.0, Clause 5.(b)
   i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
 1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
     /OU=http://certificates.godaddy.com/repository
     /CN=Go Daddy Secure Certification Authority
     /serialNumber=07969287
   i:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
 2 s:/C=US/O=The Go Daddy Group, Inc.
     /OU=Go Daddy Class 2 Certification Authority
   i:/L=ValiCert Validation Network/O=ValiCert, Inc.
     /OU=ValiCert Class 2 Policy Validation Authority
     /CN=http://www.valicert.com//emailAddress=info@valicert.com
...

この例では、www.GoDaddy.comサーバの証明書#0の持ち主("s")は、証明書#1の持ち主である発行人("i")によって署名されています。証明書#1は証明書#2の持ち主である発行人によって署名されています。しかしながら、この証明書はよく知られているValiCert, Inc.によって署名されています。ValiCert, Inc.の証明書はブラウザ自身に保存されています。

もし証明書のまとまりが追加されなければ、サーバ証明書#0だけが表示されるでしょう。

一台のHTTP/HTTPSサーバ

一台のサーバでHTTPとHTTPSリクエストの両方を処理する一台のサーバを設定することは、sslパラメータ付きのlistenディレクティブとパラメータ無しのlistenディレクティブを同じバーチャルサーバに配置することで可能です。

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

0.7.14より前のSSLは上のように個々のlistenソケットを選択的に有効にすることはできませんでした。SSLはssl ディレクティブを使ってサーバ全体で有効にすることしかできず、それが一台でHTTP/HTTPS サーバを設定することを不可能にしていました。listenディレクティブのssl パラメータはこの問題を解決するために追加されました。このようにssl ディレクティブは最近のバージョンでは避けるべきです。

名前ベースのHTTPSサーバ

二つ以上のHTTPSサーバで一つのIPアドレスをlistenするように設定する場合に良くある問題

server {
    listen          443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

この設定により、ブラウザはデフォルトのサーバ証明書を受け取ります。この場合、リクエストされたサーバ名に関係なく、www.example.comです。これはSSLプロトコルの挙動そのものによって起きます。ブラウザがHTTPリクエストを送る前にSSL接続が確立され、NGINXはリクエストされたサーバの名前を知りません。従って、デフォルトのサーバ証明書が提供されるだけになるでしょう。

この問題を解決する一番いい方法は、それぞれのHTTPSサーバに異なるIPアドレスを割り当てることです。

server {
    listen          192.168.1.1:443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}

server {
    listen          192.168.1.2:443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

同じようなHTTPS upstreamのための固有のproxy設定があることに注意してください((proxy_ssl_ciphers/proxy_ssl_protocolsproxy_ssl_session_reuse)。これらはnginxとupstream間のSSLを微調整するために使うことができます。これらについては http proxy module documentationの中でもっと読むことができます。

複数の名前をもつSSL証明書

一つのIPアドレスを複数のHTTPSサーバで共有するには他の方法があります。しかしながら、それらは全て固有の不利な点があります。一つの方法は、複数の名前、例えば www.example.comwww.example.org、がSubjectAltName 証明フィールドにある証明書を使うことです。しかしながら、SubjectAltName フィールドの長さには制限があります。

他の方法は、ワイルドカード名、例えば.example.orgの証明書を使うことです。ワイルドカード証明書は特定のドメインの全てのサブドメインを保障します。しかしトップレベルのみです。この証明書はwww.example.orgに合致しますが、example.org または www.sub.example.orgには合致しません。これら二つの方法は組み合わすこともできます。証明書は、SubjectAltName フィールドに正確な名前とワイルドカード名を含むかも知れません。例えば、example.org.example.orgです。

全てのサーバにわたって一つのメモリーのコピーが継承されるように、幾つかの名前と秘密キーを持つ証明書を設定のhttpレベルに配置した方が良いです:

ssl_certificate     common.crt;
ssl_certificate_key common.key;

server {
    listen          443 ssl;
    server_name     www.example.com;
    ...
}

server {
    listen          443 ssl;
    server_name     www.example.org;
    ...
}

Server Name Indication

一つのIPアドレスでいくつかのHTTPSサーバを実行する一般的な解決方法は、TLS Server Name Indication 拡張 (SNI, RFC 6066)で、ブラウザがSSLハンドシェイクの間にリクエストするサーバ名を渡すことができます。この解決方法により、サーバは接続に使うべき証明書を知ることができるでしょう。しかしながら、SNIはサポートするブラウザが限られています。現在のところ、次のブラウザのバージョンからサポートされ始めています:

Opera 8.0;
MSIE 7.0 (ただし、Vista以上のみ);
Mozilla platform rv:1.8.1を使っている Firefox 2.0 と その他のブラウザ;
Safari 3.2.1 (Vista以上のWindowsバージョンはサポートしています);
Chrome (Vista以上のWindowsバージョンはサポートしています)

SNIではドメイン名のみ渡すことができます。しかしながら、幾つかのブラウザはリクエストにIPアドレスの文字列が含まれる場合にサーバのIPアドレスを名前として渡すでしょう。これには応答しない方が良いでしょう。

nginxでSNIを使うためには、NGINXライブラリがビルドされた時のOpenSSLライブラリと、実行時に動的にリンクされるライブラリの両方でサポートされていなければなりません。OpenSSLは0.9.8fバージョンからoption --enable-tlsextの構成でビルドした時にSNIをサポートします。OpenSSLのバージョン0.9.8jから、このオプションはデフォルトで有効になります。NGINXがSNIサポートで構築された場合、"-V" スイッチを付けて実行した時に次のものを表示するでしょう:

$ NGINX -V
...
TLS SNI support enabled
...

しかしながら、もしSNIが有効になったNGINXがSNIをサポートサポートしていないOpenSSLに動的にリンクされた場合、NGINXは警告を表示します:

nginxがSNIサポートでビルドされたが、tlsextサポートしていないOpenSSLライブラリと動的にリンクされている場合、SNIは利用できません。

互換性の注意

  • SNIサポートステータスはバージョン0.8.21と0.7.62から"-V"スイッチを使って表示されます。
  • listen ディレクティブのsslパラメータはバージョン0.7.14からサポートされています。0.8.21以前は、defaultパラメータと一緒にのみ指定することができました。
  • SNIはバージョン0.5.32からサポートされています。
  • 共有SSLセッションキャッシュはバージョン0.5.6からサポートされています。

  • バージョン 0.7.65, 0.8.19 以降のデフォルトのSSLプロトコルは SSLv3, TLSv1, TLSv1.1, と TLSv1.2 (OpenSSLライブラリがサポートしていれば)です。

  • バージョン0.7.64, 0.8.18 以前のデフォルトのSSLプロトコルはSSLv2, SSLv3 と TLSv1 です。

  • バージョン1.0.5 以降 のデフォルトのSSL cipherは HIGH:!aNULL:!MD5です。

  • 0.7.65, 0.8.20 以降のデフォルトのSSL cipherはHIGH:!ADH:!MD5です。
  • バージョン 0.8.19 から、デフォルトのSSL cipherはALL:!ADH:RC4+RSA:+HIGH:+MEDIUMです。
  • バージョン0.7.64, 0.8.18 以前 のデフォルトのSSL cipherは ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXPです。
TOP
inserted by FC2 system