correlation/inbound_http_options.go (99 lines of code) (raw):
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)
}