fix: correctly check denied domains and ips for actions (#8810)

# Which Problems Are Solved

System administrators can block hosts and IPs for HTTP calls in actions.
Using DNS, blocked IPs could be bypassed.

# How the Problems Are Solved

- Hosts are resolved (DNS lookup) to check whether their corresponding
IP is blocked.

# Additional Changes

- Added complete lookup ip address range and "unspecified" address to
the default `DenyList`
This commit is contained in:
Livio Spring
2024-10-22 16:16:44 +02:00
committed by GitHub
parent fca6b28a97
commit 79fb4cc1cc
5 changed files with 95 additions and 60 deletions

View File

@@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"io"
"net"
"net/http"
"net/url"
"strings"
@@ -19,7 +20,7 @@ import (
func WithHTTP(ctx context.Context) Option {
return func(c *runConfig) {
c.modules["zitadel/http"] = func(runtime *goja.Runtime, module *goja.Object) {
requireHTTP(ctx, &http.Client{Transport: new(transport)}, runtime, module)
requireHTTP(ctx, &http.Client{Transport: &transport{lookup: net.LookupIP}}, runtime, module)
}
}
}
@@ -170,21 +171,34 @@ func parseHeaders(headers *goja.Object) http.Header {
return h
}
type transport struct{}
type transport struct {
lookup func(string) ([]net.IP, error)
}
func (*transport) RoundTrip(req *http.Request) (*http.Response, error) {
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
if httpConfig == nil {
return http.DefaultTransport.RoundTrip(req)
}
if isHostBlocked(httpConfig.DenyList, req.URL) {
if t.isHostBlocked(httpConfig.DenyList, req.URL) {
return nil, zerrors.ThrowInvalidArgument(nil, "ACTIO-N72d0", "host is denied")
}
return http.DefaultTransport.RoundTrip(req)
}
func isHostBlocked(denyList []AddressChecker, address *url.URL) bool {
func (t *transport) isHostBlocked(denyList []AddressChecker, address *url.URL) bool {
host := address.Hostname()
ip := net.ParseIP(host)
ips := []net.IP{ip}
// if the hostname is a domain, we need to check resolve the ip(s), since it might be denied
if ip == nil {
var err error
ips, err = t.lookup(host)
if err != nil {
return true
}
}
for _, blocked := range denyList {
if blocked.Matches(address.Hostname()) {
if blocked.Matches(ips, host) {
return true
}
}
@@ -192,5 +206,5 @@ func isHostBlocked(denyList []AddressChecker, address *url.URL) bool {
}
type AddressChecker interface {
Matches(string) bool
Matches([]net.IP, string) bool
}