mod_myfixip を試してみた

# 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 というのもありましたが試していません．．．。