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)
}
|