source: code/trunk/vendor/github.com/pires/go-proxyproto/policy.go@ 822

Last change on this file since 822 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 5.1 KB
RevLine 
[822]1package proxyproto
2
3import (
4 "fmt"
5 "net"
6 "strings"
7)
8
9// PolicyFunc can be used to decide whether to trust the PROXY info from
10// upstream. If set, the connecting address is passed in as an argument.
11//
12// See below for the different policies.
13//
14// In case an error is returned the connection is denied.
15type PolicyFunc func(upstream net.Addr) (Policy, error)
16
17// Policy defines how a connection with a PROXY header address is treated.
18type Policy int
19
20const (
21 // USE address from PROXY header
22 USE Policy = iota
23 // IGNORE address from PROXY header, but accept connection
24 IGNORE
25 // REJECT connection when PROXY header is sent
26 // Note: even though the first read on the connection returns an error if
27 // a PROXY header is present, subsequent reads do not. It is the task of
28 // the code using the connection to handle that case properly.
29 REJECT
30 // REQUIRE connection to send PROXY header, reject if not present
31 // Note: even though the first read on the connection returns an error if
32 // a PROXY header is not present, subsequent reads do not. It is the task
33 // of the code using the connection to handle that case properly.
34 REQUIRE
35 // SKIP accepts a connection without requiring the PROXY header
36 // Note: an example usage can be found in the SkipProxyHeaderForCIDR
37 // function.
38 SKIP
39)
40
41// SkipProxyHeaderForCIDR returns a PolicyFunc which can be used to accept a
42// connection from a skipHeaderCIDR without requiring a PROXY header, e.g.
43// Kubernetes pods local traffic. The def is a policy to use when an upstream
44// address doesn't match the skipHeaderCIDR.
45func SkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) PolicyFunc {
46 return func(upstream net.Addr) (Policy, error) {
47 ip, err := ipFromAddr(upstream)
48 if err != nil {
49 return def, err
50 }
51
52 if skipHeaderCIDR != nil && skipHeaderCIDR.Contains(ip) {
53 return SKIP, nil
54 }
55
56 return def, nil
57 }
58}
59
60// WithPolicy adds given policy to a connection when passed as option to NewConn()
61func WithPolicy(p Policy) func(*Conn) {
62 return func(c *Conn) {
63 c.ProxyHeaderPolicy = p
64 }
65}
66
67// LaxWhiteListPolicy returns a PolicyFunc which decides whether the
68// upstream ip is allowed to send a proxy header based on a list of allowed
69// IP addresses and IP ranges. In case upstream IP is not in list the proxy
70// header will be ignored. If one of the provided IP addresses or IP ranges
71// is invalid it will return an error instead of a PolicyFunc.
72func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error) {
73 allowFrom, err := parse(allowed)
74 if err != nil {
75 return nil, err
76 }
77
78 return whitelistPolicy(allowFrom, IGNORE), nil
79}
80
81// MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one
82// of the provided IP addresses or IP ranges is invalid.
83func MustLaxWhiteListPolicy(allowed []string) PolicyFunc {
84 pfunc, err := LaxWhiteListPolicy(allowed)
85 if err != nil {
86 panic(err)
87 }
88
89 return pfunc
90}
91
92// StrictWhiteListPolicy returns a PolicyFunc which decides whether the
93// upstream ip is allowed to send a proxy header based on a list of allowed
94// IP addresses and IP ranges. In case upstream IP is not in list reading on
95// the connection will be refused on the first read. Please note: subsequent
96// reads do not error. It is the task of the code using the connection to
97// handle that case properly. If one of the provided IP addresses or IP
98// ranges is invalid it will return an error instead of a PolicyFunc.
99func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error) {
100 allowFrom, err := parse(allowed)
101 if err != nil {
102 return nil, err
103 }
104
105 return whitelistPolicy(allowFrom, REJECT), nil
106}
107
108// MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic
109// if one of the provided IP addresses or IP ranges is invalid.
110func MustStrictWhiteListPolicy(allowed []string) PolicyFunc {
111 pfunc, err := StrictWhiteListPolicy(allowed)
112 if err != nil {
113 panic(err)
114 }
115
116 return pfunc
117}
118
119func whitelistPolicy(allowed []func(net.IP) bool, def Policy) PolicyFunc {
120 return func(upstream net.Addr) (Policy, error) {
121 upstreamIP, err := ipFromAddr(upstream)
122 if err != nil {
123 // something is wrong with the source IP, better reject the connection
124 return REJECT, err
125 }
126
127 for _, allowFrom := range allowed {
128 if allowFrom(upstreamIP) {
129 return USE, nil
130 }
131 }
132
133 return def, nil
134 }
135}
136
137func parse(allowed []string) ([]func(net.IP) bool, error) {
138 a := make([]func(net.IP) bool, len(allowed))
139 for i, allowFrom := range allowed {
140 if strings.LastIndex(allowFrom, "/") > 0 {
141 _, ipRange, err := net.ParseCIDR(allowFrom)
142 if err != nil {
143 return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP range: %v", allowFrom, err)
144 }
145
146 a[i] = ipRange.Contains
147 } else {
148 allowed := net.ParseIP(allowFrom)
149 if allowed == nil {
150 return nil, fmt.Errorf("proxyproto: given string %q is not a valid IP address", allowFrom)
151 }
152
153 a[i] = allowed.Equal
154 }
155 }
156
157 return a, nil
158}
159
160func ipFromAddr(upstream net.Addr) (net.IP, error) {
161 upstreamString, _, err := net.SplitHostPort(upstream.String())
162 if err != nil {
163 return nil, err
164 }
165
166 upstreamIP := net.ParseIP(upstreamString)
167 if nil == upstreamIP {
168 return nil, fmt.Errorf("proxyproto: invalid IP address")
169 }
170
171 return upstreamIP, nil
172}
Note: See TracBrowser for help on using the repository browser.