Refuse [::], 0.0.0.0, IPv6 multicast and documentation space (closes #341)
check / check (push) Waiting to run
check / check (push) Waiting to run
On the build host a connection to [::] reaches a listener on ::1, and one to 0.0.0.0 reaches 127.0.0.1, so a delivery target at either reached this host's loopback past the guard. 0.0.0.0/32 and ::/128 are now in alwaysBlockedNetworks, which no allowlist opens; an allowlist reaches loopback only through an entry covering a loopback address. IPv6 multicast (ff00::/8) and documentation space (2001:db8::/32) are refused by default and reopen when listed. Every default blocklist entry has a one-line comment, each list is pinned on its own, and tests refuse each address at target creation and at delivery. The README and the rules above each list match. Model: opus-5-5
This commit was merged in pull request #422.
This commit is contained in:
+57
-13
@@ -37,8 +37,8 @@ var (
|
||||
"blocked cloud metadata address",
|
||||
)
|
||||
errBlockedMetadata = errors.New(
|
||||
"blocked link-local or cloud instance metadata " +
|
||||
"address: ALLOWED_EGRESS_CIDRS cannot open it",
|
||||
"blocked link-local, cloud instance metadata or " +
|
||||
"unspecified address: ALLOWED_EGRESS_CIDRS cannot open it",
|
||||
)
|
||||
errInvalidScheme = errors.New(
|
||||
"only http and https are allowed",
|
||||
@@ -72,14 +72,17 @@ var blockedNetworks []*net.IPNet
|
||||
var blockedPublicNetworks []*net.IPNet
|
||||
|
||||
// alwaysBlockedNetworks are the ranges no configuration can
|
||||
// open: the link-local blocks and the cloud instance metadata
|
||||
// endpoints that live outside them. Reaching one is credential
|
||||
// or user-data theft rather than delivery to an internal
|
||||
// service, so a supplied CIDR that covers such an address still
|
||||
// leaves it blocked.
|
||||
// open, so a supplied CIDR that covers one still leaves it
|
||||
// blocked. An entry is here for one of two reasons: it is a
|
||||
// metadata endpoint (the link-local blocks and the cloud
|
||||
// instance metadata endpoints that live outside them), or it is
|
||||
// an unspecified address. Reaching a metadata endpoint is
|
||||
// credential or user-data theft rather than delivery to an
|
||||
// internal service.
|
||||
//
|
||||
// Inclusion criterion — an address belongs here only if BOTH
|
||||
// hold, and every entry below satisfies both:
|
||||
// Inclusion criterion for metadata endpoints — one belongs here
|
||||
// only if BOTH hold, and every metadata entry below satisfies
|
||||
// both:
|
||||
//
|
||||
// 1. It is a fixed address assigned by the provider, or a
|
||||
// range reserved by IANA — never one the operator chose.
|
||||
@@ -90,8 +93,8 @@ var blockedPublicNetworks []*net.IPNet
|
||||
// not cheaply rotated.
|
||||
//
|
||||
// Both halves are load-bearing, so use them to refuse a
|
||||
// candidate and say why. An endpoint disclosing only the
|
||||
// operator's own inventory (instance id, region, disks, NICs)
|
||||
// metadata candidate and say why. An endpoint disclosing only
|
||||
// the operator's own inventory (instance id, region, disks, NICs)
|
||||
// fails (2): letting a delivery target reach the operator's own
|
||||
// infrastructure is the feature ALLOWED_EGRESS_CIDRS exists to
|
||||
// provide. But (2) is not "IAM credentials only" either —
|
||||
@@ -112,6 +115,15 @@ var blockedPublicNetworks []*net.IPNet
|
||||
// This is a criterion, not an enumeration of every metadata
|
||||
// address in existence.
|
||||
//
|
||||
// The unspecified addresses 0.0.0.0 and :: are here for a
|
||||
// separate reason: they disclose nothing, but no host can have
|
||||
// either, and on Linux a connection to one reaches this host's
|
||||
// own loopback. Listing them means an allowlist reaches loopback
|
||||
// only through an entry that covers a loopback address
|
||||
// (127.0.0.0/8, ::1/128, 0.0.0.0/0), never through one that
|
||||
// covers only 0.0.0.0 or :: (0.0.0.0/8, for example). Nothing
|
||||
// else lives at either address, so refusing them costs nothing.
|
||||
//
|
||||
// Every entry is either already in blockedNetworks — this list is
|
||||
// what makes it unconditional — or an alternate encoding of
|
||||
// 169.254.169.254 that Contains does not match against
|
||||
@@ -131,23 +143,46 @@ var alwaysBlockedNetworks []*net.IPNet
|
||||
//nolint:gochecknoinits // init is the idiomatic way to parse CIDRs once at startup
|
||||
func init() {
|
||||
blockedNetworks = mustParseCIDRs([]string{
|
||||
// IPv4 loopback.
|
||||
"127.0.0.0/8",
|
||||
// RFC 1918 private network.
|
||||
"10.0.0.0/8",
|
||||
// RFC 1918 private network.
|
||||
"172.16.0.0/12",
|
||||
// RFC 1918 private network.
|
||||
"192.168.0.0/16",
|
||||
// IPv4 link-local.
|
||||
"169.254.0.0/16",
|
||||
// "This network", holding the IPv4 unspecified address 0.0.0.0.
|
||||
"0.0.0.0/8",
|
||||
// Carrier-grade NAT shared address space.
|
||||
"100.64.0.0/10",
|
||||
// IETF protocol assignments.
|
||||
"192.0.0.0/24",
|
||||
// IPv4 documentation (TEST-NET-1).
|
||||
"192.0.2.0/24",
|
||||
// Benchmarking.
|
||||
"198.18.0.0/15",
|
||||
// IPv4 documentation (TEST-NET-2).
|
||||
"198.51.100.0/24",
|
||||
// IPv4 documentation (TEST-NET-3).
|
||||
"203.0.113.0/24",
|
||||
// IPv4 multicast.
|
||||
"224.0.0.0/4",
|
||||
// Reserved, including the broadcast address.
|
||||
"240.0.0.0/4",
|
||||
// IPv6 loopback.
|
||||
"::1/128",
|
||||
// IPv6 unspecified address.
|
||||
"::/128",
|
||||
// IPv6 unique local addresses.
|
||||
"fc00::/7",
|
||||
// IPv6 link-local.
|
||||
"fe80::/10",
|
||||
// IPv6 multicast.
|
||||
"ff00::/8",
|
||||
// IPv6 documentation.
|
||||
"2001:db8::/32",
|
||||
})
|
||||
|
||||
blockedPublicNetworks = mustParseCIDRs([]string{
|
||||
@@ -207,6 +242,14 @@ func init() {
|
||||
// allowlist from opening it.
|
||||
"192.0.0.192/32",
|
||||
|
||||
// The unspecified addresses, each of which reaches this
|
||||
// host's loopback on Linux.
|
||||
//
|
||||
// IPv4 unspecified address, inside the blocked 0.0.0.0/8.
|
||||
"0.0.0.0/32",
|
||||
// IPv6 unspecified address.
|
||||
"::/128",
|
||||
|
||||
// 169.254.169.254 as an IPv4-compatible IPv6 address.
|
||||
"::a9fe:a9fe/128",
|
||||
// 169.254.169.254 behind the NAT64 well-known prefix.
|
||||
@@ -343,8 +386,9 @@ func (g *Guard) allows(ip net.IP) bool {
|
||||
// The order is the policy:
|
||||
//
|
||||
// 1. alwaysBlockedNetworks is refused before the allowlist is
|
||||
// consulted, so no configured CIDR reaches link-local or a
|
||||
// cloud metadata endpoint at a non-public address.
|
||||
// consulted, so no configured CIDR reaches link-local, a
|
||||
// cloud metadata endpoint at a non-public address, or an
|
||||
// unspecified address.
|
||||
// 2. The allowlist is consulted next, so a listed private
|
||||
// network, or a listed public address on the default
|
||||
// blocklist, becomes reachable.
|
||||
|
||||
Reference in New Issue
Block a user