File: inbound_http_options.go

package info (click to toggle)
golang-gitlab-gitlab-org-labkit 1.17.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,092 kB
  • sloc: sh: 210; javascript: 49; makefile: 4
file content (142 lines) | stat: -rw-r--r-- 4,049 bytes parent folder | download | duplicates (3)
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
package correlation

import (
	"net"
	"net/http"

	"github.com/sebest/xff"
	"github.com/sirupsen/logrus"
)

// XFFAllowedFunc decides whether X-Forwarded-For headers are to be trusted.
type XFFAllowedFunc func(ip string) bool

// The configuration for InjectCorrelationID.
type inboundHandlerConfig struct {
	propagation                  bool
	sendResponseHeader           bool
	invalidCIDRsForPropagation   bool
	trustedCIDRsForPropagation   []net.IPNet
	trustedCIDRsForXForwardedFor []net.IPNet
	xffAllowed                   XFFAllowedFunc
}

// InboundHandlerOption will configure a correlation handler
// currently there are no options, but this gives us the option
// to extend the interface in a backwards compatible way.
type InboundHandlerOption func(*inboundHandlerConfig)

func applyInboundHandlerOptions(opts []InboundHandlerOption) inboundHandlerConfig {
	config := inboundHandlerConfig{
		propagation: false,
	}
	for _, v := range opts {
		v(&config)
	}

	return config
}

// WithPropagation will configure the handler to propagate existing correlation_ids
// passed in from upstream services.
// This is not the default behaviour.
func WithPropagation() InboundHandlerOption {
	return func(config *inboundHandlerConfig) {
		config.propagation = true
	}
}

// WithSetResponseHeader will configure the handler to set the correlation_id
// in the http response headers.
func WithSetResponseHeader() InboundHandlerOption {
	return func(config *inboundHandlerConfig) {
		config.sendResponseHeader = true
	}
}

// WithCIDRsTrustedForPropagation will configure the handler to set a list of trusted
// CIDR blocks to allow propagation of correlation_ids.
func WithCIDRsTrustedForPropagation(trustedCIDRs []string) InboundHandlerOption {
	return func(config *inboundHandlerConfig) {
		for _, s := range trustedCIDRs {
			_, ipNet, err := net.ParseCIDR(s)
			if err != nil {
				logrus.Errorf("Bad trusted CIDR for propagation %s: %v, propagation disabled", s, err)
				config.invalidCIDRsForPropagation = true
			} else {
				config.trustedCIDRsForPropagation = append(config.trustedCIDRsForPropagation, *ipNet)
			}
		}
	}
}

// WithCIDRsTrustedForXForwardedFor will configure the handler to trust
// X-Forwarded-For from trusted CIDR blocks.
func WithCIDRsTrustedForXForwardedFor(trustedCIDRs []string) InboundHandlerOption {
	return func(config *inboundHandlerConfig) {
		for _, s := range trustedCIDRs {
			_, ipNet, err := net.ParseCIDR(s)
			if err != nil {
				logrus.Errorf("Bad trusted CIDR for XForwardedFor %s: %v", s, err)
			} else {
				config.trustedCIDRsForXForwardedFor = append(config.trustedCIDRsForXForwardedFor, *ipNet)
			}
		}

		config.xffAllowed = func(ip string) bool {
			return isTrustedIP(ip, config.trustedCIDRsForXForwardedFor)
		}
	}
}

func isTrustedIP(ipAddress string, trustedCIDRs []net.IPNet) bool {
	ip := net.ParseIP(ipAddress)

	for _, cidr := range trustedCIDRs {
		if cidr.Contains(ip) {
			return true
		}
	}

	return false
}

func isTrustedAddr(addr string, trustedCIDRs []net.IPNet) bool {
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return false
	}

	return isTrustedIP(host, trustedCIDRs)
}

func (c *inboundHandlerConfig) shouldPropagate(r *http.Request) bool {
	if !c.propagation || c.invalidCIDRsForPropagation {
		return false
	}

	if len(c.trustedCIDRsForPropagation) == 0 {
		return true
	}

	remoteAddr := r.RemoteAddr

	if c.xffAllowed != nil {
		// Unix domain sockets have a remote addr of @. This will make the
		// xff package lookup the X-Forwarded-For address if available.
		if remoteAddr == "@" {
			r.RemoteAddr = "127.0.0.1:0"
		}

		remoteAddr = xff.GetRemoteAddrIfAllowed(r, c.xffAllowed)

		// If X-Forwarded-For was allowed and returned a different address, check
		// the original address against our trusted CIDRs for propagation, in case
		// the reverse proxy is trusted
		if remoteAddr != r.RemoteAddr && isTrustedAddr(r.RemoteAddr, c.trustedCIDRsForPropagation) {
			return true
		}
	}

	return isTrustedAddr(remoteAddr, c.trustedCIDRsForPropagation)
}