LoginSignup
50
47

More than 5 years have passed since last update.

ELB(Elastic Load Balancer)の後ろをTCPにしてHTTP2をサポートしつつ、リクエスト元IPも記録

Posted at

Nginxが1.9.5でHTTP2モジュールをやや試験的ながら同梱するようになりました。つかおう。

しかし、例えばNginxがELB(TLS終端)の後ろにいる場合、バックエンドへの接続をTCPにする必要があります。
で、TCPにするとアクセス元IPを通常ではログに記録できない。

ELBでは一応そういう時のために、Proxy Protocolを差し込んでくれる。NginxもProxy Protocolを取れるように設定できる。

Nginxで普通のTLSとProxy Protocolを両方Listenする

通常の443ポートをProxy Protocolにすると動作確認とか面倒になるので、別ポートで設定する。
サンプルはこんな感じ。

    server {
        listen       443 ssl http2;
        listen       8443 ssl http2 proxy_protocol;
        server_name  _;  

        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on; 
        ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 5m; 
        ssl_certificate /path/to/cert;
        ssl_certificate_key /path/to/key;
...

ログのフォーマットには$proxy_protocol_addrを追加しておく。ELB経由で渡ってきたクライアントIPがあれば記録される。

    log_format pp_combined '$proxy_protocol_addr $remote_addr - $remote_user [$time_local] '
                               '"$request" $status $body_bytes_sent '
                               '"$http_referer" "$http_user_agent"';

Nginx直接、443ポートのHTTP/2をチェック

これは普通のクライアントから開ける。

$ curl -kI --http2 https://ec2-54-238-xxx-xxx.ap-northeast-1.compute.amazonaws.com/
HTTP/2.0 200
server:nginx/1.9.5
date:Fri, 16 Oct 2015 09:58:08 GMT
content-type:text/html
content-length:612
last-modified:Wed, 14 Oct 2015 06:23:09 GMT
vary:Accept-Encoding
etag:"561df4cd-264"
accept-ranges:bytes

アクセスログには次のように記録されたね。

119.104.xxx.xxx - - [16/Oct/2015:09:58:08 +0000] "HEAD / HTTP/2.0" 200 160 "-" "curl/7.44.0"

Nginx直接、8443ポートをチェック

cURLついでに8443も叩いてみよう。Unknown SSL protocol errorと言われる。

$ curl -kI --http2 https://ec2-54-238-xxx-xxx.ap-northeast-1.compute.amazonaws.com:8443
curl: (35) Unknown SSL protocol error in connection to ec2-54-238-xxx-xxx.ap-northeast-1.compute.amazonaws.com:8443

サーバのログでは、broken headerとされちゃう。

2015/10/16 09:59:24 [error] 11#0: *1 broken header: "?????,????F??Ŧ?)`t???B?N???0?,?(?$??
????kjih9876?????2?.?*?&???=5" while reading PROXY protocol, client: 119.104.xxx.xxx, server: 0.0.0.0:8443

ELBを 443=>8443としつつ、Proxy Protocolを有効に

じゃあ TCP/443=>TCP/8443 のELBを用意しよう。したね。

Port Configuration: 443 (TCP) forwarding to 8443 (TCP)

8443へのリクエストに対して、Proxy Protocolを使うポリシーを当てよう。

TLSをほどかないのでとりあえずProxyProtocol用の1つがついた。

...
"BackendServerDescriptions": [
     {
         "InstancePort": 8443, 
         "PolicyNames": [
             "my-ProxyProtocol-policy"
         ]
     }
 ], 

...

ELB経由、443ポートのHTTP/2をチェック

ではNginxにしたように、ELBにcURLしよう。ちゃんとHTTP/2.0だ。

$ curl -kI --http2 https://http2-test-1298257833.ap-northeast-1.elb.amazonaws.com
HTTP/2.0 200
server:nginx/1.9.5
date:Fri, 16 Oct 2015 10:08:00 GMT
content-type:text/html
content-length:612
last-modified:Wed, 14 Oct 2015 06:23:09 GMT
vary:Accept-Encoding
etag:"561df4cd-264"
accept-ranges:bytes

アクセスログには、リクエスト元のクライアントとELBの内側アドレスが両方記録される(※ログフォーマット次第)。

119.104.xxx.xxx 10.134.140.5 - - [16/Oct/2015:10:08:00 +0000] "HEAD / HTTP/2.0" 200 160 "-" "curl/7.44.0"

これでよし。

ついでにngx_mrubyでのproxy_protocol_addrの取り方

Nginx::Request#var経由でとれる。

location /hello {
  mruby_content_handler_code '
    r = Nginx::Request.new
    Nginx.echo "Your IP is #{r.var.proxy_protocol_addr}"
  ';
}  

ELB経由で確認っと。

$ curl -k --http2 https://http2-test-1298257833.ap-northeast-1.elb.amazonaws.com/hello
Your IP is 119.104.xxx.xxx

おわりに

ELBにTLS終端を任せるのは色々と都合がよいので、それを諦めてでも俺はWebサイトをHTTP/2にしたいぜ! って時に。
まあHTTP/2自体、いつのまにかELBがサポートしてそう。

50
47
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
50
47