github.com/cryptomisa/mattermost-server@v5.11.1+incompatible/services/httpservice/client.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package httpservice
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"errors"
    10  	"net"
    11  	"net/http"
    12  	"time"
    13  
    14  	"github.com/mattermost/mattermost-server/model"
    15  )
    16  
    17  const (
    18  	ConnectTimeout = 3 * time.Second
    19  	RequestTimeout = 30 * time.Second
    20  )
    21  
    22  var reservedIPRanges []*net.IPNet
    23  
    24  // IsReservedIP checks whether the target IP belongs to reserved IP address ranges to avoid SSRF attacks to the internal
    25  // network of the Mattermost server
    26  func IsReservedIP(ip net.IP) bool {
    27  	for _, ipRange := range reservedIPRanges {
    28  		if ipRange.Contains(ip) {
    29  			return true
    30  		}
    31  	}
    32  	return false
    33  }
    34  
    35  // IsOwnIP handles the special case that a request might be made to the public IP of the host which on Linux is routed
    36  // directly via the loopback IP to any listening sockets, effectively bypassing host-based firewalls such as firewalld
    37  func IsOwnIP(ip net.IP) (bool, error) {
    38  	interfaces, err := net.Interfaces()
    39  	if err != nil {
    40  		return false, err
    41  	}
    42  
    43  	for _, interf := range interfaces {
    44  		addresses, err := interf.Addrs()
    45  		if err != nil {
    46  			return false, err
    47  		}
    48  
    49  		for _, addr := range addresses {
    50  			var selfIP net.IP
    51  			switch v := addr.(type) {
    52  			case *net.IPNet:
    53  				selfIP = v.IP
    54  			case *net.IPAddr:
    55  				selfIP = v.IP
    56  			}
    57  
    58  			if ip.Equal(selfIP) {
    59  				return true, nil
    60  			}
    61  		}
    62  	}
    63  
    64  	return false, nil
    65  }
    66  
    67  var defaultUserAgent string
    68  
    69  func init() {
    70  	for _, cidr := range []string{
    71  		// See https://tools.ietf.org/html/rfc6890
    72  		"0.0.0.0/8",      // This host on this network
    73  		"10.0.0.0/8",     // Private-Use
    74  		"127.0.0.0/8",    // Loopback
    75  		"169.254.0.0/16", // Link Local
    76  		"172.16.0.0/12",  // Private-Use Networks
    77  		"192.168.0.0/16", // Private-Use Networks
    78  		"::/128",         // Unspecified Address
    79  		"::1/128",        // Loopback Address
    80  		"fc00::/7",       // Unique-Local
    81  		"fe80::/10",      // Linked-Scoped Unicast
    82  	} {
    83  		_, parsed, err := net.ParseCIDR(cidr)
    84  		if err != nil {
    85  			panic(err)
    86  		}
    87  		reservedIPRanges = append(reservedIPRanges, parsed)
    88  	}
    89  	defaultUserAgent = "mattermost-" + model.CurrentVersion
    90  }
    91  
    92  type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error)
    93  
    94  var AddressForbidden error = errors.New("address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network")
    95  
    96  func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction {
    97  	return func(ctx context.Context, network, addr string) (net.Conn, error) {
    98  		host, port, err := net.SplitHostPort(addr)
    99  		if err != nil {
   100  			return nil, err
   101  		}
   102  
   103  		if allowHost != nil && allowHost(host) {
   104  			return dial(ctx, network, addr)
   105  		}
   106  
   107  		ips, err := net.LookupIP(host)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  
   112  		var firstErr error
   113  		for _, ip := range ips {
   114  			select {
   115  			case <-ctx.Done():
   116  				return nil, ctx.Err()
   117  			default:
   118  			}
   119  
   120  			if allowIP == nil || !allowIP(ip) {
   121  				continue
   122  			}
   123  
   124  			conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port))
   125  			if err == nil {
   126  				return conn, nil
   127  			}
   128  			if firstErr == nil {
   129  				firstErr = err
   130  			}
   131  		}
   132  		if firstErr == nil {
   133  			return nil, AddressForbidden
   134  		}
   135  		return nil, firstErr
   136  	}
   137  }
   138  
   139  func NewTransport(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) http.RoundTripper {
   140  	dialContext := (&net.Dialer{
   141  		Timeout:   ConnectTimeout,
   142  		KeepAlive: 30 * time.Second,
   143  	}).DialContext
   144  
   145  	if allowHost != nil || allowIP != nil {
   146  		dialContext = dialContextFilter(dialContext, allowHost, allowIP)
   147  	}
   148  
   149  	return &MattermostTransport{
   150  		&http.Transport{
   151  			Proxy:                 http.ProxyFromEnvironment,
   152  			DialContext:           dialContext,
   153  			MaxIdleConns:          100,
   154  			IdleConnTimeout:       90 * time.Second,
   155  			TLSHandshakeTimeout:   ConnectTimeout,
   156  			ExpectContinueTimeout: 1 * time.Second,
   157  			TLSClientConfig: &tls.Config{
   158  				InsecureSkipVerify: enableInsecureConnections,
   159  			},
   160  		},
   161  	}
   162  }