Refuse [::], 0.0.0.0, IPv6 multicast and documentation space (closes #341)
check / check (push) Waiting to run

On Linux a connection to the unspecified address [::] or 0.0.0.0
reaches the host's own loopback, and the SSRF guard let [::] through.
Both unspecified addresses now sit in alwaysBlockedNetworks, so an
allowlist reaches loopback only by naming it; ::/128 joins the default
blocklist beside 0.0.0.0/8. IPv6 multicast (ff00::/8) and documentation
space (2001:db8::/32) are refused by default. Every default blocklist
entry gets a one-line comment, and the README, the rules above each
list and the two pinning tests follow.

Model: opus-5-5
This commit is contained in:
2026-10-02 07:14:58 +00:00
parent eb4c4cc849
commit aadbab8cf0
6 changed files with 143 additions and 47 deletions
+38 -28
View File
@@ -158,19 +158,20 @@ WireServer, which serves an Azure VM its credentials. Because it is a
public address, listing it in `ALLOWED_EGRESS_CIDRS` reopens it.
That is all the default blocklist covers: the IPv4 private and reserved
ranges; of IPv6, only loopback (`::1`), unique local addresses
(`fc00::/7`) and link-local addresses (`fe80::/10`); and certain public
addresses. A public address belongs on the default blocklist only if it
hands credentials, user data or bootstrap material to whatever can reach
it, without the caller presenting anything. A provider's other public
addresses are not refused. IBM Cloud, for example, serves its package
mirrors, time servers and object storage on `161.26.0.0/16`, and the
private endpoints of its own cloud services on `166.8.0.0/14`. Neither
range hands out credentials that way: the token service among those
endpoints issues a token only in exchange for something the caller
presents, such as an API key. Reaching these services can be a
legitimate delivery, and every cloud has some, so a partial list would
promise coverage it does not give.
ranges; of IPv6, only loopback (`::1`), the unspecified address (`::`),
unique local addresses (`fc00::/7`), link-local addresses (`fe80::/10`),
multicast (`ff00::/8`) and documentation space (`2001:db8::/32`); and
certain public addresses. A public address belongs on the default
blocklist only if it hands credentials, user data or bootstrap material
to whatever can reach it, without the caller presenting anything. A
provider's other public addresses are not refused. IBM Cloud, for
example, serves its package mirrors, time servers and object storage on
`161.26.0.0/16`, and the private endpoints of its own cloud services on
`166.8.0.0/14`. Neither range hands out credentials that way: the token
service among those endpoints issues a token only in exchange for
something the caller presents, such as an API key. Reaching these
services can be a legitimate delivery, and every cloud has some, so a
partial list would promise coverage it does not give.
That default is also inconvenient for the thing webhooker is mostly
for: taking a public webhook and forwarding it to something on your own
@@ -210,16 +211,16 @@ Two things this setting cannot do:
the list is always an allowlist; an empty list (the default) means
every private and reserved range stays refused. Note that
`0.0.0.0/0` gets you most of the way there anyway, per above.
- **It cannot open link-local, or a cloud metadata endpoint at a
non-public address that discloses credentials or user data.** An
address is on the list below when it is not a public address and both
of these hold: the provider fixes it, so it cannot collide with
anything you run; and reaching it hands out credentials, user data or
bootstrap material. Those stay blocked no matter what you list,
including when you list them outright or list a supernet such as
`0.0.0.0/0`, `::/0`, `fd00::/8` or `100.64.0.0/10`. Treat this as best
effort rather than a guarantee — it is a hand-maintained list and the
caveat below the table applies:
- **It cannot open link-local, the unspecified addresses, or a cloud
metadata endpoint at a non-public address that discloses credentials
or user data.** A metadata address is on the list below when it is not
a public address and both of these hold: the provider fixes it, so it
cannot collide with anything you run; and reaching it hands out
credentials, user data or bootstrap material. Those stay blocked no
matter what you list, including when you list them outright or list a
supernet such as `0.0.0.0/0`, `::/0`, `fd00::/8` or `100.64.0.0/10`.
Treat this as best effort rather than a guarantee — it is a
hand-maintained list and the caveat below the table applies:
| Blocked unconditionally | What it is |
| ----------------------- | ---------- |
@@ -233,14 +234,22 @@ Two things this setting cannot do:
| `fd00:a9fe:a9fe::1/128` | Linode/Akamai metadata over IPv6 |
| `100.100.100.200/32` | Alibaba Cloud metadata, inside CGNAT |
| `192.0.0.192/32` | Oracle Cloud Classic metadata |
| `0.0.0.0/32` | IPv4 unspecified address, which reaches this host's loopback on Linux |
| `::/128` | IPv6 unspecified address, which reaches this host's loopback on Linux |
| `::a9fe:a9fe/128` | `169.254.169.254` as an IPv4-compatible IPv6 address |
| `64:ff9b::a9fe:a9fe/128` | `169.254.169.254` behind the NAT64 well-known prefix |
The IPv4-mapped form `::ffff:169.254.169.254` is covered by the
`169.254.0.0/16` entry. Reaching any of these is credential or
user-data theft rather than delivery to an internal service. Every
entry outside the two link-local blocks is a single address, so
blocking it costs you nothing else on the network around it.
`169.254.0.0/16` entry. Reaching any of these but the two unspecified
addresses is credential or user-data theft rather than delivery to an
internal service. Every entry outside the two link-local blocks is a
single address, so blocking it costs you nothing else on the network
around it.
The unspecified addresses `0.0.0.0` and `::` hand out nothing
themselves, but no host can have either, and on Linux a connection to
one reaches this host's own loopback. They are listed so that the only
way to open loopback is to name it, as `127.0.0.0/8` or `::1`.
The six ULA entries, all inside `fd00::/8`, are why this matters in
practice: `fd00::/8` is an ordinary block to allowlist for your own
@@ -3093,7 +3102,8 @@ check, see [The login endpoint](#the-login-endpoint).
route through a single decision function, so they cannot disagree
about a destination. An operator can permit specific blocks with
[`ALLOWED_EGRESS_CIDRS`](#allowing-egress-to-your-own-network); the
guard cannot be switched off, and link-local plus a
guard cannot be switched off, and link-local, the unspecified
addresses `0.0.0.0` and `::`, and a
[pinned set](#allowing-egress-to-your-own-network) of known cloud
metadata endpoints — several of which are ULAs outside link-local —
stay blocked whatever is listed, though listing `0.0.0.0/0` or