forked from traefik/traefik
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ip.go
145 lines (120 loc) · 2.86 KB
/
ip.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
140
141
142
143
144
145
package whitelist
import (
"errors"
"fmt"
"net"
"net/http"
"strings"
)
const (
// XForwardedFor Header name
XForwardedFor = "X-Forwarded-For"
)
// IP allows to check that addresses are in a white list
type IP struct {
whiteListsIPs []*net.IP
whiteListsNet []*net.IPNet
insecure bool
useXForwardedFor bool
}
// NewIP builds a new IP given a list of CIDR-Strings to white list
func NewIP(whiteList []string, insecure bool, useXForwardedFor bool) (*IP, error) {
if len(whiteList) == 0 && !insecure {
return nil, errors.New("no white list provided")
}
ip := IP{
insecure: insecure,
useXForwardedFor: useXForwardedFor,
}
if !insecure {
for _, ipMask := range whiteList {
if ipAddr := net.ParseIP(ipMask); ipAddr != nil {
ip.whiteListsIPs = append(ip.whiteListsIPs, &ipAddr)
} else {
_, ipAddr, err := net.ParseCIDR(ipMask)
if err != nil {
return nil, fmt.Errorf("parsing CIDR white list %s: %v", ipAddr, err)
}
ip.whiteListsNet = append(ip.whiteListsNet, ipAddr)
}
}
}
return &ip, nil
}
// IsAuthorized checks if provided request is authorized by the white list
func (ip *IP) IsAuthorized(req *http.Request) error {
if ip.insecure {
return nil
}
var invalidMatches []string
if ip.useXForwardedFor {
xFFs := req.Header[XForwardedFor]
if len(xFFs) > 0 {
for _, xFF := range xFFs {
xffs := strings.Split(xFF, ",")
for _, xff := range xffs {
ok, err := ip.contains(parseHost(xff))
if err != nil {
return err
}
if ok {
return nil
}
invalidMatches = append(invalidMatches, xff)
}
}
}
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
return err
}
ok, err := ip.contains(host)
if err != nil {
return err
}
if !ok {
invalidMatches = append(invalidMatches, req.RemoteAddr)
return fmt.Errorf("%q matched none of the white list", strings.Join(invalidMatches, ", "))
}
return nil
}
// contains checks if provided address is in the white list
func (ip *IP) contains(addr string) (bool, error) {
ipAddr, err := parseIP(addr)
if err != nil {
return false, fmt.Errorf("unable to parse address: %s: %s", addr, err)
}
return ip.ContainsIP(ipAddr), nil
}
// ContainsIP checks if provided address is in the white list
func (ip *IP) ContainsIP(addr net.IP) bool {
if ip.insecure {
return true
}
for _, whiteListIP := range ip.whiteListsIPs {
if whiteListIP.Equal(addr) {
return true
}
}
for _, whiteListNet := range ip.whiteListsNet {
if whiteListNet.Contains(addr) {
return true
}
}
return false
}
func parseIP(addr string) (net.IP, error) {
userIP := net.ParseIP(addr)
if userIP == nil {
return nil, fmt.Errorf("can't parse IP from address %s", addr)
}
return userIP, nil
}
func parseHost(addr string) string {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return addr
}
return host
}