feat(engine): add trustedproxies and remoteIP (#2632)

Co-authored-by: Søren L. Hansen <soren@linux2go.dk>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: thinkerou <thinkerou@gmail.com>
Co-authored-by: Javier Provecho Fernandez <javiertitan@gmail.com>
This commit is contained in:
Manu MA
2021-04-06 05:37:25 +02:00
committed by GitHub
parent f3de8132c5
commit bfc8ca285e
6 changed files with 392 additions and 23 deletions
+71 -2
View File
@@ -11,6 +11,7 @@ import (
"net/http"
"os"
"path"
"strings"
"sync"
"github.com/gin-gonic/gin/internal/bytesconv"
@@ -81,9 +82,26 @@ type Engine struct {
// If no other Method is allowed, the request is delegated to the NotFound
// handler.
HandleMethodNotAllowed bool
ForwardedByClientIP bool
// #726 #755 If enabled, it will thrust some headers starting with
// If enabled, client IP will be parsed from the request's headers that
// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
// fetched, it falls back to the IP obtained from
// `(*gin.Context).Request.RemoteAddr`.
ForwardedByClientIP bool
// List of headers used to obtain the client IP when
// `(*gin.Engine).ForwardedByClientIP` is `true` and
// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
// network origins of `(*gin.Engine).TrustedProxies`.
RemoteIPHeaders []string
// List of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
// IPv6 CIDRs) from which to trust request's headers that contain
// alternative client IP when `(*gin.Engine).ForwardedByClientIP` is
// `true`.
TrustedProxies []string
// #726 #755 If enabled, it will trust some headers starting with
// 'X-AppEngine...' for better integration with that PaaS.
AppEngine bool
@@ -114,6 +132,7 @@ type Engine struct {
pool sync.Pool
trees methodTrees
maxParams uint16
trustedCIDRs []*net.IPNet
}
var _ IRouter = &Engine{}
@@ -139,6 +158,8 @@ func New() *Engine {
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
RemoteIPHeaders: []string{"X-Forwarded-For", "X-Real-IP"},
TrustedProxies: []string{"0.0.0.0/0"},
AppEngine: defaultAppEngine,
UseRawPath: false,
RemoveExtraSlash: false,
@@ -305,12 +326,60 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
trustedCIDRs, err := engine.prepareTrustedCIDRs()
if err != nil {
return err
}
engine.trustedCIDRs = trustedCIDRs
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
if engine.TrustedProxies == nil {
return nil, nil
}
cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies))
for _, trustedProxy := range engine.TrustedProxies {
if !strings.Contains(trustedProxy, "/") {
ip := parseIP(trustedProxy)
if ip == nil {
return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
}
switch len(ip) {
case net.IPv4len:
trustedProxy += "/32"
case net.IPv6len:
trustedProxy += "/128"
}
}
_, cidrNet, err := net.ParseCIDR(trustedProxy)
if err != nil {
return cidr, err
}
cidr = append(cidr, cidrNet)
}
return cidr, nil
}
// parseIP parse a string representation of an IP and returns a net.IP with the
// minimum byte representation or nil if input is invalid.
func parseIP(ip string) net.IP {
parsedIP := net.ParseIP(ip)
if ipv4 := parsedIP.To4(); ipv4 != nil {
// return ip in a 4-byte representation
return ipv4
}
// return ip in a 16-byte representation or nil
return parsedIP
}
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.