Skip to content

fix(dns): size the per-client limit for a browser, not against one - #103

Merged
ralyodio merged 1 commit into
masterfrom
fix/dns-client-burst
Aug 3, 2026
Merged

fix(dns): size the per-client limit for a browser, not against one#103
ralyodio merged 1 commit into
masterfrom
fix/dns-client-burst

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #102, for the case that PR did not cover.

#102 exempted loopback from the rate limiter, which fixes a machine resolving through the bridge locally. But this deployment serves dns.moshcode.sh publicly, and a laptop pointed at it is a remote client — so it still paid a 50 qps / 100 burst budget.

That budget exists to price reflection: a forged source address turns our answers into someone else's inbound traffic, so a client's budget is the most we will ever send a victim who never asked. It was never meant to price browsing, and at 50/100 it did.

A client here is an address, not a person, and behind one address is a laptop with tabs open — or a household, or an office. A single page asks A, AAAA and HTTPS for every hostname it touches, so a few tabs clear 100 queries in a burst without trying. Over-budget queries are dropped rather than refused — right for a spoofing victim, worst possible for a browser: nothing comes back, the stub waits out its full timeout, retries, and the page finishes with subresources that never resolved.

Measured against the live bridge before #102: a 150-query burst lost 20.

Why 200/600

Still a bound, just drawn around a browser instead of inside it. It lets through ~200 answers/sec to a spoofed victim — well under 100KB/s, and no kind of amplifier, since ANY is already refused and that keeps the amplification factor small. Both knobs remain configurable via MOSHPIT_DNS_QPS / MOSHPIT_DNS_BURST; this only changes the default, which is what the moshpit-dns.service unit relies on (it sets neither).

191 tests pass; tsc --noEmit clean.

🤖 Generated with Claude Code

The rate limit prices reflection: a forged source address turns our answers
into someone else's inbound traffic, so a client's budget is the most we will
ever send a victim who never asked. At 50 qps / 100 burst it priced ordinary
browsing too.

A client here is an address, not a person, and behind one address is a laptop
with tabs open — or a household, or an office. A single page asks A, AAAA and
HTTPS for every hostname it touches, so a few tabs clear 100 queries in a burst
without trying. The excess is dropped rather than refused, which is right for a
spoofing victim and the worst possible answer for a browser: nothing comes back,
the stub waits out its full timeout, retries, and the page finishes with
subresources that never resolved.

Loopback stopped paying this in #102, which covers a machine resolving through
the bridge locally. It does not cover the case this deployment actually serves —
a laptop pointed at dns.moshcode.sh is a remote client and still paid.

200/600 is still a bound, drawn around a browser instead of inside it. It lets
through ~200 answers/sec to a spoofed victim, well under 100KB/s and no kind of
amplifier: ANY is already refused, which keeps the amplification factor small
enough for this to be the right trade. Both knobs stay configurable via
MOSHPIT_DNS_QPS / MOSHPIT_DNS_BURST.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio marked this pull request as ready for review August 3, 2026 10:27
@ralyodio
ralyodio merged commit 78120ec into master Aug 3, 2026
2 checks passed
@ralyodio
ralyodio deleted the fix/dns-client-burst branch August 3, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant