mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
* Consistent license message for all the go files * Fixing the last set of unconsistencies with the license headers * Addressing PR review comments * Fixing busy.go and busy_test.go license header
162 lines
3.9 KiB
Go
162 lines
3.9 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package httpservice
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
)
|
|
|
|
const (
|
|
ConnectTimeout = 3 * time.Second
|
|
RequestTimeout = 30 * time.Second
|
|
)
|
|
|
|
var reservedIPRanges []*net.IPNet
|
|
|
|
// IsReservedIP checks whether the target IP belongs to reserved IP address ranges to avoid SSRF attacks to the internal
|
|
// network of the Mattermost server
|
|
func IsReservedIP(ip net.IP) bool {
|
|
for _, ipRange := range reservedIPRanges {
|
|
if ipRange.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsOwnIP handles the special case that a request might be made to the public IP of the host which on Linux is routed
|
|
// directly via the loopback IP to any listening sockets, effectively bypassing host-based firewalls such as firewalld
|
|
func IsOwnIP(ip net.IP) (bool, error) {
|
|
interfaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for _, interf := range interfaces {
|
|
addresses, err := interf.Addrs()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for _, addr := range addresses {
|
|
var selfIP net.IP
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
selfIP = v.IP
|
|
case *net.IPAddr:
|
|
selfIP = v.IP
|
|
}
|
|
|
|
if ip.Equal(selfIP) {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
var defaultUserAgent string
|
|
|
|
func init() {
|
|
for _, cidr := range []string{
|
|
// See https://tools.ietf.org/html/rfc6890
|
|
"0.0.0.0/8", // This host on this network
|
|
"10.0.0.0/8", // Private-Use
|
|
"127.0.0.0/8", // Loopback
|
|
"169.254.0.0/16", // Link Local
|
|
"172.16.0.0/12", // Private-Use Networks
|
|
"192.168.0.0/16", // Private-Use Networks
|
|
"::/128", // Unspecified Address
|
|
"::1/128", // Loopback Address
|
|
"fc00::/7", // Unique-Local
|
|
"fe80::/10", // Linked-Scoped Unicast
|
|
} {
|
|
_, parsed, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
reservedIPRanges = append(reservedIPRanges, parsed)
|
|
}
|
|
defaultUserAgent = "mattermost-" + model.CurrentVersion
|
|
}
|
|
|
|
type DialContextFunction func(ctx context.Context, network, addr string) (net.Conn, error)
|
|
|
|
var AddressForbidden error = errors.New("address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network")
|
|
|
|
func dialContextFilter(dial DialContextFunction, allowHost func(host string) bool, allowIP func(ip net.IP) bool) DialContextFunction {
|
|
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
host, port, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if allowHost != nil && allowHost(host) {
|
|
return dial(ctx, network, addr)
|
|
}
|
|
|
|
ips, err := net.LookupIP(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var firstErr error
|
|
for _, ip := range ips {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
if allowIP == nil || !allowIP(ip) {
|
|
continue
|
|
}
|
|
|
|
conn, err := dial(ctx, network, net.JoinHostPort(ip.String(), port))
|
|
if err == nil {
|
|
return conn, nil
|
|
}
|
|
if firstErr == nil {
|
|
firstErr = err
|
|
}
|
|
}
|
|
if firstErr == nil {
|
|
return nil, AddressForbidden
|
|
}
|
|
return nil, firstErr
|
|
}
|
|
}
|
|
|
|
func NewTransport(enableInsecureConnections bool, allowHost func(host string) bool, allowIP func(ip net.IP) bool) http.RoundTripper {
|
|
dialContext := (&net.Dialer{
|
|
Timeout: ConnectTimeout,
|
|
KeepAlive: 30 * time.Second,
|
|
}).DialContext
|
|
|
|
if allowHost != nil || allowIP != nil {
|
|
dialContext = dialContextFilter(dialContext, allowHost, allowIP)
|
|
}
|
|
|
|
return &MattermostTransport{
|
|
&http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: dialContext,
|
|
MaxIdleConns: 100,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
TLSHandshakeTimeout: ConnectTimeout,
|
|
ExpectContinueTimeout: 1 * time.Second,
|
|
TLSClientConfig: &tls.Config{
|
|
InsecureSkipVerify: enableInsecureConnections,
|
|
},
|
|
},
|
|
}
|
|
}
|