【Play Framework】 ELB 経由のアクセス元IPアドレスを取得する

ELB

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Play Framework に対するアクセスから ELB や nginx(リバースプロキシ)を経由した アクセス元IPアドレス を取得するには、少し工夫が必要です。

本記事におけるバージョン情報は下記の通りです。

  • Play Framework 2.4
  • Scala 2.11.4
  • nginx 1.6.2

下準備: IP確認するアプリケーションを用意する

アクセス元IPアドレスをどのように取得すればいいかを知るために、まず、アクセス元IPを確認するアプリケーションを作成しましょう。

ipcheck.scala.html

@(remoteAddr: String, xForwardedFor: String, xRealIp: String)

@main("IP Check") {
  <dl>
    <dt>Your remoteAddress</dt>
    <dd>@remoteAddr</dd>
    <dt>Your X-Forwarded-For</dt>
    <dd>@xForwardedFor</dd>
    <dt>Your X-Real-IP</dt>
    <dd>@xRealIp</dd>
  </dl>
}

remoteAddress として取得できるIPアドレス、 X-Forwarderd-For として取得できるIPアドレス、 X-Real-IP として取得できるIPアドレスをそれぞれ表示させます。

Application.scala

class Application extends Controller {

  def check = Action { req =>
    val remoteAddr = req.remoteAddress
    val xForwardedFor = req.headers.get("x-forwarded-for").getOrElse("Empty!")
    val xRealIp = req.headers.get("x-real-ip").getOrElse("Empty!")

    Ok(views.html.ipcheck(remoteAddr, xForwardedFor, xRealIp))
  }

}

remoteAddress, X-Forwarded-For, X-Real-IP をそれぞれ取得して、レンダリングさせます。 X-Forwarderd-For や X-Real-IP は、 リバースプロキシを介してアクセスされる際に、プロキシサーバによって追加されるヘッダのため、値が設定されていない可能性がある ことに留意してください。 今回は、値が設定されていなかった場合に Empty! と表示することにします。

自分で管理しているリバースプロキシを介する場合は、プロキシサーバの設定ファイルにこれらのヘッダを追加するよう記述する必要があります。

routes.conf

# IP check
GET /check controllers.Application.check

上記で作成したIPアドレス確認ページへアクセスできるようルーティングします。

これで下準備は完了です。実際に EC2 インスタンスにデプロイし、アクセスしてみましょう!

実際に試してみる

確認のために IP確認用のアプリケーションをデプロイしたEC2インスタンスELB を用意しました。

また、上記の EC2 インスタンス内には nginx をインストール済であり、ローカル内でリバースプロキシをかませてアプリケーションにアクセスさせるパターンも試してみます。 nginx の設定は、 Play Framework 公式の記述 に従っています。

実際にいろいろなルートからアクセスしてみると、結果は次のようになります。

IPアドレス一覧表

- IPアドレス
アクセス元IP XXX.AAA.BBB.CCC
ELBのPrivate IP 10.128.0.46
EC2インスタンスのPrivate ID 10.128.0.168

表示結果

- remoteAddress X-Forwarderd-For X-Real-IP
EC2インスタンスに直接アクセス XXX.AAA.BBB.CCC Empty! Empty!
nginxを経由してEC2インスタンスにアクセス XXX.AAA.BBB.CCC XXX.AAA.BBB.CCC XXX.AAA.BBB.CCC
ELBを経由してEC2インスタンスにアクセス XXX.AAA.BBB.CCC XXX.AAA.BBB.CCC Empty!
ELBとnginxを経由してEC2インスタンスにアクセス 10.128.0.46 XXX.AAA.BBB.CCC, 10.128.0.46 10.128.0.46

上記から、次の二点がわかります。

  • X-Forwarded-For には、カンマ区切りのIPアドレスが渡される。そのうちの先頭の要素は、アクセス元IPアドレス。
  • ELB は X-Forwarded-For を追加してくれるが、 X-Real-IP は追加してくれない。

まとめ

以上のことなどから、ELB や Nginx を介したアクセスのIPアドレスを取得するためには、次のように取得するのが安全と考えられます。

  1. X-Forwarded-For が設定されている場合は、カンマ区切りで先頭の要素をIPとして使用する。
  2. X-Forwarded-For が設定されていない場合は、remoteAddress をそのまま使用する。

このように取得しておけば、いつかアプリケーションサーバー周りのインフラ構成が変更されることになっても、問題なくアクセス元IPアドレスを取得することができるはずです! Have a nice day!

参考

  • yuba

    > X-Forwarded-For が設定されている場合は、カンマ区切りで先頭の要素をIPとして使用する。

    この方式だと、クライアント側が任意のIPアドレスを差し込むことができてしまいます。X-Forwarded-Forの一番左側にはLBもnginxも手を加えないので。

    X-Forwarded-Forは右側(末尾)からスキャンし、既知のロードバランサ・リバースプロキシに該当しないものを見つけたらそれを採用、という方式をとる必要があります。