-
-
Notifications
You must be signed in to change notification settings - Fork 108
/
policy.go
206 lines (175 loc) · 6.24 KB
/
policy.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
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
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.
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
)
// 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.
func SkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) PolicyFunc {
return func(upstream net.Addr) (Policy, error) {
ip, err := ipFromAddr(upstream)
if err != nil {
return def, err
}
if skipHeaderCIDR != nil && skipHeaderCIDR.Contains(ip) {
return SKIP, nil
}
return def, nil
}
}
// 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
}
}
// 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.
func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error) {
allowFrom, err := parse(allowed)
if err != nil {
return nil, err
}
return whitelistPolicy(allowFrom, IGNORE), nil
}
// MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one
// of the provided IP addresses or IP ranges is invalid.
func MustLaxWhiteListPolicy(allowed []string) PolicyFunc {
pfunc, err := LaxWhiteListPolicy(allowed)
if err != nil {
panic(err)
}
return pfunc
}
// 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.
func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error) {
allowFrom, err := parse(allowed)
if err != nil {
return nil, err
}
return whitelistPolicy(allowFrom, REJECT), nil
}
// MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic
// if one of the provided IP addresses or IP ranges is invalid.
func MustStrictWhiteListPolicy(allowed []string) PolicyFunc {
pfunc, err := StrictWhiteListPolicy(allowed)
if err != nil {
panic(err)
}
return pfunc
}
func whitelistPolicy(allowed []func(net.IP) bool, def Policy) PolicyFunc {
return func(upstream net.Addr) (Policy, error) {
upstreamIP, err := ipFromAddr(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
}
// IgnoreProxyHeaderNotOnInterface retuns a ConnPolicyFunc which can be used to
// decide whether to use or ignore PROXY headers depending on the connection
// being made on a specific interface. This policy can be used when the server
// is bound to multiple interfaces but wants to allow on only one interface.
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
}
}