➡️

Cloudflare DNS (Proxied) - Cloud Run - Gin で正しいアクセス元 IP アドレスを記録する

2023/12/16に公開

結論

cf-connecting-ip リクエストヘッダーを参照するよう設定する。

	g := gin.Default()
	g.TrustedPlatform = gin.PlatformCloudflare

構成

Cloudflare DNS (Proxied) -> Cloud Run -> Gin (go)

デフォルトでは 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

gin package - github.com/gin-gonic/gin - Go Packages

どういう仕組みなのか、コードを読んでいきます。

定数の定義を確認すると、 "CF-Connecting-IP" となっています。

	// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
	// the client's IP
	PlatformCloudflare = "CF-Connecting-IP"

https://github.com/gin-gonic/gin/blob/v1.9.1/gin.go#L77-L79

ではどのように使われているのか、 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