Skip to content

Commit

Permalink
fix: correctly handle ipv6 in x-forwarded-for headers
Browse files Browse the repository at this point in the history
  • Loading branch information
Victor Tavares committed Mar 28, 2020
1 parent f456f32 commit 2a86aef
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 10 deletions.
18 changes: 9 additions & 9 deletions clientip.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ func FromRequest(r *http.Request) net.IP {
}

remoteAddr := r.RemoteAddr
if strings.ContainsRune(remoteAddr, ':') {
remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
if raddr, ok := splitHostPort(remoteAddr); ok {
remoteAddr = raddr
}

return net.ParseIP(remoteAddr)
Expand All @@ -72,13 +72,8 @@ func fromXForwardedFor(xfwdfor string) net.IP {
// Azure Web App's also adds a port for some reason, so we'll only use the first part (the IP)
for _, ip := range strings.Split(xfwdfor, ",") {
ip = strings.TrimSpace(ip)
if strings.ContainsRune(ip, ':') {
// make sure we only use this if it's ipv4 (ip:port)
host, _, err := net.SplitHostPort(ip)
if err != nil {
continue
}
ip = host
if raddr, ok := splitHostPort(ip); ok {
ip = raddr
}

// Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650).
Expand All @@ -91,3 +86,8 @@ func fromXForwardedFor(xfwdfor string) net.IP {

return nil
}

func splitHostPort(addr string) (string, bool) {
raddr, _, err := net.SplitHostPort(addr)
return raddr, raddr != "" && err == nil
}
62 changes: 61 additions & 1 deletion clientip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,21 @@ func TestFromRequest(t *testing.T) {
req: createRequest("45.0.0.40", "x-client-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the value of x-client-ip",
req: createRequest("45.0.0.40", "x-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the first value of x-forwarded-for",
req: createRequest("45.0.0.40", "x-forwarded-for", "129.78.138.66, 129.78.64.103, 129.78.64.105"),
expectedIP: net.ParseIP("129.78.138.66"),
},
{
name: "returns the first value of x-forwarded-for with ipv6",
req: createRequest("45.0.0.40", "x-forwarded-for", "2001:0db8:0123:4567:89ab:cdef:1234:5678, 129.78.64.103, 129.78.64.105"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the first valid IP value of x-forwarded-for",
req: createRequest("45.0.0.40", "x-forwarded-for", "unknown, 129.78.64.103, 129.78.64.105"),
Expand All @@ -47,41 +57,81 @@ func TestFromRequest(t *testing.T) {
req: createRequest("45.0.0.40", "x-forwarded-for", "129.78.138.66:12345, 129.78.64.103, 129.78.64.105"),
expectedIP: net.ParseIP("129.78.138.66"),
},
{
name: "returns the correct IP value of x-forwarded-for with port",
req: createRequest("45.0.0.40", "x-forwarded-for", "[2001:0db8:0123:4567:89ab:cdef:1234:5678]:12345, 129.78.64.103, 129.78.64.105"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of cf-connecting-ip",
req: createRequest("45.0.0.40", "cf-connecting-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of cf-connecting-ip",
req: createRequest("45.0.0.40", "cf-connecting-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of fastly-client-ip",
req: createRequest("45.0.0.40", "fastly-client-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of fastly-client-ip",
req: createRequest("45.0.0.40", "fastly-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of true-client-ip",
req: createRequest("45.0.0.40", "true-client-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of true-client-ip",
req: createRequest("45.0.0.40", "true-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of x-real-ip",
req: createRequest("45.0.0.40", "x-real-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of x-real-ip",
req: createRequest("45.0.0.40", "x-real-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of x-cluster-client-ip",
req: createRequest("45.0.0.40", "x-cluster-client-ip", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of x-cluster-client-ip",
req: createRequest("45.0.0.40", "x-cluster-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of x-forwarded",
req: createRequest("45.0.0.40", "x-forwarded", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of x-forwarded",
req: createRequest("45.0.0.40", "x-forwarded", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the value of forwarded-for",
req: createRequest("45.0.0.40", "forwarded-for", "45.9.248.40"),
expectedIP: net.ParseIP("45.9.248.40"),
},
{
name: "returns the ipv6 value of forwarded-for",
req: createRequest("45.0.0.40", "forwarded-for", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the correct value of request.RemoteAddr when it contains the port",
req: createRequest("45.0.0.40:8080"),
Expand All @@ -92,6 +142,16 @@ func TestFromRequest(t *testing.T) {
req: createRequest("45.0.0.40"),
expectedIP: net.ParseIP("45.0.0.40"),
},
{
name: "returns the correct ipv6 value of request.RemoteAddr when it contains the port",
req: createRequest("[2001:0db8:0123:4567:89ab:cdef:1234:5678]:8080"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns the correct ipv6 value of request.RemoteAddr when it doesn't contain the port",
req: createRequest("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
},
{
name: "returns nil when no valid IP was found",
req: createRequest(""),
Expand All @@ -105,7 +165,7 @@ func TestFromRequest(t *testing.T) {
t.Parallel()
ip := FromRequest(tt.req)
if !reflect.DeepEqual(tt.expectedIP, ip) {
t.Errorf("expected %s to equal %s", tt.expectedIP, ip)
t.Errorf("expected %s to equal %s", ip, tt.expectedIP)
}
})
}
Expand Down

0 comments on commit 2a86aef

Please sign in to comment.