Proxy Protocol とは
http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt にある、proxy の後ろにいる子達に client の IP address を教えてあげる為の protocol。HTTP のときに X-Forwarded-For
header を足したりするのとは違って、L4 の payload の頭に IP address をぶっこむイメージ。なので後ろにいる子達がぶっこまれた IP address を解釈できないといけない。
Proxy Protocol 対応状況は?
http://blog.haproxy.com/haproxy/proxy-protocol/ が逐次更新されているみたい。Apache HTTP Server の module が紹介されている。
- https://github.com/ggrandes/apache22-modules/blob/master/mod_myfixip.c
- https://github.com/ggrandes/apache24-modules/blob/master/mod_myfixip.c
nginx はもうちょっと前から対応している、オフィシャルに。
ELB + Apache HTTP Server + mod_myfixip を試してみる
普通に ELB を使ってみる
ELB は TCP 80, TCP 80 で Listener を作りました。
backend instance には Amazon Linux を選択。
[ec2-user@ip-172-31-0-231 ~]$ sudo yum -y install httpd
[ec2-user@ip-172-31-0-231 ~]$ echo "ok" | sudo tee /var/www/html/index.html
[ec2-user@ip-172-31-0-231 ~]$ sudo service httpd start
[ec2-user@ip-172-31-0-231 ~]$ curl -v proxy-test-1912640500.us-west-2.elb.amazonaws.com
ok
* Closing connection 0
ELB で Proxy Protocol を有効化してみる
Proxy Protocol の有効化は http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-proxy-protocol.html に書いてあります。Management Console は未対応のようでした。
[ec2-user@ip-172-31-0-209 ~]$ aws --region us-west-2 elb create-load-balancer-policy --load-balancer-name proxy-test --policy-name EnableProxyProtocol --policy-type-name ProxyProtocolPolicyType --policy-attributes AttributeName=ProxyProtocol,AttributeValue=True
[ec2-user@ip-172-31-0-209 ~]$ aws --region us-west-2 elb create-load-balancer-policy --load-balancer-name proxy-test --policy-name EnableProxyProtocol --policy-type-name ProxyProtocolPolicyType --policy-attributes AttributeName=ProxyProtocol,AttributeValue=True
1 つめで Policy を作って、2 つめで TCP 80 port にこれを適用です。確認してみるとちゃんと適用されているようです。
[ec2-user@ip-172-31-0-209 ~]$ aws --region us-west-2 elb describe-load-balancers --load-balancer-name proxy-test --query 'LoadBalancerDescriptions[0].BackendServerDescriptions'
[
{
"InstancePort": 80,
"PolicyNames": [
"EnableProxyProtocol"
]
}
]
curl でアクセスしてみるとちゃんとエラーが出るようになりました:
[ec2-user@ip-172-31-0-209 ~]$ curl http://proxy-test-1912640500.us-west-2.elb.amazonaws.com/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Request header field is missing ':' separator.<br />
<pre>
GET / HTTP/1.1</pre>
</p>
<hr>
<address>Apache/2.2.29 (Amazon) Server at ip-172-31-0-231.us-west-2.compute.internal Port 80</address>
</body></html>
backend instance 上で sudo tcpdump -X -nn tcp port 80
とすると、PROXY.TCP4
という文字列が見え client の IP address が伝わってるのが見えます
05:28:06.399141 IP 172.31.1.154.35083 > 172.31.0.231.80: Flags [P.], seq 1:46, ack 1, win 71, options [nop,nop,TS val 9366818 ecr 9352303], length 45
0x0000: 4500 0061 f7f9 4000 4006 e7dd ac1f 019a E..a..@.@.......
0x0010: ac1f 00e7 890b 0050 e8da 1ad6 8442 e13e .......P.....B.>
0x0020: 8018 0047 d5ce 0000 0101 080a 008e ed22 ...G..........."
0x0030: 008e b46f 5052 4f58 5920 5443 5034 2035 ...oPROXY.TCP4.5
Health Check 自体はこの段階では成功しています。Proxy してないし Proxy Protocol 使わないからですね、きっと。
backend で Proxy Protocol を有効化
mod_myfixip の使い方は上記の source に書いてあるので素直に実行してみます。compile には httpd-devel と gcc があれば十分そうです。
[ec2-user@ip-172-31-0-231 ~]$ sudo yum install -y httpd-devel gcc
[ec2-user@ip-172-31-0-231 ~]$ wget https://raw.githubusercontent.com/ggrandes/apache22-modules/master/mod_myfixip.c
[ec2-user@ip-172-31-0-231 ~]$ sudo apxs -c -i mod_myfixip.c
config file も一先ず最低限の変更だけします。
[ec2-user@ip-172-31-0-231 ~]$ diff -u /etc/httpd/conf/httpd.conf.org /etc/httpd/conf/httpd.conf
--- /etc/httpd/conf/httpd.conf.org 2015-04-17 05:38:04.540734203 +0000
+++ /etc/httpd/conf/httpd.conf 2015-04-17 05:44:06.569742559 +0000
@@ -199,6 +199,7 @@
LoadModule disk_cache_module modules/mod_disk_cache.so
LoadModule cgi_module modules/mod_cgi.so
LoadModule version_module modules/mod_version.so
+LoadModule myfixip_module modules/mod_myfixip.so
#
# The following modules are not loaded by default:
@@ -1008,3 +1009,8 @@
# ErrorLog logs/dummy-host.example.com-error_log
# CustomLog logs/dummy-host.example.com-access_log common
#</VirtualHost>
+
+<IfModule mod_myfixip.c>
+ RewriteIPResetHeader off
+ RewriteIPAllow 172.31.0.0/16
+</IfModule>
これで httpd を再起動します。すると? client の global IP address が記録されるようになりました。
xxx.xxx.xxx.xxx - - [17/Apr/2015:06:05:24 +0000] "GET / HTTP/1.1" 200 3 "-" "curl/7.40.0"
問題・課題
ざっと source を見たかぎり、ELB node の IP address を取る方法が分かりませんでした。C 力が足りない...。
ぐぐったら https://github.com/roadrunner2/mod-proxy-protocol というのもありましたが試していません...。