package webhook
import (
"errors"
"fmt"
"net"
"strings"
"syscall"
)
var ErrBlockedAddress = errors.New("blocked destination address")
var blockedNets = func() []*net.IPNet {
cidrs := []string{
"100.64.0.0/10", "0.0.0.0/8", "240.0.0.0/4", "198.18.0.0/15", "192.0.0.0/24", "192.88.99.0/24", "192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24", "64:ff9b::/96", "64:ff9b:1::/48", "::/96", "2002::/16", "2001::/23", "fec0::/10", "100::/64", "2001:db8::/32", "3fff::/20", "5f00::/16", }
nets := make([]*net.IPNet, 0, len(cidrs))
for _, c := range cidrs {
_, n, _ := net.ParseCIDR(c)
nets = append(nets, n)
}
return nets
}()
func CheckWebhookHost(host string) error {
host = strings.TrimSuffix(strings.ToLower(host), ".")
if host == "localhost" || strings.HasSuffix(host, ".localhost") {
return ErrBlockedAddress
}
if ip := net.ParseIP(host); ip != nil && isBlockedIP(ip) {
return ErrBlockedAddress
}
return nil
}
func BlockInternal(network, address string, _ syscall.RawConn) error {
host, _, err := net.SplitHostPort(address)
if err != nil {
return err
}
ip := net.ParseIP(host)
if ip == nil {
return fmt.Errorf("cannot resolve %q", address)
}
if isBlockedIP(ip) {
return fmt.Errorf("%w: %s", ErrBlockedAddress, ip)
}
return nil
}
func isBlockedIP(ip net.IP) bool {
if ip.IsLoopback() ||
ip.IsPrivate() ||
ip.IsLinkLocalUnicast() ||
ip.IsLinkLocalMulticast() ||
ip.IsUnspecified() ||
ip.IsMulticast() {
return true
}
for _, n := range blockedNets {
if n.Contains(ip) {
return true
}
}
return false
}