とある機能のアプリケーションログをLaravelで出力して、社内からのアクセスを判別して抜くために、エンドユーザのIPを取得したい、という目的で調べた結果をまとめたものです。
LaravelでエンドユーザのIPを取得したい時には Request::setTrustedProxy
で信頼できる Proxy を設定して X-Forwarded-For からエンドユーザのIPを取得するという情報をキャッチしたのでやってみましたが、ちょっとハマりました。
参考: https://qiita.com/sh-watanabe/items/c3695bcecccd00fd91dd
環境
- laravel 5.8 (古いバージョンからあげているっているのでアプリケーションレイヤのコードは一部古い可能性があります)
- PHP 7.2
結論
結論でいうと、以下の公式ドキュメントにある「信用するプロキシの設定」を使えばOKでした。
https://readouble.com/laravel/5.8/ja/requests.html
すなわち、TrustProxies Middleware(app/Http/Middleware/TrustProxies.php
)を Laravel Kernel で呼び出し、 proxies と headers を設定すればOK。
特に、proxies についてはAWS環境であれば*
を設定してあげる。
ハマりどころ(symfony の設定メソッドを直接呼び出そうとする)
setTrustedProxy は Laravel を構成している symfony の機能として提供されていると聞いたので、それを直接呼び出そうとしたらハマりどころがありました。
- このメソッドを直接使っても動く条件は Controller 内部で $request を受け取っており、そこで設定する。これは後述の上書きがすでに終わっており、問題が起こらないレイヤです。
- 動かない条件はLaravel middleware を独自で作成し設定しようとすること(特に上述のLaravelにより提供されている TrustProxies Middleware の前で設定しようとすると)
なぜこういうことが起こるかというと、
-
app/Http/Kernel.php
の内部で呼び出す middleware を順番に定義している
protected $middleware = [
// この前に独自定義しようとすると動かない(TrustProxiesを今まで使ったことないので、そういうものがあるのを知らず、最初は独自定義しようとした)
\App\Http\Middleware\TrustProxies::class,
// 略
];
-
app/Http/Middleware/TrustProxies.php
の親であるvendor/fideloper/proxy/src/TrustProxies.php
の handle() は下記のように一度リセットしてから、改めて先ほど見た$proxies
変数に設定されているものを見ています。
public function handle(Request $request, Closure $next)
{
// ここでリセット
$request::setTrustedProxies([], $this->getTrustedHeaderNames()); // Reset trusted proxies between requests
$this->setTrustedProxyIpAddresses($request);
return $next($request);
}
protected function setTrustedProxyIpAddresses(Request $request)
{
// ここで改めて設定取得している
$trustedIps = $this->proxies ?: $this->config->get('trustedproxy.proxies');
// Only trust specific IP addresses
if (is_array($trustedIps)) {
return $this->setTrustedProxyIpAddressesToSpecificIps($request, $trustedIps);
}
// Trust any IP address that calls us
// `**` for backwards compatibility, but is depreciated
if ($trustedIps === '*' || $trustedIps === '**') {
return $this->setTrustedProxyIpAddressesToTheCallingIp($request);
}
}
終わりに(反省会会場)
Middleware あたりにこういう設定のところがあるよ、となんとなく知っていればすんなり解決したかもしれないが、適当にググって symfony の setTrustedProxies メソッドがあるよ、から入ってしまったので Middleware に設定があると気がつくまでに時間がかかってしまった。
普段からLaravelのフレームワークのコードリーディングを意識的にやっていれば、このレイヤにこういった設定があるのは(後から思い返せば)実に自然なので、困った時だけじゃなくて普段使いにおいても徐々に読み進めておくのが良さそう。