[MM-14333] Stricten external HTTP Calls to require that own IPs need to be explicitly whitelisted (#10375)

* Stricten external HTTP Calls to require that own IPs need to be explicitly whitelisted

* gofmt

* Documentation; Style fixes for IsOwnIP function
This commit is contained in:
Daniel Schalla
2019-03-01 16:22:24 +01:00
committed by GitHub
parent d85dff81a8
commit 7ac5715a02
3 changed files with 88 additions and 1 deletions

View File

@@ -21,6 +21,8 @@ const (
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) {
@@ -30,6 +32,38 @@ func IsReservedIP(ip net.IP) bool {
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() {

View File

@@ -145,3 +145,44 @@ func NewHTTPClient(transport http.RoundTripper) *http.Client {
Transport: transport,
}
}
func TestIsReservedIP(t *testing.T) {
tests := []struct {
name string
ip net.IP
want bool
}{
{"127.8.3.5", net.IPv4(127, 8, 3, 5), true},
{"192.168.0.1", net.IPv4(192, 168, 0, 1), true},
{"169.254.0.6", net.IPv4(169, 254, 0, 6), true},
{"127.120.6.3", net.IPv4(127, 120, 6, 3), true},
{"8.8.8.8", net.IPv4(8, 8, 8, 8), false},
{"9.9.9.9", net.IPv4(9, 9, 9, 8), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsReservedIP(tt.ip); got != tt.want {
t.Errorf("IsReservedIP() = %v, want %v", got, tt.want)
}
})
}
}
func TestIsOwnIP(t *testing.T) {
tests := []struct {
name string
ip net.IP
want bool
}{
{"127.0.0.1", net.IPv4(127, 0, 0, 1), true},
{"8.8.8.8", net.IPv4(8, 0, 0, 8), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got, _ := IsOwnIP(tt.ip); got != tt.want {
t.Errorf("IsOwnIP() = %v, want %v", got, tt.want)
t.Errorf(tt.ip.String());
}
})
}
}

View File

@@ -67,12 +67,24 @@ func (h *HTTPServiceImpl) MakeTransport(trustURLs bool) http.RoundTripper {
}
allowIP := func(ip net.IP) bool {
if !IsReservedIP(ip) {
reservedIP := IsReservedIP(ip)
ownIP, err := IsOwnIP(ip)
// If there is an error getting the self-assigned IPs, default to the secure option
if err != nil {
return false
}
// If it's not a reserved IP and it's not self-assigned IP, accept the IP
if !reservedIP && !ownIP {
return true
}
if h.configService.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil {
return false
}
// In the case it's the self-assigned IP, enforce that it needs to be explicitly added to the AllowedUntrustedInternalConnections
for _, allowed := range strings.Fields(*h.configService.Config().ServiceSettings.AllowedUntrustedInternalConnections) {
if _, ipRange, err := net.ParseCIDR(allowed); err == nil && ipRange.Contains(ip) {
return true