mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
* Add DenyLists parsing * Remove unneeded returned error * Plug global denylist into Command * app creation: apply denylist to backchannel logout URI * Inject denylist to backchannel logout worker * webhook config: validate against blocked URLs * Add notificationsWebhook denylist target * command: Add SMTP endpoint validation against blocklist * command: Add SMS endpoint validation against blocklist * Validate webhook endpoint against denylist on channel notification * Remove unused tests * handle deprecated denylists * remove unintended denylist entry in deprecated list * use single http client * fix tests * update comments * fixes * cleanup * address comments * fix merge --------- Co-authored-by: Livio Spring <[email protected]>
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package denylist
|
|
|
|
import (
|
|
"net"
|
|
"net/url"
|
|
"strings"
|
|
|
|
internal_net "github.com/zitadel/zitadel/internal/net"
|
|
)
|
|
|
|
var _ AddressChecker = (*HostChecker)(nil)
|
|
|
|
type HostChecker struct {
|
|
Net *net.IPNet
|
|
IP net.IP
|
|
Domain string
|
|
}
|
|
|
|
// NewHostChecker returns an [AddressChecker].
|
|
//
|
|
// If the input string is neither a network, nor an IP address, it is considered a domain and saved as such
|
|
func NewHostChecker(entry string) AddressChecker {
|
|
if entry == "" {
|
|
return nil
|
|
}
|
|
_, network, err := net.ParseCIDR(entry)
|
|
if err == nil {
|
|
return &HostChecker{Net: network}
|
|
}
|
|
if ip := net.ParseIP(entry); ip != nil {
|
|
return &HostChecker{IP: ip}
|
|
}
|
|
return &HostChecker{Domain: entry}
|
|
}
|
|
|
|
// IsDenied checks if the address or one of the IPs is blocked by the [HostChecker].
|
|
// The address is checked using string comparison.
|
|
// If either is blocked, an [AddressDeniedError] will be returned.
|
|
// If either is not blocked, it will return nil.
|
|
func (c *HostChecker) IsDenied(ips []net.IP, address string) error {
|
|
// if the address matches the domain, no additional checks as needed
|
|
if c.Domain != "" && strings.EqualFold(c.Domain, address) {
|
|
return NewAddressDeniedError(c.Domain)
|
|
}
|
|
// otherwise we need to check on ips (incl. the resolved ips of the host)
|
|
for _, ip := range ips {
|
|
if c.Net != nil && c.Net.Contains(ip) {
|
|
return NewAddressDeniedError(c.Net.String())
|
|
}
|
|
if c.IP != nil && c.IP.Equal(ip) {
|
|
return NewAddressDeniedError(c.IP.String())
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsHostBlocked checks address against denyList. If a match is found, an [AddressDeniedError] will be returned.
|
|
// If no match is found, it will return nil.
|
|
func IsHostBlocked(denyList []AddressChecker, host string, ips ...net.IP) error {
|
|
for _, denied := range denyList {
|
|
if err := denied.IsDenied(ips, host); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsHostnameBlocked checks a hostname against the denyList. If a match is found, an [AddressDeniedError] will be returned
|
|
// It takes an input lookupFunc to convert the hostname to a list of IP addresses.
|
|
// If lookupFunc is nil, it defaults to [net.LookupIP]
|
|
func IsHostnameBlocked(denyList []AddressChecker, hostname string, lookupFunc internal_net.IPLookupFunc) error {
|
|
if lookupFunc == nil {
|
|
lookupFunc = net.LookupIP
|
|
}
|
|
|
|
ips, err := internal_net.HostnameToIPList(hostname, lookupFunc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return IsHostBlocked(denyList, hostname, ips...)
|
|
}
|
|
|
|
// IsURLBlocked checks a URL against denyList. If a match is found, an [AddressDeniedError] will be returned
|
|
// It takes an input lookupFunc to convert the URL's hostname to a list of IP addresses.
|
|
// If lookupFunc is nil, it defaults to [net.LookupIP]
|
|
func IsURLBlocked(denyList []AddressChecker, address *url.URL, lookupFunc internal_net.IPLookupFunc) error {
|
|
return IsHostnameBlocked(denyList, address.Hostname(), lookupFunc)
|
|
}
|