feat(dns): trust a name on the strength of its pin, and stop blaming the registry - #279
Draft
ralyodio wants to merge 2 commits into
Draft
feat(dns): trust a name on the strength of its pin, and stop blaming the registry#279ralyodio wants to merge 2 commits into
ralyodio wants to merge 2 commits into
Conversation
…the registry Two failures from the same first run, neither of which the machine could explain. 1. `dns enable` printed "no TLDs claimed yet — nothing to route" on a machine where the registry holds 5674 endings. The fetch had failed and the failure was swallowed to an empty array, so a network problem was reported as a fact about the registry — sending someone to claim an ending they already own. Same bug as the one fixed in `dns start`; this is the other half of it. The two cases now read differently, and the failure prints the curl that checks it. 2. The certificate error survived everything. moshpit-proxy verifies the registry pin and re-signs with a local root, but nothing routes to it: the resolver answers the origin, the proxy sits on loopback, and the two were never connected. So the root installed by #275 signs nothing, and `curl https://seo.rank` still meets the origin's self-signed certificate. `dns trust <name>` closes that without the proxy at all. A Moshpit name serves a self-signed leaf whose SAN names only itself, so trusting it vouches for that one name — a far smaller grant than a root, and why this path needs no name-constraints argument to be defensible. The pin check is what separates it from recklessness. Installing whatever answered the socket is trusting whoever reached the port first; installing it only when the registry already vouches for that exact key is registry-backed trust, which is a stronger claim than domain validation ever made. Every published pin is accepted, not just the first, because the registry lists the old one beside the new during a rotation precisely so a key can change without a flag day. A registry outage is reported as an outage, never as a rejected certificate: the answer to "the registry is down" is to wait, not to distrust a name. Verified against the live registry — the key seo.rank serves matches the pin the Pit publishes, and trusting that leaf takes curl from error 60 to 200. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ThreatCrush Security Scan88 finding(s) HIGH/CRITICAL: 50 | MEDIUM: 38
…and 38 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
#281) `dns trust <name>` works and does not scale. Someone browsing Moshpit meets a certificate error on every site they have not personally thought about, which is indistinguishable from the namespace being broken. `dns start --trust-all` trusts a name as it resolves: fetch the certificate it serves, check the key against the pin the registry published for that name, install it only on a match. Nothing is trusted on sight — a name with no published pin gets nothing, silently and forever — so this is registry-backed trust rather than trust-on-first-use. Three ways the automation could go wrong, none about cryptography: - blocking a DNS answer on certificate work. consider() queues and returns; the drain runs detached from the query handler. - asking once per query rather than once per name. A browser sends A and AAAA together and retries, so "on resolve" is a firehose: ten lookups of two names is two certificate fetches. - retrying a name that will never succeed. A refusal is final for that name until restart, or every lookup writes a log line and fails. Only a name that actually resolved to one of ours is considered: a forwarded clearnet name is not ours to trust, and NXDOMAIN has no origin to fetch from. Without root it says so once, rather than failing per name forever in the query log. A registry outage is not narrated per name — if the registry is down every name fails, and saying so each time turns the query log into the outage. Only refusals and successes are reported. Fixed while testing: idle() awaited a boolean rather than the in-flight drain, so it reported a queue as settled while it was still being worked. A flag can say someone else is draining; it cannot be awaited. Stacked on #279, which added the per-name command this automates. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two failures from one first run, neither of which the machine could explain.
1. "no TLDs claimed yet" was not true
The registry holds 5674 endings. The fetch had failed, and the failure was swallowed:
So a network problem was reported as a fact about the registry — which sends someone off to claim an ending they already own. This is the same bug fixed in
dns start, in the command next to it.The two cases now read differently, and the failure prints the one-liner that settles it:
2. The certificate error survived everything
moshpit-proxyverifies the registry pin and re-signs with a local root. But nothing routes to it:The resolver answers the origin, the proxy waits on loopback, and the two were never connected —
moshpit-dnshas no loopback mode at all. So the root installed by #275 signs nothing, andcurl https://seo.rankmeets the origin's self-signed certificate exactly as before. That was my error in #275: I assumed the proxy was in the path.dns trust <name>Closes it without the proxy. A Moshpit name serves a self-signed leaf whose SAN names only itself, so trusting it vouches for that one name — a far smaller grant than a root, and the reason this path needs no name-constraints argument to be defensible.
The pin check is what separates it from recklessness:
and when it does not match, nothing is written and nothing is refreshed:
Installing whatever answered the socket is the definition of trusting whoever reached the port first. Installing it only when the registry already vouches for that exact key is registry-backed trust — a stronger claim than domain validation ever made, since it proves registry ownership rather than momentary control of a socket.
Details that are deliberate:
ECONNREFUSEDcontains the wordREFUSED— the same confusion one layer down.)Verified against the live registry
23 new tests in
test/trust.test.mjs, generating real self-signed leaves with openssl. Suite green.