Skip to content

Commit 2a86aef

Browse files
author
Victor Tavares
committed
fix: correctly handle ipv6 in x-forwarded-for headers
1 parent f456f32 commit 2a86aef

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

clientip.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ func FromRequest(r *http.Request) net.IP {
5656
}
5757

5858
remoteAddr := r.RemoteAddr
59-
if strings.ContainsRune(remoteAddr, ':') {
60-
remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
59+
if raddr, ok := splitHostPort(remoteAddr); ok {
60+
remoteAddr = raddr
6161
}
6262

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

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

9287
return nil
9388
}
89+
90+
func splitHostPort(addr string) (string, bool) {
91+
raddr, _, err := net.SplitHostPort(addr)
92+
return raddr, raddr != "" && err == nil
93+
}

clientip_test.go

+61-1
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,21 @@ func TestFromRequest(t *testing.T) {
3232
req: createRequest("45.0.0.40", "x-client-ip", "45.9.248.40"),
3333
expectedIP: net.ParseIP("45.9.248.40"),
3434
},
35+
{
36+
name: "returns the value of x-client-ip",
37+
req: createRequest("45.0.0.40", "x-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
38+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
39+
},
3540
{
3641
name: "returns the first value of x-forwarded-for",
3742
req: createRequest("45.0.0.40", "x-forwarded-for", "129.78.138.66, 129.78.64.103, 129.78.64.105"),
3843
expectedIP: net.ParseIP("129.78.138.66"),
3944
},
45+
{
46+
name: "returns the first value of x-forwarded-for with ipv6",
47+
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"),
48+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
49+
},
4050
{
4151
name: "returns the first valid IP value of x-forwarded-for",
4252
req: createRequest("45.0.0.40", "x-forwarded-for", "unknown, 129.78.64.103, 129.78.64.105"),
@@ -47,41 +57,81 @@ func TestFromRequest(t *testing.T) {
4757
req: createRequest("45.0.0.40", "x-forwarded-for", "129.78.138.66:12345, 129.78.64.103, 129.78.64.105"),
4858
expectedIP: net.ParseIP("129.78.138.66"),
4959
},
60+
{
61+
name: "returns the correct IP value of x-forwarded-for with port",
62+
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"),
63+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
64+
},
5065
{
5166
name: "returns the value of cf-connecting-ip",
5267
req: createRequest("45.0.0.40", "cf-connecting-ip", "45.9.248.40"),
5368
expectedIP: net.ParseIP("45.9.248.40"),
5469
},
70+
{
71+
name: "returns the ipv6 value of cf-connecting-ip",
72+
req: createRequest("45.0.0.40", "cf-connecting-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
73+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
74+
},
5575
{
5676
name: "returns the value of fastly-client-ip",
5777
req: createRequest("45.0.0.40", "fastly-client-ip", "45.9.248.40"),
5878
expectedIP: net.ParseIP("45.9.248.40"),
5979
},
80+
{
81+
name: "returns the ipv6 value of fastly-client-ip",
82+
req: createRequest("45.0.0.40", "fastly-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
83+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
84+
},
6085
{
6186
name: "returns the value of true-client-ip",
6287
req: createRequest("45.0.0.40", "true-client-ip", "45.9.248.40"),
6388
expectedIP: net.ParseIP("45.9.248.40"),
6489
},
90+
{
91+
name: "returns the ipv6 value of true-client-ip",
92+
req: createRequest("45.0.0.40", "true-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
93+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
94+
},
6595
{
6696
name: "returns the value of x-real-ip",
6797
req: createRequest("45.0.0.40", "x-real-ip", "45.9.248.40"),
6898
expectedIP: net.ParseIP("45.9.248.40"),
6999
},
100+
{
101+
name: "returns the ipv6 value of x-real-ip",
102+
req: createRequest("45.0.0.40", "x-real-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
103+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
104+
},
70105
{
71106
name: "returns the value of x-cluster-client-ip",
72107
req: createRequest("45.0.0.40", "x-cluster-client-ip", "45.9.248.40"),
73108
expectedIP: net.ParseIP("45.9.248.40"),
74109
},
110+
{
111+
name: "returns the ipv6 value of x-cluster-client-ip",
112+
req: createRequest("45.0.0.40", "x-cluster-client-ip", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
113+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
114+
},
75115
{
76116
name: "returns the value of x-forwarded",
77117
req: createRequest("45.0.0.40", "x-forwarded", "45.9.248.40"),
78118
expectedIP: net.ParseIP("45.9.248.40"),
79119
},
120+
{
121+
name: "returns the ipv6 value of x-forwarded",
122+
req: createRequest("45.0.0.40", "x-forwarded", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
123+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
124+
},
80125
{
81126
name: "returns the value of forwarded-for",
82127
req: createRequest("45.0.0.40", "forwarded-for", "45.9.248.40"),
83128
expectedIP: net.ParseIP("45.9.248.40"),
84129
},
130+
{
131+
name: "returns the ipv6 value of forwarded-for",
132+
req: createRequest("45.0.0.40", "forwarded-for", "2001:0db8:0123:4567:89ab:cdef:1234:5678"),
133+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
134+
},
85135
{
86136
name: "returns the correct value of request.RemoteAddr when it contains the port",
87137
req: createRequest("45.0.0.40:8080"),
@@ -92,6 +142,16 @@ func TestFromRequest(t *testing.T) {
92142
req: createRequest("45.0.0.40"),
93143
expectedIP: net.ParseIP("45.0.0.40"),
94144
},
145+
{
146+
name: "returns the correct ipv6 value of request.RemoteAddr when it contains the port",
147+
req: createRequest("[2001:0db8:0123:4567:89ab:cdef:1234:5678]:8080"),
148+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
149+
},
150+
{
151+
name: "returns the correct ipv6 value of request.RemoteAddr when it doesn't contain the port",
152+
req: createRequest("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
153+
expectedIP: net.ParseIP("2001:0db8:0123:4567:89ab:cdef:1234:5678"),
154+
},
95155
{
96156
name: "returns nil when no valid IP was found",
97157
req: createRequest(""),
@@ -105,7 +165,7 @@ func TestFromRequest(t *testing.T) {
105165
t.Parallel()
106166
ip := FromRequest(tt.req)
107167
if !reflect.DeepEqual(tt.expectedIP, ip) {
108-
t.Errorf("expected %s to equal %s", tt.expectedIP, ip)
168+
t.Errorf("expected %s to equal %s", ip, tt.expectedIP)
109169
}
110170
})
111171
}

0 commit comments

Comments
 (0)