@marcel91200 / caddy Mirror · read-only / commits / dc12bd97
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 19:30 UTC

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 <lavofr@gmail.com> * add config options for `Deny` CIDR and fallback policy * use `netip` package & trust unix sockets --------- Co-authored-by: Francis Lavoie <lavofr@gmail.com>

Mohammed Al Sahaf committed Dec 13, 2023 at 19:07 UTC dc12bd974367b1c15e6b8f3c6b446a208ac3546f
6 files changed +176 -42
go.mod
+1 -1
@@ -14,7 +14,6 @@ require (
14 github.com/google/uuid v1.3.1
15 github.com/klauspost/compress v1.17.0
16 github.com/klauspost/cpuid/v2 v2.2.5
17 - github.com/mastercactapus/proxyprotocol v0.0.4
17 github.com/mholt/acmez v1.2.0
18 github.com/prometheus/client_golang v1.15.1
19 github.com/quic-go/quic-go v0.40.0
@@ -117,6 +116,7 @@ require (
116 github.com/mitchellh/copystructure v1.2.0 // indirect
117 github.com/mitchellh/go-ps v1.0.0 // indirect
118 github.com/mitchellh/reflectwalk v1.0.2 // indirect
119 + github.com/pires/go-proxyproto v0.7.0
120 github.com/pkg/errors v0.9.1 // indirect
121 github.com/prometheus/client_model v0.4.0 // indirect
122 github.com/prometheus/common v0.42.0 // indirect
go.sum
+2 -2
@@ -352,8 +352,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
352 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
353 github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
354 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=
355 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
356 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
357 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
431 github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
432 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
433 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=
436 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
437 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
438 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
modules/caddyhttp/proxyprotocol/listenerwrapper.go
+57 -15
@@ -15,11 +15,11 @@
15 package proxyprotocol
16
17 import (
18 - "fmt"
18 "net"
19 + "net/netip"
20 "time"
21
22 - "github.com/mastercactapus/proxyprotocol"
22 + goproxy "github.com/pires/go-proxyproto"
23
24 "github.com/caddyserver/caddy/v2"
25 )
@@ -38,32 +38,74 @@ type ListenerWrapper struct {
38 // Allow is an optional list of CIDR ranges to
39 // allow/require PROXY headers from.
40 Allow []string `json:"allow,omitempty"`
41 + allow []netip.Prefix
42
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/go-proxyproto@v0.7.0#Policy
51 + FallbackPolicy Policy `json:"fallback_policy,omitempty"`
52 +
53 + policy goproxy.PolicyFunc
54 }
55
56 // Provision sets up the listener wrapper.
57 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)
60 if err != nil {
51 - return fmt.Errorf("invalid subnet '%s': %w", s, err)
61 + return err
62 }
53 - rules = append(rules, proxyprotocol.Rule{
54 - Timeout: time.Duration(pp.Timeout),
55 - Subnet: n,
56 - })
63 + pp.allow = append(pp.allow, ipnet)
64 }
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 + }
82
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 + }
100 return nil
101 }
102
103 // WrapListener adds PROXY protocol support to the listener.
104 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
110 return pl
111 }
modules/caddyhttp/proxyprotocol/module.go
+13 -1
@@ -35,6 +35,8 @@ func (ListenerWrapper) CaddyModule() caddy.ModuleInfo {
35 // proxy_protocol {
36 // timeout <duration>
37 // allow <IPs...>
38 +// deny <IPs...>
39 +// fallback_policy <policy>
40 // }
41 func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
42 for d.Next() {
@@ -57,7 +59,17 @@ func (w *ListenerWrapper) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
59
60 case "allow":
61 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
73 default:
74 return d.ArgErr()
75 }
modules/caddyhttp/proxyprotocol/policy.go new
+82
@@ -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/go-proxyproto@v0.7.0#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
+21 -23
@@ -28,7 +28,7 @@ import (
28 "strings"
29 "time"
30
31 - "github.com/mastercactapus/proxyprotocol"
31 + "github.com/pires/go-proxyproto"
32 "go.uber.org/zap"
33 "golang.org/x/net/http2"
34
@@ -207,44 +207,42 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
207 if !ok {
208 return nil, fmt.Errorf("failed to get proxy protocol info from context")
209 }
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
219 // little interest, we just set it to all zeros.
214 - var destIP net.IP
220 switch {
221 case proxyProtocolInfo.AddrPort.Addr().Is4():
217 - destIP = net.IPv4zero
222 + header.TransportProtocol = proxyproto.TCPv4
223 + header.DestinationAddr = &net.TCPAddr{
224 + IP: net.IPv4zero,
225 + }
226 case proxyProtocolInfo.AddrPort.Addr().Is6():
219 - destIP = net.IPv6zero
227 + header.TransportProtocol = proxyproto.TCPv6
228 + header.DestinationAddr = &net.TCPAddr{
229 + IP: net.IPv6zero,
230 + }
231 default:
232 return nil, fmt.Errorf("unexpected remote addr type in proxy protocol info")
233 }
234
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.
235 switch h.ProxyProtocol {
236 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
238 caddyCtx.Logger().Debug("sending proxy protocol header v1", zap.Any("header", header))
235 - _, err = header.WriteTo(conn)
239 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
241 caddyCtx.Logger().Debug("sending proxy protocol header v2", zap.Any("header", header))
243 - _, err = header.WriteTo(conn)
242 default:
243 return nil, fmt.Errorf("unexpected proxy protocol version")
244 }
247 -
245 + _, err = header.WriteTo(conn)
246 if err != nil {
247 // identify this error as one that occurred during
248 // dialing, which can be important when trying to