Cloudflare DNS (Proxied) - Cloud Run - Gin で正しいアクセス元 IP アドレスを記録する
結論
cf-connecting-ip
リクエストヘッダーを参照するよう設定する。
g := gin.Default()
g.TrustedPlatform = gin.PlatformCloudflare
構成
デフォルトでは Cloudflare の IP アドレスが記録される
何も設定しないと、 Cloudflare の IP アドレスが記録されます。
[GIN] 2023/12/16 - 00:39:03 | 200 | 10m1s | 162.158.119.178 | GET "/v2/ws"
[GIN] 2023/12/16 - 00:48:26 | 200 | 10m1s | 172.70.222.243 | GET "/v2/ws"
本来のアクセス元 IP アドレスは CF-Connecting-IP ヘッダーにある
Cloudflare のドキュメントを確認すると、本来のアクセス元 IP アドレスは CF-Connecting-IP
ヘッダーにあるとの記述があります。
CF-Connecting-IP
CF-Connecting-IP provides the client IP address connecting to Cloudflare to the origin web server. This header will only be sent on the traffic from Cloudflare’s edge to your origin web server.
Cloudflare HTTP request headers · Cloudflare Fundamentals docs
Gin で CF-Connecting-IP ヘッダーを参照する
Gin でこのヘッダーを参照するには、 TrustedPlatform
フィールドに Cloudflare の定数を指定します。
g.TrustedPlatform = gin.PlatformCloudflare
正しく記録されるようになりました。
[GIN] 2023/12/16 - 07:44:44 | 200 | 49.018309172s | 2406:da14:cad:7302:9c8b:9c22:9a24:5729 | GET "/v2/ws"
[GIN] 2023/12/16 - 07:46:13 | 200 | 2.337363401s | 2406:da14:cad:7302:9c8b:9c22:9a24:5729 | GET "/v2/ws"
なぜ TrustedPlatform = gin.PlatformCloudflare でうまくいくのか
フィールド名からはピンと来ませんが、コメントを読むと「gin.Platform* の定数で設定しておくと、そのプラットフォームで設定されたヘッダーを信頼する」とあります。
// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by // that platform, for example to determine the client IP TrustedPlatform string
どういう仕組みなのか、コードを読んでいきます。
定数の定義を確認すると、 "CF-Connecting-IP"
となっています。
// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining // the client's IP PlatformCloudflare = "CF-Connecting-IP"
ではどのように使われているのか、 TrustedPlatform フィールドの使い道を調べると、 Context.ClientIP()
メソッドに辿り着きます。
if c.engine.TrustedPlatform != "" { // Developers can define their own header of Trusted Platform or use predefined constants if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" { return addr } }
https://github.com/gin-gonic/gin/blob/v1.9.1/context.go#L773-L778
ここでリクエストヘッダーの CF-Connecting-IP
(TrustedPlatform) をアクセス元 IP アドレスとして返すようになっています。
めでたしめでたし。
参考
Discussion