Bucket IPv6 rate-limit keys by /64 (closes #125)
All checks were successful
check / check (push) Successful in 3m2s
All checks were successful
check / check (push) Successful in 3m2s
Rate-limit keys were per-address, i.e. per /128 for IPv6. A routed /64 is the normal residential and mobile IPv6 allocation, so a client could rotate source addresses inside its own prefix and mint a fresh bucket per request, evading every limiter here at the network layer with no spoofing and nothing to detect. The shared key function now reduces the client address to a bucket by family: IPv4 keys on the full address, IPv6 on its /64 prefix. All four limiter instances (login, password change, and the receiver's per-entrypoint and aggregate limits) go through that one function, so all of them are covered. IPv4-mapped addresses (::ffff:1.2.3.4) key as the IPv4 address they carry rather than being masked, which would otherwise collapse every IPv4 client behind a mapping proxy into the ::ffff:0:0/96 bucket. An unparseable RemoteAddr still keys on its raw value, so those stay in distinct buckets instead of collapsing together. No new configuration surface.
This commit is contained in:
@@ -911,7 +911,14 @@ Every limiter here — receiver, login, and password change — identifies
|
||||
the client the same way, through one shared key function: the
|
||||
connection's own address, unless the peer is listed in
|
||||
`TRUSTED_PROXIES`, in which case the forwarded client address is used
|
||||
instead. See [Trusted proxies](#trusted-proxies). Deployed without that
|
||||
instead. That address becomes a bucket by family: IPv4 keys on the full
|
||||
address, IPv6 on its `/64` prefix. A routed `/64` is the normal
|
||||
residential and mobile IPv6 allocation, so keying IPv6 per address would
|
||||
let one subscriber rotate source addresses and mint a fresh bucket per
|
||||
request, evading these limits at the network layer without spoofing
|
||||
anything; the cost is that distinct clients inside one `/64` share a
|
||||
bucket. IPv4-mapped addresses (`::ffff:1.2.3.4`) key as the IPv4 address
|
||||
they carry. See [Trusted proxies](#trusted-proxies). Deployed without that
|
||||
variable set, a client behind a reverse proxy shares one bucket with
|
||||
every other client behind the same proxy. Set `TRUSTED_PROXIES` to the
|
||||
proxy's address to get per-client limits back. What the shared bucket
|
||||
|
||||
Reference in New Issue
Block a user