Skip to content

Commit dc12bd9

Browse files
proxyprotocol: use github.com/pires/go-proxyproto (#5915)
* proxyprotocol: use github.com/pires/go-proxyproto * Fix typo: r/generelly/generally Co-authored-by: Francis Lavoie <[email protected]> * add config options for `Deny` CIDR and fallback policy * use `netip` package & trust unix sockets --------- Co-authored-by: Francis Lavoie <[email protected]>
1 parent 56c6b3f commit dc12bd9

File tree

6 files changed

+176
-42
lines changed

6 files changed

+176
-42
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ require (
1414
github.com/google/uuid v1.3.1
1515
github.com/klauspost/compress v1.17.0
1616
github.com/klauspost/cpuid/v2 v2.2.5
17-
github.com/mastercactapus/proxyprotocol v0.0.4
1817
github.com/mholt/acmez v1.2.0
1918
github.com/prometheus/client_golang v1.15.1
2019
github.com/quic-go/quic-go v0.40.0
@@ -117,6 +116,7 @@ require (
117116
github.com/mitchellh/copystructure v1.2.0 // indirect
118117
github.com/mitchellh/go-ps v1.0.0 // indirect
119118
github.com/mitchellh/reflectwalk v1.0.2 // indirect
119+
github.com/pires/go-proxyproto v0.7.0
120120
github.com/pkg/errors v0.9.1 // indirect
121121
github.com/prometheus/client_model v0.4.0 // indirect
122122
github.com/prometheus/common v0.42.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
352352
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
353353
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
354354
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
355-
github.com/mastercactapus/proxyprotocol v0.0.4 h1:qSY75IZF30ZqIU9iW1ip3I7gTnm8wRAnGWqPxCBVgq0=
356-
github.com/mastercactapus/proxyprotocol v0.0.4/go.mod h1:X8FRVEDZz9FkrIoL4QYTBF4Ka4ELwTv0sah0/5NxCPw=
357355
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
358356
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
359357
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -433,6 +431,8 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9
433431
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
434432
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
435433
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
434+
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
435+
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
436436
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
437437
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
438438
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

modules/caddyhttp/proxyprotocol/listenerwrapper.go

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
package proxyprotocol
1616

1717
import (
18-
"fmt"
1918
"net"
19+
"net/netip"
2020
"time"
2121

22-
"github.com/mastercactapus/proxyprotocol"
22+
goproxy "github.com/pires/go-proxyproto"
2323

2424
"github.com/caddyserver/caddy/v2"
2525
)
@@ -38,32 +38,74 @@ type ListenerWrapper struct {
3838
// Allow is an optional list of CIDR ranges to
3939
// allow/require PROXY headers from.
4040
Allow []string `json:"allow,omitempty"`
41+
allow []netip.Prefix
4142

42-
rules []proxyprotocol.Rule
43+
// Denby is an optional list of CIDR ranges to
44+
// deny PROXY headers from.
45+
Deny []string `json:"deny,omitempty"`
46+
deny []netip.Prefix
47+
48+
// Accepted values are: ignore, use, reject, require, skip
49+
// default: ignore
50+
// Policy definitions are here: https://pkg.go.dev/github.com/pires/[email protected]#Policy
51+
FallbackPolicy Policy `json:"fallback_policy,omitempty"`
52+
53+
policy goproxy.PolicyFunc
4354
}
4455

4556
// Provision sets up the listener wrapper.
4657
func (pp *ListenerWrapper) Provision(ctx caddy.Context) error {
47-
rules := make([]proxyprotocol.Rule, 0, len(pp.Allow))
48-
for _, s := range pp.Allow {
49-
_, n, err := net.ParseCIDR(s)
58+
for _, cidr := range pp.Allow {
59+
ipnet, err := netip.ParsePrefix(cidr)
5060
if err != nil {
51-
return fmt.Errorf("invalid subnet '%s': %w", s, err)
61+
return err
5262
}
53-
rules = append(rules, proxyprotocol.Rule{
54-
Timeout: time.Duration(pp.Timeout),
55-
Subnet: n,
56-
})
63+
pp.allow = append(pp.allow, ipnet)
5764
}
65+
for _, cidr := range pp.Deny {
66+
ipnet, err := netip.ParsePrefix(cidr)
67+
if err != nil {
68+
return err
69+
}
70+
pp.deny = append(pp.deny, ipnet)
71+
}
72+
pp.policy = func(upstream net.Addr) (goproxy.Policy, error) {
73+
// trust unix sockets
74+
if network := upstream.Network(); caddy.IsUnixNetwork(network) {
75+
return goproxy.USE, nil
76+
}
77+
ret := pp.FallbackPolicy
78+
host, _, err := net.SplitHostPort(upstream.String())
79+
if err != nil {
80+
return goproxy.REJECT, err
81+
}
5882

59-
pp.rules = rules
60-
83+
ip, err := netip.ParseAddr(host)
84+
if err != nil {
85+
return goproxy.REJECT, err
86+
}
87+
for _, ipnet := range pp.deny {
88+
if ipnet.Contains(ip) {
89+
return goproxy.REJECT, nil
90+
}
91+
}
92+
for _, ipnet := range pp.allow {
93+
if ipnet.Contains(ip) {
94+
ret = PolicyUSE
95+
break
96+
}
97+
}
98+
return policyToGoProxyPolicy[ret], nil
99+
}
61100
return nil
62101
}
63102

64103
// WrapListener adds PROXY protocol support to the listener.
65104
func (pp *ListenerWrapper) WrapListener(l net.Listener) net.Listener {
66-
pl := proxyprotocol.NewListener(l, time.Duration(pp.Timeout))
67-
pl.SetFilter(pp.rules)
105+
pl := &goproxy.Listener{
106+
Listener: l,
107+
ReadHeaderTimeout: time.Duration(pp.Timeout),
108+
}
109+
pl.Policy = pp.policy
68110
return pl
69111
}

modules/caddyhttp/proxyprotocol/module.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ func (ListenerWrapper) CaddyModule() caddy.ModuleInfo {
3535
// proxy_protocol {
3636
// timeout <duration>
3737
// allow <IPs...>
38+
// deny <IPs...>
39+
// fallback_policy <policy>
3840
// }
3941
func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
4042
for d.Next() {
@@ -57,7 +59,17 @@ func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
5759

5860
case "allow":
5961
w.Allow = append(w.Allow, d.RemainingArgs()...)
60-
62+
case "deny":
63+
w.Deny = append(w.Deny, d.RemainingArgs()...)
64+
case "fallback_policy":
65+
if !d.NextArg() {
66+
return d.ArgErr()
67+
}
68+
p, err := parsePolicy(d.Val())
69+
if err != nil {
70+
return d.WrapErr(err)
71+
}
72+
w.FallbackPolicy = p
6173
default:
6274
return d.ArgErr()
6375
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package proxyprotocol
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
goproxy "github.com/pires/go-proxyproto"
9+
)
10+
11+
type Policy int
12+
13+
// as defined in: https://pkg.go.dev/github.com/pires/[email protected]#Policy
14+
const (
15+
// IGNORE address from PROXY header, but accept connection
16+
PolicyIGNORE Policy = iota
17+
// USE address from PROXY header
18+
PolicyUSE
19+
// REJECT connection when PROXY header is sent
20+
// Note: even though the first read on the connection returns an error if
21+
// a PROXY header is present, subsequent reads do not. It is the task of
22+
// the code using the connection to handle that case properly.
23+
PolicyREJECT
24+
// REQUIRE connection to send PROXY header, reject if not present
25+
// Note: even though the first read on the connection returns an error if
26+
// a PROXY header is not present, subsequent reads do not. It is the task
27+
// of the code using the connection to handle that case properly.
28+
PolicyREQUIRE
29+
// SKIP accepts a connection without requiring the PROXY header
30+
// Note: an example usage can be found in the SkipProxyHeaderForCIDR
31+
// function.
32+
PolicySKIP
33+
)
34+
35+
var policyToGoProxyPolicy = map[Policy]goproxy.Policy{
36+
PolicyUSE: goproxy.USE,
37+
PolicyIGNORE: goproxy.IGNORE,
38+
PolicyREJECT: goproxy.REJECT,
39+
PolicyREQUIRE: goproxy.REQUIRE,
40+
PolicySKIP: goproxy.SKIP,
41+
}
42+
43+
var policyMap = map[Policy]string{
44+
PolicyUSE: "USE",
45+
PolicyIGNORE: "IGNORE",
46+
PolicyREJECT: "REJECT",
47+
PolicyREQUIRE: "REQUIRE",
48+
PolicySKIP: "SKIP",
49+
}
50+
51+
var policyMapRev = map[string]Policy{
52+
"USE": PolicyUSE,
53+
"IGNORE": PolicyIGNORE,
54+
"REJECT": PolicyREJECT,
55+
"REQUIRE": PolicyREQUIRE,
56+
"SKIP": PolicySKIP,
57+
}
58+
59+
// MarshalText implements the text marshaller method.
60+
func (x Policy) MarshalText() ([]byte, error) {
61+
return []byte(policyMap[x]), nil
62+
}
63+
64+
// UnmarshalText implements the text unmarshaller method.
65+
func (x *Policy) UnmarshalText(text []byte) error {
66+
name := string(text)
67+
tmp, err := parsePolicy(name)
68+
if err != nil {
69+
return err
70+
}
71+
*x = tmp
72+
return nil
73+
}
74+
75+
func parsePolicy(name string) (Policy, error) {
76+
if x, ok := policyMapRev[strings.ToUpper(name)]; ok {
77+
return x, nil
78+
}
79+
return Policy(0), fmt.Errorf("%s is %w", name, errInvalidPolicy)
80+
}
81+
82+
var errInvalidPolicy = errors.New("invalid policy")

modules/caddyhttp/reverseproxy/httptransport.go

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
"strings"
2929
"time"
3030

31-
"github.com/mastercactapus/proxyprotocol"
31+
"github.com/pires/go-proxyproto"
3232
"go.uber.org/zap"
3333
"golang.org/x/net/http2"
3434

@@ -207,44 +207,42 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
207207
if !ok {
208208
return nil, fmt.Errorf("failed to get proxy protocol info from context")
209209
}
210-
211-
// The src and dst have to be of the some address family. As we don't know the original
212-
// dst address (it's kind of impossible to know) and this address is generelly of very
210+
header := proxyproto.Header{
211+
SourceAddr: &net.TCPAddr{
212+
IP: proxyProtocolInfo.AddrPort.Addr().AsSlice(),
213+
Port: int(proxyProtocolInfo.AddrPort.Port()),
214+
Zone: proxyProtocolInfo.AddrPort.Addr().Zone(),
215+
},
216+
}
217+
// The src and dst have to be of the same address family. As we don't know the original
218+
// dst address (it's kind of impossible to know) and this address is generally of very
213219
// little interest, we just set it to all zeros.
214-
var destIP net.IP
215220
switch {
216221
case proxyProtocolInfo.AddrPort.Addr().Is4():
217-
destIP = net.IPv4zero
222+
header.TransportProtocol = proxyproto.TCPv4
223+
header.DestinationAddr = &net.TCPAddr{
224+
IP: net.IPv4zero,
225+
}
218226
case proxyProtocolInfo.AddrPort.Addr().Is6():
219-
destIP = net.IPv6zero
227+
header.TransportProtocol = proxyproto.TCPv6
228+
header.DestinationAddr = &net.TCPAddr{
229+
IP: net.IPv6zero,
230+
}
220231
default:
221232
return nil, fmt.Errorf("unexpected remote addr type in proxy protocol info")
222233
}
223234

224-
// TODO: We should probably migrate away from net.IP to use netip.Addr,
225-
// but due to the upstream dependency, we can't do that yet.
226235
switch h.ProxyProtocol {
227236
case "v1":
228-
header := proxyprotocol.HeaderV1{
229-
SrcIP: net.IP(proxyProtocolInfo.AddrPort.Addr().AsSlice()),
230-
SrcPort: int(proxyProtocolInfo.AddrPort.Port()),
231-
DestIP: destIP,
232-
DestPort: 0,
233-
}
237+
header.Version = 1
234238
caddyCtx.Logger().Debug("sending proxy protocol header v1", zap.Any("header", header))
235-
_, err = header.WriteTo(conn)
236239
case "v2":
237-
header := proxyprotocol.HeaderV2{
238-
Command: proxyprotocol.CmdProxy,
239-
Src: &net.TCPAddr{IP: net.IP(proxyProtocolInfo.AddrPort.Addr().AsSlice()), Port: int(proxyProtocolInfo.AddrPort.Port())},
240-
Dest: &net.TCPAddr{IP: destIP, Port: 0},
241-
}
240+
header.Version = 2
242241
caddyCtx.Logger().Debug("sending proxy protocol header v2", zap.Any("header", header))
243-
_, err = header.WriteTo(conn)
244242
default:
245243
return nil, fmt.Errorf("unexpected proxy protocol version")
246244
}
247-
245+
_, err = header.WriteTo(conn)
248246
if err != nil {
249247
// identify this error as one that occurred during
250248
// dialing, which can be important when trying to

0 commit comments

Comments
 (0)