mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 14:52:18 +08:00
feat(engine): add ipv6 support to TrustedProxies
This commit is contained in:
parent
a598663fde
commit
7e649c347f
@ -2123,9 +2123,10 @@ Gin lets you specify which headers to hold the real client IP (if any),
|
|||||||
as well as specifying which proxies (or direct clients) you trust to
|
as well as specifying which proxies (or direct clients) you trust to
|
||||||
specify one of these headers.
|
specify one of these headers.
|
||||||
|
|
||||||
The `TrustedProxies` slice on your `gin.Engine` specifes the clients
|
The `TrustedProxies` slice on your `gin.Engine` specifes network addresses or
|
||||||
trusted to specify unspoofed client IP headers. Proxies can be specified
|
network CIDRs from where clients which their request headers related to client
|
||||||
as IPs or CIDRs.
|
IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
||||||
|
IPv6 CIDRs.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
|
37
gin.go
37
gin.go
@ -95,9 +95,10 @@ type Engine struct {
|
|||||||
// network origins of `(*gin.Engine).TrustedProxies`.
|
// network origins of `(*gin.Engine).TrustedProxies`.
|
||||||
RemoteIPHeaders []string
|
RemoteIPHeaders []string
|
||||||
|
|
||||||
// List of network origins (IPv4 addresses, IPv4 CIDRs or IPv6 addresses)
|
// List of network origins (IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
||||||
// from which to trust request's headers that contain alternative client
|
// IPv6 CIDRs) from which to trust request's headers that contain
|
||||||
// IP when `(*gin.Engine).ForwardedByClientIP` is `true`.
|
// alternative client IP when `(*gin.Engine).ForwardedByClientIP` is
|
||||||
|
// `true`.
|
||||||
TrustedProxies []string
|
TrustedProxies []string
|
||||||
|
|
||||||
// #726 #755 If enabled, it will trust some headers starting with
|
// #726 #755 If enabled, it will trust some headers starting with
|
||||||
@ -325,7 +326,7 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
|||||||
func (engine *Engine) Run(addr ...string) (err error) {
|
func (engine *Engine) Run(addr ...string) (err error) {
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
trustedCIDRs, err := engine.prepareCIDR()
|
trustedCIDRs, err := engine.prepareTrustedCIDRs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -336,12 +337,22 @@ func (engine *Engine) Run(addr ...string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) prepareCIDR() ([]*net.IPNet, error) {
|
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
||||||
if engine.TrustedProxies != nil {
|
if engine.TrustedProxies != nil {
|
||||||
cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies))
|
cidr := make([]*net.IPNet, 0, len(engine.TrustedProxies))
|
||||||
for _, trustedProxy := range engine.TrustedProxies {
|
for _, trustedProxy := range engine.TrustedProxies {
|
||||||
if !strings.Contains(trustedProxy, "/") {
|
if !strings.Contains(trustedProxy, "/") {
|
||||||
trustedProxy += "/32"
|
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)
|
_, cidrNet, err := net.ParseCIDR(trustedProxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -354,6 +365,20 @@ func (engine *Engine) prepareCIDR() ([]*net.IPNet, error) {
|
|||||||
return nil, nil
|
return nil, 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.
|
// 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)
|
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
|
125
gin_test.go
125
gin_test.go
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -532,6 +533,130 @@ func TestEngineHandleContextManyReEntries(t *testing.T) {
|
|||||||
assert.Equal(t, int64(expectValue), middlewareCounter)
|
assert.Equal(t, int64(expectValue), middlewareCounter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
||||||
|
r := New()
|
||||||
|
|
||||||
|
// valid ipv4 cidr
|
||||||
|
{
|
||||||
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")}
|
||||||
|
r.TrustedProxies = []string{"0.0.0.0/0"}
|
||||||
|
|
||||||
|
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid ipv4 cidr
|
||||||
|
{
|
||||||
|
r.TrustedProxies = []string{"192.168.1.33/33"}
|
||||||
|
|
||||||
|
_, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid ipv4 address
|
||||||
|
{
|
||||||
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("192.168.1.33/32")}
|
||||||
|
r.TrustedProxies = []string{"192.168.1.33"}
|
||||||
|
|
||||||
|
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid ipv4 address
|
||||||
|
{
|
||||||
|
r.TrustedProxies = []string{"192.168.1.256"}
|
||||||
|
|
||||||
|
_, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid ipv6 address
|
||||||
|
{
|
||||||
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
|
||||||
|
r.TrustedProxies = []string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"}
|
||||||
|
|
||||||
|
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid ipv6 address
|
||||||
|
{
|
||||||
|
r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"}
|
||||||
|
|
||||||
|
_, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid ipv6 cidr
|
||||||
|
{
|
||||||
|
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
|
||||||
|
r.TrustedProxies = []string{"::/0"}
|
||||||
|
|
||||||
|
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid ipv6 cidr
|
||||||
|
{
|
||||||
|
r.TrustedProxies = []string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"}
|
||||||
|
|
||||||
|
_, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid combination
|
||||||
|
{
|
||||||
|
expectedTrustedCIDRs := []*net.IPNet{
|
||||||
|
parseCIDR("::/0"),
|
||||||
|
parseCIDR("192.168.0.0/16"),
|
||||||
|
parseCIDR("172.16.0.1/32"),
|
||||||
|
}
|
||||||
|
r.TrustedProxies = []string{
|
||||||
|
"::/0",
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"172.16.0.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
trustedCIDRs, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expectedTrustedCIDRs, trustedCIDRs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invalid combination
|
||||||
|
{
|
||||||
|
r.TrustedProxies = []string{
|
||||||
|
"::/0",
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"172.16.0.256",
|
||||||
|
}
|
||||||
|
_, err := r.prepareTrustedCIDRs()
|
||||||
|
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCIDR(cidr string) *net.IPNet {
|
||||||
|
_, parsedCIDR, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return parsedCIDR
|
||||||
|
}
|
||||||
|
|
||||||
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
|
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
|
||||||
for _, gotRoute := range gotRoutes {
|
for _, gotRoute := range gotRoutes {
|
||||||
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
|
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user