-
-
Notifications
You must be signed in to change notification settings - Fork 127
Expand file tree
/
Copy pathpolicy.go
More file actions
302 lines (261 loc) · 9.98 KB
/
policy.go
File metadata and controls
302 lines (261 loc) · 9.98 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
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package proxyproto
import (
"fmt"
"net"
"strings"
)
// PolicyFunc can be used to decide whether to trust the PROXY info from
// upstream. If set, the connecting address is passed in as an argument.
//
// See below for the different policies.
//
// In case an error is returned the connection is denied.
//
// Deprecated: use ConnPolicyFunc instead.
type PolicyFunc func(upstream net.Addr) (Policy, error)
// ConnPolicyFunc can be used to decide whether to trust the PROXY info
// based on connection policy options. If set, the connecting addresses
// (remote and local) are passed in as argument.
//
// See below for the different policies.
//
// In case an error is returned the connection is denied.
type ConnPolicyFunc func(connPolicyOptions ConnPolicyOptions) (Policy, error)
// ConnPolicyOptions contains the remote and local addresses of a connection.
type ConnPolicyOptions struct {
Upstream net.Addr
Downstream net.Addr
}
// Policy defines how a connection with a PROXY header address is treated.
type Policy int
const (
// USE address from PROXY header.
USE Policy = iota
// IGNORE address from PROXY header, but accept connection.
IGNORE
// REJECT connection when PROXY header is sent
// Note: even though the first read on the connection returns an error if
// a PROXY header is present, subsequent reads do not. It is the task of
// the code using the connection to handle that case properly.
REJECT
// REQUIRE connection to send PROXY header, reject if not present
// Note: even though the first read on the connection returns an error if
// a PROXY header is not present, subsequent reads do not. It is the task
// of the code using the connection to handle that case properly.
REQUIRE
// SKIP accepts a connection without requiring the PROXY header.
// Note: an example usage can be found in the SkipProxyHeaderForCIDR
// function.
SKIP
)
// ConnSkipProxyHeaderForCIDR returns a ConnPolicyFunc which can be used to accept
// a connection from a skipHeaderCIDR without requiring a PROXY header, e.g.
// Kubernetes pods local traffic. The def is a policy to use when an upstream
// address doesn't match the skipHeaderCIDR.
func ConnSkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) ConnPolicyFunc {
return func(connOpts ConnPolicyOptions) (Policy, error) {
ip, err := ipFromAddr(connOpts.Upstream)
if err != nil {
return def, err
}
if skipHeaderCIDR != nil && skipHeaderCIDR.Contains(ip) {
return SKIP, nil
}
return def, nil
}
}
// SkipProxyHeaderForCIDR returns a PolicyFunc which can be used to accept a
// connection from a skipHeaderCIDR without requiring a PROXY header, e.g.
// Kubernetes pods local traffic. The def is a policy to use when an upstream
// address doesn't match the skipHeaderCIDR.
//
// Deprecated: use ConnSkipProxyHeaderForCIDR instead.
func SkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) PolicyFunc {
connPolicy := ConnSkipProxyHeaderForCIDR(skipHeaderCIDR, def)
return func(upstream net.Addr) (Policy, error) {
return connPolicy(ConnPolicyOptions{Upstream: upstream})
}
}
// WithPolicy adds given policy to a connection when passed as option to NewConn().
func WithPolicy(p Policy) func(*Conn) {
return func(c *Conn) {
c.ProxyHeaderPolicy = p
}
}
// ConnLaxWhiteListPolicy returns a ConnPolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list the proxy
// header will be ignored. If one of the provided IP addresses or IP ranges
// is invalid it will return an error instead of a ConnPolicyFunc.
func ConnLaxWhiteListPolicy(allowed []string) (ConnPolicyFunc, error) {
allowFrom, err := parse(allowed)
if err != nil {
return nil, err
}
return connWhitelistPolicy(allowFrom, IGNORE), nil
}
// LaxWhiteListPolicy returns a PolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list the proxy
// header will be ignored. If one of the provided IP addresses or IP ranges
// is invalid it will return an error instead of a PolicyFunc.
//
// Deprecated: use ConnLaxWhiteListPolicy instead.
func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error) {
connPolicy, err := ConnLaxWhiteListPolicy(allowed)
if err != nil {
return nil, err
}
return func(upstream net.Addr) (Policy, error) {
return connPolicy(ConnPolicyOptions{Upstream: upstream})
}, nil
}
// ConnMustLaxWhiteListPolicy returns a ConnLaxWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
func ConnMustLaxWhiteListPolicy(allowed []string) ConnPolicyFunc {
pfunc, err := ConnLaxWhiteListPolicy(allowed)
if err != nil {
panic(err)
}
return pfunc
}
// MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one
// of the provided IP addresses or IP ranges is invalid.
//
// Deprecated: use ConnMustLaxWhiteListPolicy instead.
func MustLaxWhiteListPolicy(allowed []string) PolicyFunc {
connPolicy := ConnMustLaxWhiteListPolicy(allowed)
return func(upstream net.Addr) (Policy, error) {
return connPolicy(ConnPolicyOptions{Upstream: upstream})
}
}
// ConnStrictWhiteListPolicy returns a ConnPolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list reading on
// the connection will be refused on the first read. Please note: subsequent
// reads do not error. It is the task of the code using the connection to
// handle that case properly. If one of the provided IP addresses or IP
// ranges is invalid it will return an error instead of a ConnPolicyFunc.
func ConnStrictWhiteListPolicy(allowed []string) (ConnPolicyFunc, error) {
allowFrom, err := parse(allowed)
if err != nil {
return nil, err
}
return connWhitelistPolicy(allowFrom, REJECT), nil
}
// StrictWhiteListPolicy returns a PolicyFunc which decides whether the
// upstream ip is allowed to send a proxy header based on a list of allowed
// IP addresses and IP ranges. In case upstream IP is not in list reading on
// the connection will be refused on the first read. Please note: subsequent
// reads do not error. It is the task of the code using the connection to
// handle that case properly. If one of the provided IP addresses or IP
// ranges is invalid it will return an error instead of a PolicyFunc.
//
// Deprecated: use ConnStrictWhiteListPolicy instead.
func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error) {
connPolicy, err := ConnStrictWhiteListPolicy(allowed)
if err != nil {
return nil, err
}
return func(upstream net.Addr) (Policy, error) {
return connPolicy(ConnPolicyOptions{Upstream: upstream})
}, nil
}
// ConnMustStrictWhiteListPolicy returns a ConnStrictWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
func ConnMustStrictWhiteListPolicy(allowed []string) ConnPolicyFunc {
pfunc, err := ConnStrictWhiteListPolicy(allowed)
if err != nil {
panic(err)
}
return pfunc
}
// MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
//
// Deprecated: use ConnMustStrictWhiteListPolicy instead.
func MustStrictWhiteListPolicy(allowed []string) PolicyFunc {
connPolicy := ConnMustStrictWhiteListPolicy(allowed)
return func(upstream net.Addr) (Policy, error) {
return connPolicy(ConnPolicyOptions{Upstream: upstream})
}
}
func connWhitelistPolicy(allowed []func(net.IP) bool, def Policy) ConnPolicyFunc {
return func(connOpts ConnPolicyOptions) (Policy, error) {
upstreamIP, err := ipFromAddr(connOpts.Upstream)
if err != nil {
// something is wrong with the source IP, better reject the connection
return REJECT, err
}
for _, allowFrom := range allowed {
if allowFrom(upstreamIP) {
return USE, nil
}
}
return def, nil
}
}
func parse(allowed []string) ([]func(net.IP) bool, error) {
a := make([]func(net.IP) bool, len(allowed))
for i, allowFrom := range allowed {
if strings.LastIndex(allowFrom, "/") > 0 {
_, ipRange, err := net.ParseCIDR(allowFrom)
if err != nil {
return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP range: %v", allowFrom, err)
}
a[i] = ipRange.Contains
} else {
allowed := net.ParseIP(allowFrom)
if allowed == nil {
return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP address", allowFrom)
}
a[i] = allowed.Equal
}
}
return a, nil
}
func ipFromAddr(upstream net.Addr) (net.IP, error) {
upstreamString, _, err := net.SplitHostPort(upstream.String())
if err != nil {
return nil, err
}
upstreamIP := net.ParseIP(upstreamString)
if nil == upstreamIP {
return nil, fmt.Errorf("proxyproto: invalid IP address")
}
return upstreamIP, nil
}
// TrustProxyHeaderFrom returns a ConnPolicyFunc which can be used to decide
// whether to use or reject PROXY headers based on the source IP of the
// connection. This policy ensures that only trusted sources can set the PROXY
// header. Connections from IPs not in the trusted list will be rejected.
func TrustProxyHeaderFrom(trustedIPs ...net.IP) ConnPolicyFunc {
return func(connOpts ConnPolicyOptions) (Policy, error) {
ip, err := ipFromAddr(connOpts.Upstream)
if err != nil {
return REJECT, err
}
for _, trustedIP := range trustedIPs {
if trustedIP.Equal(ip) {
return USE, nil
}
}
return REJECT, nil
}
}
// IgnoreProxyHeaderNotOnInterface returns a ConnPolicyFunc which can be used to
// decide whether to use or ignore PROXY headers depending on the connection
// being made on specific interfaces. This policy can be used when the server
// is bound to multiple interfaces but wants to allow on one or more interfaces.
func IgnoreProxyHeaderNotOnInterface(allowedIP net.IP) ConnPolicyFunc {
return func(connOpts ConnPolicyOptions) (Policy, error) {
ip, err := ipFromAddr(connOpts.Downstream)
if err != nil {
return REJECT, err
}
if allowedIP.Equal(ip) {
return USE, nil
}
return IGNORE, nil
}
}