Nghttp2
OSX10.10.2に brewで Nghttp2をインストールする。
$ brew install nghttp2
適当に index.htmlを作って置く。
$ cat /var/www/index.html
<html>
<body>Hello, World</body>
</html>
まずは TLS を無効にして Server を起動する。
$ nghttpd -v --no-tls -d "/var/www" 8080
IPv4: listen 0.0.0.0:8080
IPv6: listen :::8080
[id=1] [ 1.906] send SETTINGS frame
(niv=1)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[id=1] [ 1.907] recv SETTINGS frame
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[id=1] [ 1.907] recv SETTINGS frame
; ACK
(niv=0)
[id=1] [ 1.907] recv PRIORITY frame
(dep_stream_id=0, weight=201, exclusive=0)
[id=1] [ 1.907] recv PRIORITY frame
(dep_stream_id=0, weight=101, exclusive=0)
[id=1] [ 1.907] recv PRIORITY frame
(dep_stream_id=0, weight=1, exclusive=0)
[id=1] [ 1.907] recv PRIORITY frame
(dep_stream_id=7, weight=1, exclusive=0)
[id=1] [ 1.907] recv PRIORITY frame
(dep_stream_id=3, weight=1, exclusive=0)
[id=1] [ 1.907] recv (stream_id=13) :method: GET
[id=1] [ 1.907] recv (stream_id=13) :path: /index.html
[id=1] [ 1.907] recv (stream_id=13) :scheme: http
[id=1] [ 1.907] recv (stream_id=13) :authority: 127.0.0.1:8080
[id=1] [ 1.907] recv (stream_id=13) accept: */*
[id=1] [ 1.907] recv (stream_id=13) accept-encoding: gzip, deflate
[id=1] [ 1.907] recv (stream_id=13) user-agent: nghttp2/1.0.2
[id=1] [ 1.907] recv HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
[id=1] [ 1.907] send SETTINGS frame
; ACK
(niv=0)
[id=1] [ 1.907] send HEADERS frame
; END_HEADERS
(padlen=0)
; First response header
:status: 200
server: nghttpd nghttp2/1.0.2
content-length: 44
cache-control: max-age=3600
date: Sun, 30 Aug 2015 13:00:40 GMT
last-modified: Sun, 30 Aug 2015 07:25:07 GMT
[id=1] [ 1.908] send DATA frame
; END_STREAM
[id=1] [ 1.908] stream_id=13 closed
[id=1] [ 1.908] recv GOAWAY frame
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
[id=1] [ 1.908] closed
nghttp2付属の Client からアクセスする。
$ nghttp -nv https://127.0.0.1:8080/index.html
[ 0.001] Connected
[ 0.002] recv SETTINGS frame
(niv=1)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[ 0.002] send SETTINGS frame
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.002] send SETTINGS frame
; ACK
(niv=0)
[ 0.002] send PRIORITY frame
(dep_stream_id=0, weight=201, exclusive=0)
[ 0.002] send PRIORITY frame
(dep_stream_id=0, weight=101, exclusive=0)
[ 0.002] send PRIORITY frame
(dep_stream_id=0, weight=1, exclusive=0)
[ 0.002] send PRIORITY frame
(dep_stream_id=7, weight=1, exclusive=0)
[ 0.002] send PRIORITY frame
(dep_stream_id=3, weight=1, exclusive=0)
[ 0.002] send HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /index.html
:scheme: http
:authority: 127.0.0.1:8080
accept: */*
accept-encoding: gzip, deflate
user-agent: nghttp2/1.0.2
[ 0.003] recv SETTINGS frame
; ACK
(niv=0)
[ 0.003] recv (stream_id=13) :status: 200
[ 0.003] recv (stream_id=13) server: nghttpd nghttp2/1.0.2
[ 0.003] recv (stream_id=13) content-length: 44
[ 0.003] recv (stream_id=13) cache-control: max-age=3600
[ 0.003] recv (stream_id=13) date: Sun, 30 Aug 2015 13:00:40 GMT
[ 0.003] recv (stream_id=13) last-modified: Sun, 30 Aug 2015 07:25:07 GMT
[ 0.003] recv HEADERS frame
; END_HEADERS
(padlen=0)
; First response header
[ 0.004] recv DATA frame
; END_STREAM
[ 0.004] send GOAWAY frame
(last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
TLSを無効にしているため, Wiresharkでもそのまま平文の通信が確認できる。
暗号化(TLS)でHTTP/2
Wireshark (v1.12.7)で, デフォルトとも言える暗号化された HTTP/2通信の中を覗くことを目指す。
実験なので適当にオレオレ証明書を作る。
$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
$ rm server.csr
作成した鍵・証明書を指定して nghttpdを起動する。
$ nghttpd -v -d "/var/www" 8080 server.key server.crt
話が逸れるが, curlの Extension機能を使うと –http2 でHTTP/2 Clientになれる。
$ brew install curl --with-nghttp2 --with-openssl
$ curl --version
curl 7.43.0 (x86_64-apple-darwin14.1.0) libcurl/7.43.0 OpenSSL/1.0.2c zlib/1.2.5 nghttp2/1.0.2
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
$ curl --http2 --insecure https://127.0.0.1:8080/index.html
<html>
<body>Hello, World</body>
</html>
また, Chrome / Firefox から https://127.0.0.1:8080/index.html にアクセスして, Hello, World が表示されることを確認。
WiresharkでHTTP/2のパケットキャプチャ
本題のパケットキャプチャ。OSXで CLIから Firefoxを起動するときに, 環境変数に SSLKEYLOGFILEの場所を設定する。[1]
これは, NSS Key Log Formatというらしい。
起動したら https://127.0.0.1:8080/index.html にアクセスする。
$ SSLKEYLOGFILE=~/Desktop/tls_key_firefox.log "/Applications/Firefox.app/Contents/MacOS/firefox-bin"
$ cat ~/Desktop/tls_key_firefox.log
# SSL/TLS secrets log file, generated by NSS
CLIENT_RANDOM d33200b945a2193b357bbb6aeb75857f0ca042ae2bd8f1abaca4d0ee84647f3c dbda6eddb91bf5678b17a9418e1609fcaa72ab60e33478904f16bfcac8656437b3170cb225b3e91f785b7a781f719169
...
作成された SSLKEYLOGFILE を Wireshark に設定する。
Edit > Preferences > Protocols > SSL > (Pre)-Master-Secret log filename に指定する。[2]
Applyで適用すると, Protocol がHTTP2と表示される。画面下部のtabで Frameのままだと暗号化されたままなので, 横の Decrypted SSL Dataを選択すると復号された Hello, Worldが確認できた。
Nginxは HTTP/2用の Patchが出ていて, 正式対応も近いらしいので楽しみです。
[追記] 匿名接続ツール Tor を使ってみた
匿名接続ツールの Tor (The Onion Router) を OSX で使ってみたのでメモ。
$ brew install tor
/usr/local/Cellar/tor/0.2.9.8: 20 files, 9.3M
まずは, Tor を使わずに curl でとあるホストに接続し サーバログ (LTSV) を調べる。
$ curl -vvv -sL https://www.example.jp/ > normal.log
* Trying 160.16.116.76...
* Connected to www.example.jp (160.16.116.76) port 80 (#0)
> GET / HTTP/1.1
> Host: www.example.jp
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.4.6 (Ubuntu)
< Date: Sun, 15 Jan 2017 08:02:07 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding, Cookie
< Cache-Control: no-cache
< WP-Super-Cache: Served supercache file from PHP
< Expires: Sun, 15 Jan 2017 08:02:06 GMT
< Cache-Control: private
<
{ [13096 bytes data]
* Connection #0 to host www.example.jp left intact
Torサーバ を経由していないので, IPアドレスは日本国内。
domain:www.example.jp host:106.154.40.xxx user:- time:15/Jan/2017:16:59:00 +0900 method:GET path:/ protocol:HTTP/1.1 status:200 size:32903 referer:- agent:curl/7.49.1 response_time:0.226 cookie:- set_cookie:- upstream_addr:127.0.0.1:9000 upstream_cache_status:- upstream_response_time:0.226
続いて `tor &` で Torサーバを立ち上げておいて, `–socks5`オプション で指定し curl を実行する。
$ curl -vvv -sL --socks5 127.0.0.1:9050 https://www.example.jp/ > tor.log
* Trying 127.0.0.1...
* 160
* 16
* 116
* 76
* Connected to 127.0.0.1 (127.0.0.1) port 9050 (#0)
> GET / HTTP/1.1
> Host: www.example.jp
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.4.6 (Ubuntu)
< Date: Sun, 15 Jan 2017 08:03:13 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding, Cookie
< Cache-Control: no-cache
< WP-Super-Cache: Served supercache file from PHP
< Expires: Sun, 15 Jan 2017 08:03:12 GMT
< Cache-Control: private
<
{ [3102 bytes data]
* Connection #0 to host www.example.jp left intact
IPアドレスから地域を調べると ポーランド だったので Tor サーバをいくつか経由していそうだ。
domain:www.example.jp host:178.217.187.39 user:- time:15/Jan/2017:17:03:13 +0900 method:GET path:/ protocol:HTTP/1.1 status:200 size:32903 referer:- agent:curl/7.49.1 response_time:0.005 cookie:- set_cookie:- upstream_addr:127.0.0.1:9000 upstream_cache_status:- upstream_response_time:0.005
[1] HTTP/2 over TLS の通信をダンプする方法
[2] Decrypting TLS Browser Traffic With Wireshark – The Easy Way!