[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
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
d85dff81a8
Коммит
7ac5715a02
@@ -21,6 +21,8 @@ const (
|
|||||||
|
|
||||||
var reservedIPRanges []*net.IPNet
|
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 {
|
func IsReservedIP(ip net.IP) bool {
|
||||||
for _, ipRange := range reservedIPRanges {
|
for _, ipRange := range reservedIPRanges {
|
||||||
if ipRange.Contains(ip) {
|
if ipRange.Contains(ip) {
|
||||||
@@ -30,6 +32,38 @@ func IsReservedIP(ip net.IP) bool {
|
|||||||
return false
|
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
|
var defaultUserAgent string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -145,3 +145,44 @@ func NewHTTPClient(transport http.RoundTripper) *http.Client {
|
|||||||
Transport: transport,
|
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 {
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.configService.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil {
|
if h.configService.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil {
|
||||||
return false
|
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) {
|
for _, allowed := range strings.Fields(*h.configService.Config().ServiceSettings.AllowedUntrustedInternalConnections) {
|
||||||
if _, ipRange, err := net.ParseCIDR(allowed); err == nil && ipRange.Contains(ip) {
|
if _, ipRange, err := net.ParseCIDR(allowed); err == nil && ipRange.Contains(ip) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user