diff --git a/realip/real.go b/realip/real.go index 58835c7..d24e79b 100644 --- a/realip/real.go +++ b/realip/real.go @@ -21,6 +21,9 @@ var privateRanges = []ipRange{ {start: net.ParseIP("192.0.0.0"), end: net.ParseIP("192.0.0.255")}, {start: net.ParseIP("192.168.0.0"), end: net.ParseIP("192.168.255.255")}, {start: net.ParseIP("198.18.0.0"), end: net.ParseIP("198.19.255.255")}, + {start: net.ParseIP("::1"), end: net.ParseIP("::1")}, + {start: net.ParseIP("fc00::"), end: net.ParseIP("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, + {start: net.ParseIP("fe80::"), end: net.ParseIP("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, } // Get returns real ip from the given request @@ -28,8 +31,6 @@ func Get(r *http.Request) (string, error) { var firstIP string for _, h := range []string{"X-Forwarded-For", "X-Real-Ip"} { addresses := strings.Split(r.Header.Get(h), ",") - // march from right to left until we get a public address - // that will be the address right before our proxy. for i := len(addresses) - 1; i >= 0; i-- { ip := strings.TrimSpace(addresses[i]) realIP := net.ParseIP(ip) @@ -43,12 +44,10 @@ func Get(r *http.Request) (string, error) { } } - // if we cannot find a public address in X-Forwarded-For or X-Real-IP headers, fallback to first ip if firstIP != "" { return firstIP, nil } - // get IP from RemoteAddr ip, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return "", fmt.Errorf("can't parse ip %q: %w", r.RemoteAddr, err) @@ -62,21 +61,18 @@ func Get(r *http.Request) (string, error) { // inRange - check to see if a given ip address is within a range given func inRange(r ipRange, ipAddress net.IP) bool { - // strcmp type byte comparison - if bytes.Compare(ipAddress, r.start) >= 0 && bytes.Compare(ipAddress, r.end) < 0 { - return true - } - return false + // ensure the IPs are in the same format for comparison + ipAddress = ipAddress.To16() + r.start = r.start.To16() + r.end = r.end.To16() + return bytes.Compare(ipAddress, r.start) >= 0 && bytes.Compare(ipAddress, r.end) <= 0 } // isPrivateSubnet - check to see if this ip is in a private subnet func isPrivateSubnet(ipAddress net.IP) bool { - if ipCheck := ipAddress.To4(); ipCheck != nil { - for _, r := range privateRanges { - // check if this ip is in a private range - if inRange(r, ipAddress) { - return true - } + for _, r := range privateRanges { + if inRange(r, ipAddress) { + return true } } return false diff --git a/realip/real_test.go b/realip/real_test.go index 1d76228..e8f6a5f 100644 --- a/realip/real_test.go +++ b/realip/real_test.go @@ -93,6 +93,31 @@ func TestGetFromHeaders(t *testing.T) { assert.Error(t, err) assert.Equal(t, "", ip) }) + t.Run("X-Real-IP IPv6", func(t *testing.T) { + req, err := http.NewRequest("GET", "/something", http.NoBody) + assert.NoError(t, err) + req.Header.Add("X-Real-IP", "2001:0db8:85a3:0000:0000:8a2e:0370:7334") + adr, err := Get(req) + require.NoError(t, err) + assert.Equal(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", adr) + }) + t.Run("X-Forwarded-For last IPv6 public", func(t *testing.T) { + req, err := http.NewRequest("GET", "/something", http.NoBody) + assert.NoError(t, err) + req.Header.Add("X-Forwarded-For", "2001:db8::ff00:42:8329,::1,fc00::") + adr, err := Get(req) + require.NoError(t, err) + assert.Equal(t, "2001:db8::ff00:42:8329", adr) + }) + + t.Run("RemoteAddr IPv6 fallback", func(t *testing.T) { + req, err := http.NewRequest("GET", "/something", http.NoBody) + assert.NoError(t, err) + req.RemoteAddr = "[2001:db8::ff00:42:8329]:1234" + adr, err := Get(req) + require.NoError(t, err) + assert.Equal(t, "2001:db8::ff00:42:8329", adr) + }) } func TestGetFromRemoteAddr(t *testing.T) {