From 4307d8386f1f10597795abbb7df936b996ddc6c3 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Tue, 19 Oct 2021 22:14:18 +0800 Subject: [PATCH 1/3] refine TrustedPlatform and docs --- README.md | 29 +++++++++++++++++++++++++++++ context.go | 11 +++++++++-- context_test.go | 14 +++++++++++++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 62094ff101..620f52e7c3 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [http2 server push](#http2-server-push) - [Define format for the log of routes](#define-format-for-the-log-of-routes) - [Set and get a cookie](#set-and-get-a-cookie) + - [Don't trust all proxies](#don't-trust-all-proxies) - [Testing](#testing) - [Users](#users) @@ -2236,6 +2237,34 @@ func main() { } ``` +**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform` +to skip TrustedProxies check, it has a higher priority than TrustedProxies. +Look at the example below: +```go +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +func main() { + + router := gin.Default() + // Use predefined header gin.PlatformXXX + router.TrustedPlatform = gin.PlatformGoogleAppEngine + // Or set your own trusted request header for another trusted proxy service + // Don't set it to any suspect request header, it's unsafe + router.TrustedPlatform = "X-CDN-IP" + + router.GET("/", func(c *gin.Context) { + // If you set TrustedPlatform, ClientIP() will resolve the + // corresponding header and return IP directly + fmt.Printf("ClientIP: %s\n", c.ClientIP()) + }) + router.Run() +} +``` + ## Testing The `net/http/httptest` package is preferable way for HTTP testing. diff --git a/context.go b/context.go index bea95ccaba..b4a362889c 100644 --- a/context.go +++ b/context.go @@ -733,14 +733,16 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e return bb.BindBody(body, obj) } -// ClientIP implements a best effort algorithm to return the real client IP. +// ClientIP implements one best effort algorithm to return the real client IP. // It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. // If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). // If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy, // the remote IP (coming form Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { - // Check if we're running on a trusted platform + // Check if we're running on a trusted platform, continue running backwards if error switch c.engine.TrustedPlatform { + case "": + // TrustedPlatform is empty, do nothing case PlatformGoogleAppEngine: if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { return addr @@ -749,6 +751,11 @@ func (c *Context) ClientIP() string { if addr := c.requestHeader("CF-Connecting-IP"); addr != "" { return addr } + default: + // Developers can define their own header of Trusted Platform + if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" { + return addr + } } // Legacy "AppEngine" flag diff --git a/context_test.go b/context_test.go index e9fe88f920..c286c0f4cb 100644 --- a/context_test.go +++ b/context_test.go @@ -1458,8 +1458,20 @@ func TestContextClientIP(t *testing.T) { c.engine.TrustedPlatform = PlatformGoogleAppEngine assert.Equal(t, "50.50.50.50", c.ClientIP()) - // Test the legacy flag + // Use custom TrustedPlatform header + c.engine.TrustedPlatform = "X-CDN-IP" + c.Request.Header.Set("X-CDN-IP", "80.80.80.80") + assert.Equal(t, "80.80.80.80", c.ClientIP()) + // wrong header + c.engine.TrustedPlatform = "X-Wrong-Header" + assert.Equal(t, "40.40.40.40", c.ClientIP()) + + c.Request.Header.Del("X-CDN-IP") + // TrustedPlatform is empty c.engine.TrustedPlatform = "" + assert.Equal(t, "40.40.40.40", c.ClientIP()) + + // Test the legacy flag c.engine.AppEngine = true assert.Equal(t, "50.50.50.50", c.ClientIP()) c.engine.AppEngine = false From 40b983805d59a0561328c14cf42660bf2a865094 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Thu, 21 Oct 2021 10:17:38 +0800 Subject: [PATCH 2/3] refactor for switch --- context.go | 10 +--------- gin.go | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/context.go b/context.go index b4a362889c..d12771e48a 100644 --- a/context.go +++ b/context.go @@ -743,16 +743,8 @@ func (c *Context) ClientIP() string { switch c.engine.TrustedPlatform { case "": // TrustedPlatform is empty, do nothing - case PlatformGoogleAppEngine: - if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { - return addr - } - case PlatformCloudflare: - if addr := c.requestHeader("CF-Connecting-IP"); addr != "" { - return addr - } default: - // Developers can define their own header of Trusted Platform + // Developers can define their own header of Trusted Platform or use predefined constants if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" { return addr } diff --git a/gin.go b/gin.go index af83161b68..3ef9b0fef6 100644 --- a/gin.go +++ b/gin.go @@ -59,10 +59,10 @@ type RoutesInfo []RouteInfo const ( // When running on Google App Engine. Trust X-Appengine-Remote-Addr // for determining the client's IP - PlatformGoogleAppEngine = "google-app-engine" + PlatformGoogleAppEngine = "X-Appengine-Remote-Addr" // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining // the client's IP - PlatformCloudflare = "cloudflare" + PlatformCloudflare = "CF-Connecting-IP" ) // Engine is the framework's instance, it contains the muxer, middleware and configuration settings. From 45a17535e9f8a43e215579987b05c27ce46948b6 Mon Sep 17 00:00:00 2001 From: Notealot <714804968@qq.com> Date: Thu, 21 Oct 2021 10:34:53 +0800 Subject: [PATCH 3/3] refactor switch to if statement --- context.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/context.go b/context.go index d12771e48a..5172fba6a3 100644 --- a/context.go +++ b/context.go @@ -740,10 +740,7 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e // the remote IP (coming form Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { // Check if we're running on a trusted platform, continue running backwards if error - switch c.engine.TrustedPlatform { - case "": - // TrustedPlatform is empty, do nothing - default: + 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