playのRequest#remoteAddressで取れるのはIPアドレスではない
ちゃんと細かくドキュメントを読まず http://www.playframework.com/documentation/2.1.0/HTTPServer の書くままに設定してると痛い目にあう
突然カンマ区切りのIPアドレスがやってくる
FugaLogging.write(id, "投稿しました", request.remoteAddress)
こんな感じでログを残していたのだが突然IPアドレスがカンマ区切りで記録されていることが…
2014-02-10 23:36:41,933 XXXXXX A.B.C.D 2014-02-10 23:36:47,432 XXXXXX A.B.C.D 2014-02-10 23:37:47,142 XXXXXX A.B.C.D, E.F.G.H 2014-02-10 23:38:15,870 XXXXXX A.B.C.D 2014-02-10 23:38:20,336 XXXXXX A.B.C.D
うっ…
Request#remoteAddressはX-Forwarded-Forを返す
http://www.playframework.com/documentation/2.1.0/HTTPServerの下に記載されている通り次の場合はRequest#remoteAddressの返り値は接続元のIPアドレスではなくX-Forwarded-Forとなる。これは、nginxやApache等のリバースプロキシ存在下を考慮した仕様。
- 127.0.0.1からのアクセス
- application.conf等にtrustxforwarded=trueが書かれている
ドキュメントの通り次のようにnginxでproxy_set_headerを設定しているとX-Forwarded-Forには "接続元のIPアドレス" もしくは "接続元のIPアドレス, nginxにアクセスした時点で付加されていたX-Forwarded-For" という形になる。
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
アクセス元のIPアドレスだけを取る方法
今回のケースでは迷惑行為防止としてログに記録するのが目的なのでX-Forwarded-Forが全て記録されているのは好都合だったが、本当に「アクセス元のIPアドレス」がほしい場合があるかもしれない。
そういう場合は次のいずれかの方法をとれば良い。
- Request#remoteAddress の結果を分解して一番右のIPアドレスを得る (おすすめ)
- メリット: 確実
- デメリット: ちょっとだけ面倒
- (僕の頭でplayが勝手にX-Forwarded-Forの一番右のIPアドレスを取り出すのかと思い込んでいた…)
- 上記のドキュメント通りのnginxの設定だった場合、X-Real-IPヘッダを取り出し使う
- メリット: 簡単
- デメリット: playには直接アクセスできないことが条件(X-Real-IPヘッダが信頼できる状況)
- パフォーマンスの関係上、クライアントが直接playにリクエストを送りたい場合がある(一部の通信はnginxを通して, 一部の通信は直接playに…とか)と、X-Real-IPヘッダが信頼できるのかどうか判定できない。
- proxy_set_header X-Forwarded-For $remote_addr としてしまう
- メリット: 簡単
- デメリット: X-Forwarded-Forの意味からズレてる
Request#remoteAddress が playへの直接アクセスのときとリバースプロキシ通したときとで意味が変わるのはややこしいなあ…