3行まとめ
- CloudRunは
X-Forwarded-For
を上書きしてクライアントのIPアドレスにしてしまう - User - CDN - CloudRun としている場合、
X-Forwarded-For
の値はCDNのIPアドレスになってしまう - 解決策はCDN独自のヘッダーを付与してアプリケーション側で読むしか方法がなさそう
X-Forwarded-For
X-Forwarded-For
というヘッダーがあります。多分MDNを見るのが一番早いですが、色々なプロキシを通ってオリジンに到達するようなサーバー構成のときにクライアントのIPアドレスをうまく伝えるようなヘッダーです。
形式としてはX-Forwarded-For: <client>, <proxy1>, <proxy2>
のようになり、プロキシサーバーやCDNを通るとX-Forwarded-For
の値の右にそのプロキシやCDNのIPアドレスが追加されます。オリジンのWebサーバーはこのヘッダーを右から読んでいきIPアドレスが信頼されているものかどうかを確認して行き信頼できないIPアドレスが来たらそれがクライアントのIPアドレスという風に認識します*1*2。
このような仕様となっているX-Forwarded-For
は実はCloudRunでは末尾にIPを追加といったことをせずに直接クライアントのIPアドレスを代入します。こんな感じ、
X-Forwarded-For: 157.52.91.33
これは、実際にCloudRunの前段にFastlyを置いていて X-Forwarded-For | Fastly Documentation でX-Forwarded-For
を渡す設定にしていますがFastly側で設定されているIPアドレスが消えてしまっています。これではうまくWebアプリケーション側でIPアドレスを取得できません。
解決策
解決策としてはCDN側で別のヘッダーにクライアントのIPアドレスを付与することで解決ができます。Fastlyではぶっちゃけどんなヘッダーでもつけることができますが、ドキュメントではFastly-Client-IP
が紹介されているのでこれを使うと良さそうです。
他にもCloudflareではCF-Connecting-IP
が使えるようです。
もし、WebアプリケーションをGo/Echoで実装しているなら以下のようにIPExtractor
を追加することでカスタムヘッダからIPアドレスを取ることができます(Fastly ver)。
var extractIpFromXFFHeader = echo.ExtractIPFromXFFHeader() func ExtractIPFromFastlyHeader(req *http.Request) string { fastlyClientIp := req.Header.Get("Fastly-Client-IP") if fastlyClientIp != "" { return fastlyClientIp } return extractIpFromXFFHeader(req) } ... e := echo.New() e.IPExtractor = ExtractIPFromFastlyHeader
個人的感想
クライアントのIPアドレスを決め打ちで渡すのであればX-Real-IP
というヘッダーがnginxなどにあるのでできればこっち使ってくれたらありがたかったけど今から仕様変えると動いているアプリケーションがすべて壊れるので難しいんだろうな。