caddy: the signup rate-limit zone counts 4 requests per sign-up against a 10/min budget #304
Etiketter
Inga etiketter
area/backups
area/ci
area/control-panel
area/identity
area/infra
area/observability
area/payments
area/security
area/storage
area/web
blocked
needs-info
needs-triage
ready-for-implementation
type
bug
type
chore
type
docs
type
epic
type
feature
type
task
wontfix
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Inget förfallodatum satt.
Beroenden
Inga beroenden satta
Referens
bitborg/bitborg-infra#304
Läser in…
Hänvisa till i nytt ärende
Ingen beskrivning angiven.
Ta bort grenen "%!s()"
Borttagning av en gren är permanent. Även om den borttagna grenen kan fortsätta existera en kort tid innan den faktiskt tas bort, kan det INTE ångras i de flesta fall. Vill du fortsätta?
The
signuprate-limit zone budgets one request per sign-up. A real sign-up costs four, so theeffective limit is two sign-ups per minute per client IP — and on a shared IPv4 egress that budget is
shared by everyone behind it.
The arithmetic
roles/caddy/templates/Caddyfile.j2matchespath /signup /api/signup /api/captcha/*, keyed{remote_host}withipv6_prefix 56, atcaddy_ratelimit_signup_events: 10/window: 1m.One completed sign-up hits four of those paths:
GET /signupPOST /api/captcha/challengePOST /api/captcha/redeembitborg-web/src/lib/captcha.tsPOST /api/signupcap-tokenSo
events 10permits two sign-ups per minute per IP, not ten. A visitor who mistypes andresubmits re-solves the captcha and consumes roughly seven of the ten unaided. Visitors who merely
load
/signupand leave spend the same budget as people who convert.Why it matters now
Announcement traffic is bursty and concentrated behind shared egresses — corporate and university NAT,
mobile CGNAT — where every visitor presents the same IPv4 to Caddy.
ipv6_prefix 56is a reasonableper-household grouping, so IPv6 is not the exposure; shared IPv4 is.
The failure mode is the bad one: a 429 arriving mid-captcha reads as a broken site rather than as a
rate limit, and it lands precisely at the point of conversion.
This is the third instance of the same shape in this area — the reconciler was throttled because it
reaches Forgejo over the public URL and all its calls share one
{remote_host}key, and #195 was afail2ban ban on the runners' shared SNAT egress. A limit keyed on a shared egress identity punishes
the group for the behaviour of one member.
Suggested direction
Count state changes, not page views: scope the zone to
POST /api/signupand/api/captcha/redeem, dropGET /signupfrom the match set, then size the budget from what a realflow costs plus retries. That keeps the protection where abuse actually happens — challenge redemption
and account creation — instead of spending it on people reading the page. Simply raising
caddy_ratelimit_signup_eventsis a defensible launch-day measure but leaves the mis-sizing in place.Check the in-process limiter in
bitborg-web/src/lib/rate-limit.tsat the same time. The edge zone isdocumented as defence-in-depth over it, so if the app-level limiter is keyed on client IP with a
similarly tight budget, changing only Caddy fixes nothing.
Found while verifying launch readiness; being addressed together with the #302 tuning pass since it is
the same file and the same decision.