Provide custom options of TrustedPlatform for another CDN services (#2906)

* refine TrustedPlatform and docs

* refactor for switch

* refactor switch to if statement
This commit is contained in:
Notealot 2021-10-24 08:34:03 +08:00 committed by GitHub
parent 3fe928994b
commit 2d3d6d2f13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 12 deletions

View File

@ -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) - [http2 server push](#http2-server-push)
- [Define format for the log of routes](#define-format-for-the-log-of-routes) - [Define format for the log of routes](#define-format-for-the-log-of-routes)
- [Set and get a cookie](#set-and-get-a-cookie) - [Set and get a cookie](#set-and-get-a-cookie)
- [Don't trust all proxies](#don't-trust-all-proxies)
- [Testing](#testing) - [Testing](#testing)
- [Users](#users) - [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 ## Testing
The `net/http/httptest` package is preferable way for HTTP testing. The `net/http/httptest` package is preferable way for HTTP testing.

View File

@ -735,20 +735,16 @@ func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (e
return bb.BindBody(body, obj) 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. // 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 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, // 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. // the remote IP (coming form Request.RemoteAddr) is returned.
func (c *Context) ClientIP() string { 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 { if c.engine.TrustedPlatform != "" {
case PlatformGoogleAppEngine: // Developers can define their own header of Trusted Platform or use predefined constants
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" {
return addr
}
case PlatformCloudflare:
if addr := c.requestHeader("CF-Connecting-IP"); addr != "" {
return addr return addr
} }
} }

View File

@ -1458,8 +1458,20 @@ func TestContextClientIP(t *testing.T) {
c.engine.TrustedPlatform = PlatformGoogleAppEngine c.engine.TrustedPlatform = PlatformGoogleAppEngine
assert.Equal(t, "50.50.50.50", c.ClientIP()) 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 = "" c.engine.TrustedPlatform = ""
assert.Equal(t, "40.40.40.40", c.ClientIP())
// Test the legacy flag
c.engine.AppEngine = true c.engine.AppEngine = true
assert.Equal(t, "50.50.50.50", c.ClientIP()) assert.Equal(t, "50.50.50.50", c.ClientIP())
c.engine.AppEngine = false c.engine.AppEngine = false

4
gin.go
View File

@ -59,10 +59,10 @@ type RoutesInfo []RouteInfo
const ( const (
// When running on Google App Engine. Trust X-Appengine-Remote-Addr // When running on Google App Engine. Trust X-Appengine-Remote-Addr
// for determining the client's IP // 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 // When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
// the client's IP // the client's IP
PlatformCloudflare = "cloudflare" PlatformCloudflare = "CF-Connecting-IP"
) )
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings. // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.