ApacheでのHTTP/2設定
概要
ApacheでHTTP/2を設定した時のメモ。
Fedora23で行った。Fedora 23ではhttpdはVer. 2.4.18で、mod_http2が組み込まれているため、設定の変更のみでHTTP/2が使えるようになる。
http通信(暗号化なし)での設定
まずは暗号化をしないhttp通信での設定。HTTP/2を使いたいVirtualHostかServer Configに以下の設定を追加するだけでよい(設定変更後のreload等の細かい手順は省略)。
Protocols h2c http/1.1 ProtocolsHonorOrder On
h2cはHTTP/2を使ったhttp通信を意味する。
次にHTTP/2での接続を確認する。
HTTP/2をサポートしたブラウザにはChrome,FireFoxがあるが、これらのブラウザは、http://へのHTTP/2接続をサポートしていないため、curlコマンドで確認する。curlは--http2オプションをつければ、HTTP/2での接続を試みる。
$ curl -v --http2 http://test.bit-hive.com/ * Trying 127.0.0.1... * Connected to test.bit-hive.com (127.0.0.1) port 80 (#0) > GET / HTTP/1.1 > Host: test.bit-hive.com > User-Agent: curl/7.43.0 > Accept: */* > Connection: Upgrade, HTTP2-Settings > Upgrade: h2c > HTTP2-Settings: AAMAAABkAAQAAP__ > < HTTP/1.1 101 Switching Protocols < Upgrade: h2c < Connection: Upgrade * Received 101 * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=28 * http2_recv: 16384 bytes buffer at 0x557636819ba8 (stream 1) * http2_recv: returns 390 for stream 1 < HTTP/2.0 200 < date:Mon, 01 Feb 2016 13:05:52 GMT < server:Apache/2.4.18 (Fedora) OpenSSL/1.0.2e-fips PHP/5.6.16 < last-modified:Wed, 27 Jan 2016 10:35:34 GMT < etag:"8d-52a4e5cb89cbe" < accept-ranges:bytes < content-length:141 < content-type:text/html
クライアントからのリクエストでConnection: Upgrade, HTTP2-Settings, Upgrade: h2cを送信し、HTTP/2へのアップグレードを試みているのがわかる。サーバーからのレスポンスではHTTP/1.1 101 Switching Protocolsが返り、HTTP1.1からHTTP/2への変更を受け付けている。
https通信(SSL通信)での設定
次はhttps通信での設定。VirtualHostに以下の設定を追加する。
Protocols h2 http/1.1 ProtocolsHonorOrder On
httpでの設定と違うのはh2cとh2。h2はHTTP/2を使ったhttps通信を意味する。
これで、curlを使って接続を確認してみる。先程と同じように--http2を付けて接続を試みる。SSL証明書がダミーのものなので、証明書のチェックをスキップするため、以下の例では、--insecureを付けている。
$ curl -v --insecure --http2 https://test.bit-hive.com/ * Trying 127.0.0.1... * Connected to test.bit-hive.com (127.0.0.1) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * skipping SSL peer certificate verification * ALPN, server accepted to use h2 * SSL connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA * Server certificate: * subject: E=root@localhost.localdomain,CN=localhost.localdomain,OU=SomeOrganizationalUnit,O=SomeOrganization,L=SomeCity,ST=SomeState,C=-- * start date: 1月 29 06:43:04 2016 GMT * expire date: 1月 28 06:43:04 2017 GMT * common name: localhost.localdomain * issuer: E=root@localhost.localdomain,CN=localhost.localdomain,OU=SomeOrganizationalUnit,O=SomeOrganization,L=SomeCity,ST=SomeState,C=-- * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x56020bac3260) > GET / HTTP/1.1 > Host: test.bit-hive.com > User-Agent: curl/7.43.0 > Accept: */* > * http2_recv: 16384 bytes buffer at 0x56020bac3ba8 (stream 1) * HTTP/2 stream 1 was not closed cleanly: error_code = 8 * Closing connection 0 curl: (16) HTTP/2 stream 1 was not closed cleanly: error_code = 8
https通信では、http通信時とは異り、アップグレードではなく、ALPNというTLS拡張機能を使って、接続プロトコルを指定する。上記ログの"ALPN, server accepted to use h2"からHTTP/2で接続しようとしていることがわかる。
ただ、最終的には、接続が中断されていることがわかる("HTTP/2 stream 1 was not closed cleanly: error_code = 8")。これは、接続時にTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(ログの"SSL connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"より)という暗号スイートが選択されたため。RFC 7540 Appendix A. によると、暗号スイートのブラックリストが列挙されているが、このブラックリストに入っている暗号スイートが選択された場合は、接続エラー(INADEQUATE_SECURITY)とすることになっている。TLS_ECDHE_RSA_WITH_AES_256_CBC_SHAはこのブラックリストに入っているため、接続が切断されている。
そこで、ブラックリストにはないTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256を使って接続するように、--cipherオプションを指定するとHTTP/2で正常に接続できるようになる。
# curl -v --insecure --cipher ecdhe_rsa_aes_128_gcm_sha_256 --http2 https://test.bit-hive.com/ * Trying 127.0.0.1... * Connected to test.bit-hive.com (127.0.0.1) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * skipping SSL peer certificate verification * ALPN, server accepted to use h2 * SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 <略> < HTTP/2.0 200 < date:Mon, 01 Feb 2016 13:41:20 GMT < server:Apache/2.4.18 (Fedora) OpenSSL/1.0.2e-fips PHP/5.6.16 < last-modified:Wed, 27 Jan 2016 10:35:34 GMT < etag:"8d-52a4e5cb89cbe" < accept-ranges:bytes < content-length:141 < content-type:text/html; charset=UTF-8
ただ、上記のようにクライアント側で暗号スイートを指定しないといけないのは具合が悪い。特にChromeやFireFoxなどのブラウザからアクセスする場合は、暗号スイートを細かく指定することはできない。
このため、ApacheのSSLCipherSuiteを適切に設定し、ブラックリストに載っていない暗号スイートが優先的に選択されるように設定しておく必要がある。
Fedora23のデフォルトの設定では"SSLCipherSuite PROFILE=SYSTEM"のようになっている。PROFILE=SYSTEMは何かというと、Fedora独自のcrypto policyという仕組みを使って、システム全体の設定を参照している。具体的には以下のような設定と同じになっている。crypto policyについての詳細はここでは省くので、こちらを参照のこと。
デフォルトのCipher Suite設定
SSLCipherSuite !SSLv2:kEECDH:kRSA:kEDH:kPSK:+3DES:!aNULL:!eNULL:!MD5:!EXP:!RC4:!SEED:!IDEA:!DES
この設定を変更して、ブラックリストに載っていない暗号スイートが選択されるようにする。今回は楽をして、crypto policyをDEFAULTからFUTURE(よりセキュリティレベルの高い設定)に変更することで対応する。
crypto policyを変更するには/etc/crypto-policies/configの設定をFUTUREに変えて、update-crypto-policiesを実行すればよい。これで、ポリシーは変更されるが、httpdにも反映させるために、reloadしてconfigを再読み込みさせてやる。
crypto policy変更の反映
# update-crypto-policies Setting system policy to FUTURE # service httpd reload
これでSSLCipherSuiteは以下の設定と等価になる。Fedora以外で設定する場合でも、SSLCipherSuiteを以下の設定にすれば、とりあえず接続はできるようになるはず。
crypto policyをFUTUREに変更後のSSLCipherSuite設定
SSLCipherSuite !SSLv2:!SSLv3:kEECDH:kRSA:kEDH:kPSK:!aNULL:!eNULL:!3DES:!MD5:!EXP:!RC4:!SEED:!IDEA:!DES
この状態で再度、curlからアクセスをしてみる。
$ curl -v --insecure --http2 https://test.bit-hive.com/ * Trying 127.0.0.1... * Connected to test.bit-hive.com (127.0.0.1) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * skipping SSL peer certificate verification * ALPN, server accepted to use h2 * SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * Server certificate: * subject: E=root@localhost.localdomain,CN=localhost.localdomain,OU=SomeOrganizationalUnit,O=SomeOrganization,L=SomeCity,ST=SomeState,C=-- * start date: 1月 29 06:43:04 2016 GMT * expire date: 1月 28 06:43:04 2017 GMT * common name: localhost.localdomain * issuer: E=root@localhost.localdomain,CN=localhost.localdomain,OU=SomeOrganizationalUnit,O=SomeOrganization,L=SomeCity,ST=SomeState,C=-- * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x56338129a260) > GET / HTTP/1.1 > Host: test.bit-hive.com > User-Agent: curl/7.43.0 > Accept: */* > * http2_recv: 16384 bytes buffer at 0x56338129aba8 (stream 1) * http2_recv: 16384 bytes buffer at 0x56338129aba8 (stream 1) * http2_recv: 16384 bytes buffer at 0x56338129aba8 (stream 1) * http2_recv: returns 405 for stream 1 < HTTP/2.0 200 < date:Mon, 01 Feb 2016 14:14:25 GMT < server:Apache/2.4.18 (Fedora) OpenSSL/1.0.2e-fips PHP/5.6.16 < last-modified:Wed, 27 Jan 2016 10:35:34 GMT < etag:"8d-52a4e5cb89cbe" < accept-ranges:bytes < content-length:141 < content-type:text/html; charset=UTF-8
今回は--cipherを指定しなくても、暗号スイートにTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256が選択されて、HTTP/2での接続が成功した。
Chromeからもアクセスして、chrome://net-internals/#http2 で確認したところ、ChromeからもHTTP/2で接続できていた。
Protocolsを指定しているはずなのに、ブラウザからHTTP/2で接続できずにHTTP/1.1になってしまうような場合は、どの暗号スイートが選択されているか確認してみるとよい。