fix(phase3): skip templateless domains in scheduler; block CGNAT range in webhook SSRF guard
Domains imported without a template (TemplateID == nil) are a valid, unconfigured state, not a failure — RunOnce now skips them before calling checkDomain instead of letting LoadDomain's "no template" error turn into StatusError and a spammy unknown->error notification. isBlockedIP now also rejects 100.64.0.0/10 (RFC 6598 carrier-grade NAT), which net.IP.IsPrivate() does not cover, closing an SSRF gap in the webhook destination guard (both the pre-request check and the per-dial check use isBlockedIP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
@@ -83,17 +83,34 @@ func isAllowedURL(rawurl string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// cgnatBlock is the shared address space reserved for carrier-grade NAT
|
||||
// (RFC 6598, 100.64.0.0/10). net.IP.IsPrivate() only covers RFC1918/RFC4193
|
||||
// and does not treat this range as private, so it must be checked
|
||||
// explicitly or CGNAT-addressed internal services would be reachable via
|
||||
// webhook SSRF.
|
||||
var cgnatBlock = func() *net.IPNet {
|
||||
_, block, err := net.ParseCIDR("100.64.0.0/10")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return block
|
||||
}()
|
||||
|
||||
// isBlockedIP reports whether ip must never be connected to: loopback,
|
||||
// private (RFC1918 etc.), link-local, unspecified, or multicast. Used both
|
||||
// by isAllowedURL's pre-request check and by dialControl's per-connection
|
||||
// check.
|
||||
// private (RFC1918 etc.), link-local, unspecified, multicast, or
|
||||
// carrier-grade NAT (RFC 6598). Used both by isAllowedURL's pre-request
|
||||
// check and by dialControl's per-connection check.
|
||||
func isBlockedIP(ip net.IP) bool {
|
||||
if v4 := ip.To4(); v4 != nil {
|
||||
ip = v4
|
||||
}
|
||||
return ip.IsLoopback() ||
|
||||
ip.IsPrivate() ||
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
ip.IsLinkLocalMulticast() ||
|
||||
ip.IsUnspecified() ||
|
||||
ip.IsMulticast()
|
||||
ip.IsMulticast() ||
|
||||
cgnatBlock.Contains(ip)
|
||||
}
|
||||
|
||||
// dialControl returns a net.Dialer.Control function enforcing the SSRF guard
|
||||
|
||||
Reference in New Issue
Block a user