-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxyaddr.go
More file actions
104 lines (93 loc) · 2.94 KB
/
proxyaddr.go
File metadata and controls
104 lines (93 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Package proxyaddr is a middleware for determining client address for proxied requests.
package proxyaddr
import (
"net"
"net/http"
"strings"
)
// ProxyAddr stores the group of CIDR of trusted proxies. With these and HTTP "x-forwarded-for" header,
// ProxyAddr determines actual IP address of the connecting client.
type ProxyAddr struct {
trustedProxies []*net.IPNet
}
const (
// CIDRLoopback represents IPv4 and IPv6 loopback addresses
CIDRLoopback = "loopback"
// CIDRLinkLocal represents IPv4 and IPv6 link-local addresses
CIDRLinkLocal = "linklocal"
// CIDRUniqueLocal represents IPv4 private addresses and IPv6 unique local addresses
CIDRUniqueLocal = "uniquelocal"
)
var commonCIDR = map[string][]string{
CIDRLinkLocal: {"169.254.0.0/16", "fe80::/10"},
CIDRLoopback: {"127.0.0.1/8", "::1/128"},
CIDRUniqueLocal: {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fc00::/7"},
}
// Init initializes the list of trusted proxies with provided string values.
// Without initializing, ProxyAddr trusts no IP address at all, and does not change RemoteAddr field.
//
// Parameters of Init can only be exported constants of this package or CIDR-formatted strings (like 10.0.0.0/8).
func (pa *ProxyAddr) Init(trustedProxies ...string) error {
pa.trustedProxies = nil
var trustedNetworks []*net.IPNet
for _, proxyCIDR := range trustedProxies {
if cidrList, ok := commonCIDR[proxyCIDR]; ok {
for _, cidr := range cidrList {
_, network, err := net.ParseCIDR(cidr)
if err != nil {
panic(err)
}
trustedNetworks = append(trustedNetworks, network)
}
continue
}
_, network, err := net.ParseCIDR(proxyCIDR)
if err != nil {
return err
}
trustedNetworks = append(trustedNetworks, network)
}
pa.trustedProxies = trustedNetworks
return nil
}
func (pa *ProxyAddr) trusted(ip net.IP) bool {
for _, network := range pa.trustedProxies {
if network.Contains(ip) {
return true
}
}
return false
}
func (pa *ProxyAddr) getRemoteAddr(r *http.Request) string {
addr, port, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
remoteAddr := net.ParseIP(addr)
if xff := r.Header.Get("x-forwarded-for"); xff != "" {
ipList := strings.Split(xff, ",")
for i := len(ipList) - 1; i >= 0; i-- {
if !pa.trusted(remoteAddr) {
break
}
ip := net.ParseIP(strings.TrimSpace(ipList[i]))
if ip == nil {
break
}
remoteAddr = ip
}
}
return net.JoinHostPort(remoteAddr.String(), port)
}
// Handler is the plain http.Handler which updates RemoteAddr with actual IP from x-forwarded-for header.
func (pa *ProxyAddr) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.RemoteAddr = pa.getRemoteAddr(r)
h.ServeHTTP(w, r)
})
}
// ServeHTTP is the implementation of Negroni middleware interface.
func (pa *ProxyAddr) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
r.RemoteAddr = pa.getRemoteAddr(r)
next(w, r)
}