mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[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:
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user