Decision: should IPv6 rate-limit keys bucket by /64 rather than per-address? #125

Open
opened 2026-08-12 11:36:52 +02:00 by clawbot · 0 comments
Collaborator

Raised by the independent review of #122. Pre-existing and inherent to per-IP keying, so it was correctly out of scope there — but it is worth deciding before 1.0.0, because it limits how much the receiver rate limit is actually worth over IPv6.

Rate-limit keys are per-address, i.e. per /128 for IPv6. A client with a routed /64 — which is the normal residential and mobile IPv6 allocation — can rotate source addresses and mint a fresh bucket per request. All three limiters are affected; the unauthenticated receiver endpoint is the one that matters.

Net effect: #88 closed header spoofing, but for IPv6 clients the limit is still evadable at the network layer, without any spoofing at all.

Needs your call because bucketing by prefix trades precision for coverage.

Options

  1. Key IPv6 by /64, IPv4 by /32 (unchanged). Matches how the allocation boundary actually works. One subscriber gets one bucket.
  2. Make the IPv6 prefix length configurable, defaulting to 64.
  3. Leave per-address and accept that IPv6 clients can evade the receiver limit.

Recommendation: option 1. It is the conventional choice, needs no new configuration surface, and matches the threat — the attacker gets addresses in /64 units, so that is the unit worth limiting. The cost is that genuine distinct clients sharing a /64 share a bucket, which for a webhook receiver is the correct conservative direction. Option 2 adds a knob that nobody will tune correctly. Option 3 leaves the control half-working on the address family that is increasingly the default.

Done-criteria once decided

  • The key function derives the bucket from the /64 for IPv6 addresses and the full address for IPv4.
  • A test asserting two addresses in the same /64 share a bucket, and two in different /64s do not.
  • Applies to all three limiters, since they share the key function.

Note this touches internal/middleware/ratelimit.go, the same file as #124 — sequence them rather than running both at once.

Raised by the independent review of https://git.eeqj.de/sneak/webhooker/pulls/122. Pre-existing and inherent to per-IP keying, so it was correctly out of scope there — but it is worth deciding before 1.0.0, because it limits how much the receiver rate limit is actually worth over IPv6. Rate-limit keys are per-address, i.e. per `/128` for IPv6. A client with a routed `/64` — which is the normal residential and mobile IPv6 allocation — can rotate source addresses and mint a fresh bucket per request. All three limiters are affected; the unauthenticated receiver endpoint is the one that matters. Net effect: https://git.eeqj.de/sneak/webhooker/issues/88 closed header spoofing, but for IPv6 clients the limit is still evadable at the network layer, without any spoofing at all. Needs your call because bucketing by prefix trades precision for coverage. ## Options 1. **Key IPv6 by `/64`, IPv4 by `/32` (unchanged).** Matches how the allocation boundary actually works. One subscriber gets one bucket. 2. **Make the IPv6 prefix length configurable**, defaulting to 64. 3. **Leave per-address** and accept that IPv6 clients can evade the receiver limit. Recommendation: option 1. It is the conventional choice, needs no new configuration surface, and matches the threat — the attacker gets addresses in `/64` units, so that is the unit worth limiting. The cost is that genuine distinct clients sharing a `/64` share a bucket, which for a webhook receiver is the correct conservative direction. Option 2 adds a knob that nobody will tune correctly. Option 3 leaves the control half-working on the address family that is increasingly the default. ## Done-criteria once decided - The key function derives the bucket from the `/64` for IPv6 addresses and the full address for IPv4. - A test asserting two addresses in the same `/64` share a bucket, and two in different `/64`s do not. - Applies to all three limiters, since they share the key function. Note this touches `internal/middleware/ratelimit.go`, the same file as https://git.eeqj.de/sneak/webhooker/issues/124 — sequence them rather than running both at once.
sneak was assigned by clawbot 2026-08-12 11:36:52 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#125