-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrealip.go
139 lines (127 loc) · 3.25 KB
/
realip.go
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package realip
import (
"errors"
"net"
"net/http"
"strings"
)
// Popular Headers
const (
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXRealIP = "X-Real-IP"
// we can also specify True-Client-IP, CF-Connecting-IP and so on to c.RealIPHeader
headerForwarded = "Forwarded"
)
// Middleware returns a http middleware detecting the real IP of the client from request
// and set it in the request header.
func Middleware(c *Config) (func(http.Handler) http.Handler, error) {
if c == nil {
c = &Config{}
}
if strings.ToLower(c.realIPHeader()) == strings.ToLower(headerForwarded) {
return nil, errors.New("haven't supported Forwarded header yet")
}
return func(next http.Handler) http.Handler {
return c.handler(next)
}, nil
}
// MustMiddleware returns a http middleware of realip. It panics when error occurred.
func MustMiddleware(c *Config) func(http.Handler) http.Handler {
m, err := Middleware(c)
if err != nil {
panic(err)
}
return m
}
// Config is configuration for realip middleware.
// The fields naming of it is similar to ngx_http_realip_module.
type Config struct {
RealIPFrom []*net.IPNet
RealIPHeader string
RealIPRecursive bool
SetHeader string
}
func remoteIP(remoteAddr string) string {
ip, _, _ := net.SplitHostPort(remoteAddr)
return ip
}
func (c *Config) handler(next http.Handler) http.Handler {
switch c.realIPHeader() {
case HeaderXForwardedFor:
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if !trustedIP(net.ParseIP(remoteIP(req.RemoteAddr)), c.RealIPFrom) {
if realIP := remoteIP(req.RemoteAddr); realIP != "" {
req.Header.Set(c.setHeader(), realIP)
}
} else {
realIP := realIPFromXFF(
req.Header.Get(HeaderXForwardedFor),
c.RealIPFrom,
c.RealIPRecursive)
if realIP == "" {
realIP = remoteIP(req.RemoteAddr)
}
if realIP != "" {
req.Header.Set(c.setHeader(), realIP)
}
}
next.ServeHTTP(w, req)
})
default:
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if !trustedIP(net.ParseIP(remoteIP(req.RemoteAddr)), c.RealIPFrom) {
if realIP := remoteIP(req.RemoteAddr); realIP != "" {
req.Header.Set(c.setHeader(), realIP)
}
} else {
realIP := req.Header.Get(c.realIPHeader())
if realIP == "" {
realIP = remoteIP(req.RemoteAddr)
}
if realIP != "" {
req.Header.Set(c.setHeader(), realIP)
}
}
next.ServeHTTP(w, req)
})
}
}
func trustedIP(ip net.IP, realIPFrom []*net.IPNet) bool {
if len(realIPFrom) == 0 {
return true
}
for _, fromIP := range realIPFrom {
if fromIP.Contains(ip) {
return true
}
}
return false
}
func (c *Config) realIPHeader() string {
if c.RealIPHeader == "" {
return HeaderXRealIP // default
}
return c.RealIPHeader
}
func (c *Config) setHeader() string {
if c.SetHeader == "" {
return HeaderXRealIP // default
}
return c.SetHeader
}
func realIPFromXFF(xff string, realIPFrom []*net.IPNet, recursive bool) string {
ips := strings.Split(xff, ",")
if len(ips) == 0 {
return ""
}
if !recursive {
return strings.TrimSpace(ips[len(ips)-1])
}
for i := len(ips) - 1; i >= 0; i-- {
ipStr := strings.TrimSpace(ips[i])
if !trustedIP(net.ParseIP(ipStr), realIPFrom) {
return ipStr
}
}
return strings.TrimSpace(ips[0])
}