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 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 through an entry that covers a loopback address, never through one that covers only 0.0.0.0 or ::; ::/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, the two pinning tests and the allowlist refusal test follow. Model: opus-5-5
This commit is contained in:
@@ -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,25 @@ 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 an
|
||||
allowlist reaches loopback only through an entry that covers a loopback
|
||||
address, such as `127.0.0.0/8`, `::1` or `0.0.0.0/0`, never through one
|
||||
that covers only `0.0.0.0` or `::`; `0.0.0.0/8`, for example, does not
|
||||
open loopback.
|
||||
|
||||
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 +3105,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
|
||||
|
||||
Reference in New Issue
Block a user