Commit 9617f81
feat(dns): finish the job — install trust, not just routing (#275)
* feat(dns): finish the job — install trust, not just routing
`dns enable` pointed names at the resolver and stopped, so a Moshpit
name resolved and then failed its TLS handshake. To the person who typed
the URL that is not "DNS works", it is "still broken" — which is exactly
how it was reported.
No certificate authority will ever close that gap. The Baseline
Requirements banned issuance for non-IANA names: it stopped in November
2015 and the survivors were revoked by October 2016, and a CA in a root
store that issued for `.rank` would be distrusted for doing it. The rule
is the penalty, so this is not a matter of trying harder.
moshpit-proxy already solved it — verify the origin key against the pin
the registry published, then re-sign with a root generated on this
machine, because restating the result is the only language a stock client
accepts. Nobody had wired it up. This is the wiring, not new crypto.
The one thing it must never do is install a root that could vouch for the
clearnet, so that is checked rather than assumed. A root without a
critical nameConstraints extension permitting our endings is REFUSED, not
warned about — there is no wording of a warning that makes an
unconstrained root in a system store acceptable. moshpit-proxy sets the
constraint today and tests it; this side cannot assume it always will,
because that regression would otherwise land automatically on every
machine that ran `dns enable`.
Reported honestly rather than summarised:
- NSS and the system store are separate outcomes. NSS is user-level and
always lands; curl's store needs root. Saying "installed" while curl
still refuses is what sends someone back to conclude it is broken, so
without root it does the half it can and names the half it cannot.
- It never fails `enable`. DNS is switched and verified by the time
this runs; failing the command over a trust store would roll back
working resolution to fix a certificate.
`--no-trust` opts out.
This only helps machines it is run on. Nothing shippable makes a
stranger's curl trust a Moshpit name; TronBrowser is the answer for
people who have run nothing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(trust): make the name-constraints gate actually constrain
The gate accepted a root that can vouch for anything, which is the one
thing it exists to refuse.
`requireNameConstraints` searched the whole `openssl x509 -text` output for
`DNS:.<tld>`. That string reads identically under `Permitted:` and under
`Excluded:`, so the check could not tell the two apart -- and RFC 5280
4.2.1.10 says constraints bind only the name *types* they mention. A root
carrying `nameConstraints=critical,excluded;DNS:.hacker` therefore permits
every DNS name on the internet, names the ending we asked about, is marked
critical, and passed:
requireNameConstraints -> {"ok":true,"why":"constrained to Moshpit endings"}
trustPlan.ok -> true | steps -> ["nss","system"]
It then went into the system store. So did a root permitting `.evil` while
excluding `.hacker`, and one permitting `.hacker` *and* `.com`.
Parse the extension instead: read Permitted and Excluded as the separate
lists they are, require a permitted DNS subtree to exist at all, require
every claimed ending inside it, and refuse anything else in it.
Also: the paths were root's, not the operator's.
Everything here keys off a home directory -- moshpit-proxy's root under
~/.moshpit, the NSS database the browser reads at ~/.pki/nssdb. But `dns
enable` needs root, and since #274 it escalates itself, so os.homedir()
is /root by the time this runs. It looked for the root in a directory
moshpit-proxy never wrote to and reported "no local root" on a machine
that had one. operatorHome() resolves SUDO_USER/DOAS_USER through the
shell (passwd, so macOS and unusual layouts work), and the NSS store is
chowned back afterwards -- left root-owned, the browser silently loses
the ability to update its own store. Same $HOME-under-sudo trap #272 and
#274 closed.
Also in this commit:
- tests skip loudly instead of silently. Four used `if (!text) return`
when openssl was unavailable, passing without asserting anything --
a green guard that had tested nothing.
- `--no-trust` is documented, in `dns` usage and in the CLI schema, so
help renders it. It existed only as a string literal.
- `verifyStockTls` is wired in rather than exported, tested and never
called. It runs only when a root was installed and there is a probe
name, and never fails the command.
- merges main, so this sits on #274's self-escalation rather than
predating it.
1348 tests, 1345 pass, 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent 0ab56aa commit 9617f81
4 files changed
Lines changed: 809 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
189 | 189 | | |
190 | 190 | | |
191 | 191 | | |
| 192 | + | |
192 | 193 | | |
193 | 194 | | |
194 | 195 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1930 | 1930 | | |
1931 | 1931 | | |
1932 | 1932 | | |
| 1933 | + | |
1933 | 1934 | | |
1934 | 1935 | | |
1935 | 1936 | | |
| |||
1974 | 1975 | | |
1975 | 1976 | | |
1976 | 1977 | | |
| 1978 | + | |
| 1979 | + | |
| 1980 | + | |
1977 | 1981 | | |
1978 | 1982 | | |
1979 | 1983 | | |
| |||
2548 | 2552 | | |
2549 | 2553 | | |
2550 | 2554 | | |
| 2555 | + | |
| 2556 | + | |
| 2557 | + | |
| 2558 | + | |
| 2559 | + | |
| 2560 | + | |
| 2561 | + | |
| 2562 | + | |
| 2563 | + | |
| 2564 | + | |
| 2565 | + | |
| 2566 | + | |
| 2567 | + | |
| 2568 | + | |
| 2569 | + | |
| 2570 | + | |
| 2571 | + | |
| 2572 | + | |
| 2573 | + | |
| 2574 | + | |
2551 | 2575 | | |
2552 | 2576 | | |
2553 | 2577 | | |
| |||
0 commit comments