gitlab.com/gitlab-org/labkit@v1.21.0/correlation/inbound_http_options.go (about)

     1  package correlation
     2  
     3  import (
     4  	"net"
     5  	"net/http"
     6  
     7  	"github.com/sebest/xff"
     8  	"github.com/sirupsen/logrus"
     9  )
    10  
    11  // XFFAllowedFunc decides whether X-Forwarded-For headers are to be trusted.
    12  type XFFAllowedFunc func(ip string) bool
    13  
    14  // The configuration for InjectCorrelationID.
    15  type inboundHandlerConfig struct {
    16  	propagation                  bool
    17  	sendResponseHeader           bool
    18  	invalidCIDRsForPropagation   bool
    19  	trustedCIDRsForPropagation   []net.IPNet
    20  	trustedCIDRsForXForwardedFor []net.IPNet
    21  	xffAllowed                   XFFAllowedFunc
    22  }
    23  
    24  // InboundHandlerOption will configure a correlation handler
    25  // currently there are no options, but this gives us the option
    26  // to extend the interface in a backwards compatible way.
    27  type InboundHandlerOption func(*inboundHandlerConfig)
    28  
    29  func applyInboundHandlerOptions(opts []InboundHandlerOption) inboundHandlerConfig {
    30  	config := inboundHandlerConfig{
    31  		propagation: false,
    32  	}
    33  	for _, v := range opts {
    34  		v(&config)
    35  	}
    36  
    37  	return config
    38  }
    39  
    40  // WithPropagation will configure the handler to propagate existing correlation_ids
    41  // passed in from upstream services.
    42  // This is not the default behaviour.
    43  func WithPropagation() InboundHandlerOption {
    44  	return func(config *inboundHandlerConfig) {
    45  		config.propagation = true
    46  	}
    47  }
    48  
    49  // WithSetResponseHeader will configure the handler to set the correlation_id
    50  // in the http response headers.
    51  func WithSetResponseHeader() InboundHandlerOption {
    52  	return func(config *inboundHandlerConfig) {
    53  		config.sendResponseHeader = true
    54  	}
    55  }
    56  
    57  // WithCIDRsTrustedForPropagation will configure the handler to set a list of trusted
    58  // CIDR blocks to allow propagation of correlation_ids.
    59  func WithCIDRsTrustedForPropagation(trustedCIDRs []string) InboundHandlerOption {
    60  	return func(config *inboundHandlerConfig) {
    61  		for _, s := range trustedCIDRs {
    62  			_, ipNet, err := net.ParseCIDR(s)
    63  			if err != nil {
    64  				logrus.Errorf("Bad trusted CIDR for propagation %s: %v, propagation disabled", s, err)
    65  				config.invalidCIDRsForPropagation = true
    66  			} else {
    67  				config.trustedCIDRsForPropagation = append(config.trustedCIDRsForPropagation, *ipNet)
    68  			}
    69  		}
    70  	}
    71  }
    72  
    73  // WithCIDRsTrustedForXForwardedFor will configure the handler to trust
    74  // X-Forwarded-For from trusted CIDR blocks.
    75  func WithCIDRsTrustedForXForwardedFor(trustedCIDRs []string) InboundHandlerOption {
    76  	return func(config *inboundHandlerConfig) {
    77  		for _, s := range trustedCIDRs {
    78  			_, ipNet, err := net.ParseCIDR(s)
    79  			if err != nil {
    80  				logrus.Errorf("Bad trusted CIDR for XForwardedFor %s: %v", s, err)
    81  			} else {
    82  				config.trustedCIDRsForXForwardedFor = append(config.trustedCIDRsForXForwardedFor, *ipNet)
    83  			}
    84  		}
    85  
    86  		config.xffAllowed = func(ip string) bool {
    87  			return isTrustedIP(ip, config.trustedCIDRsForXForwardedFor)
    88  		}
    89  	}
    90  }
    91  
    92  func isTrustedIP(ipAddress string, trustedCIDRs []net.IPNet) bool {
    93  	ip := net.ParseIP(ipAddress)
    94  
    95  	for _, cidr := range trustedCIDRs {
    96  		if cidr.Contains(ip) {
    97  			return true
    98  		}
    99  	}
   100  
   101  	return false
   102  }
   103  
   104  func isTrustedAddr(addr string, trustedCIDRs []net.IPNet) bool {
   105  	host, _, err := net.SplitHostPort(addr)
   106  	if err != nil {
   107  		return false
   108  	}
   109  
   110  	return isTrustedIP(host, trustedCIDRs)
   111  }
   112  
   113  func (c *inboundHandlerConfig) shouldPropagate(r *http.Request) bool {
   114  	if !c.propagation || c.invalidCIDRsForPropagation {
   115  		return false
   116  	}
   117  
   118  	if len(c.trustedCIDRsForPropagation) == 0 {
   119  		return true
   120  	}
   121  
   122  	remoteAddr := r.RemoteAddr
   123  
   124  	if c.xffAllowed != nil {
   125  		// Unix domain sockets have a remote addr of @. This will make the
   126  		// xff package lookup the X-Forwarded-For address if available.
   127  		if remoteAddr == "@" {
   128  			r.RemoteAddr = "127.0.0.1:0"
   129  		}
   130  
   131  		remoteAddr = xff.GetRemoteAddrIfAllowed(r, c.xffAllowed)
   132  
   133  		// If X-Forwarded-For was allowed and returned a different address, check
   134  		// the original address against our trusted CIDRs for propagation, in case
   135  		// the reverse proxy is trusted
   136  		if remoteAddr != r.RemoteAddr && isTrustedAddr(r.RemoteAddr, c.trustedCIDRsForPropagation) {
   137  			return true
   138  		}
   139  	}
   140  
   141  	return isTrustedAddr(remoteAddr, c.trustedCIDRsForPropagation)
   142  }