moshcode 0.85.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/dns-service.mjs +8 -4
- package/src/trust.mjs +45 -6
package/package.json
CHANGED
package/src/dns-service.mjs
CHANGED
|
@@ -250,10 +250,14 @@ export function proxyServiceUnit({
|
|
|
250
250
|
`Environment=MOSHPIT_PROXY_PORT=${port}`,
|
|
251
251
|
`Environment=MOSHPIT_PROXY_DIR=${dir}`,
|
|
252
252
|
];
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
|
|
253
|
+
// Deliberately not written any more.
|
|
254
|
+
//
|
|
255
|
+
// It used to be set from the endings the registry had sold — 18224 of them, a
|
|
256
|
+
// ~150 KB environment variable in a unit file, for a list stale the next time
|
|
257
|
+
// one is sold. moshpit-proxy now reads an unset value as "every Moshpit
|
|
258
|
+
// ending", defining the namespace by excluding the real internet rather than
|
|
259
|
+
// by enumerating what Moshpit owns, so there is nothing left to pass. Setting
|
|
260
|
+
// it there still narrows, which is a deployment's choice and not this unit's.
|
|
257
261
|
|
|
258
262
|
lines.push(
|
|
259
263
|
`ExecStart=${wrapper}`,
|
package/src/trust.mjs
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import path from "node:path";
|
|
21
21
|
import os from "node:os";
|
|
22
22
|
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { IANA_TLDS } from "./iana-tlds.mjs";
|
|
23
24
|
|
|
24
25
|
/** Where moshpit-proxy generates its root on first run. */
|
|
25
26
|
export function caPath({ home = os.homedir(), dir = null } = {}) {
|
|
@@ -139,15 +140,53 @@ export function requireNameConstraints(text, { tlds = [] } = {}) {
|
|
|
139
140
|
// `excluded;DNS:.hacker` alone is an unconstrained root wearing the word
|
|
140
141
|
// "constraints". Requiring a permitted DNS subtree is what makes the rest of
|
|
141
142
|
// this check mean anything.
|
|
143
|
+
const bare = (entry) => entry.replace(/^\./, "");
|
|
144
|
+
|
|
145
|
+
// Two shapes are acceptable, and they make the same promise a different way.
|
|
146
|
+
//
|
|
147
|
+
// The old one permits a list of Moshpit endings and is bounded by what it
|
|
148
|
+
// names. It cannot scale: there are 18224 endings, a permitted subtree each
|
|
149
|
+
// is roughly 214 KB of constraints on every handshake, and the list is stale
|
|
150
|
+
// the next time the registry sells one — which is why a machine could reach
|
|
151
|
+
// `.2600` over HTTPS and not `.hacker`.
|
|
152
|
+
//
|
|
153
|
+
// The new one names nothing and excludes the real internet instead: all 1438
|
|
154
|
+
// delegated top-level domains, about 15 KB, covering every Moshpit ending
|
|
155
|
+
// that exists or ever will. RFC 5280 4.2.1.10 leaves a name type unrestricted
|
|
156
|
+
// when no permitted subtree names it, which is what makes that work — and is
|
|
157
|
+
// also exactly what an unconstrained root looks like, so the difference has
|
|
158
|
+
// to be established rather than assumed.
|
|
159
|
+
//
|
|
160
|
+
// It is established against the same IANA list this tool refuses to sell
|
|
161
|
+
// endings from. A root that excludes the internet cannot forge your bank,
|
|
162
|
+
// which is the whole property the old shape bought by enumeration.
|
|
142
163
|
if (!constraints.permitted.length) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
const excluded = new Set(constraints.excluded.map(bare));
|
|
165
|
+
const covered = [...IANA_TLDS].filter((tld) => excluded.has(tld));
|
|
166
|
+
const uncovered = [...IANA_TLDS].filter((tld) => !excluded.has(tld));
|
|
167
|
+
|
|
168
|
+
// A handful of exclusions is not this shape; it is an unconstrained root
|
|
169
|
+
// with a few names crossed out, which is the thing this gate exists to
|
|
170
|
+
// refuse. The threshold sits far below the real count on purpose: the
|
|
171
|
+
// question is "is the internet excluded", not "is this list current".
|
|
172
|
+
if (covered.length < 1000) {
|
|
173
|
+
return {
|
|
174
|
+
ok: false,
|
|
175
|
+
kind: "unconstrained",
|
|
176
|
+
why: covered.length
|
|
177
|
+
? `the root excludes only ${covered.length} real top-level domains — it could still vouch for the rest of the internet`
|
|
178
|
+
: "the root permits no DNS subtree and excludes no real domains, so it could vouch for any name",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// A root minted before a TLD was delegated does not exclude it. A real gap,
|
|
183
|
+
// and a small one, so it is reported rather than made fatal: refusing here
|
|
184
|
+
// would mean refusing every root the day IANA adds a name.
|
|
185
|
+
return uncovered.length
|
|
186
|
+
? { ok: true, why: `excludes ${covered.length} real top-level domains`, uncovered }
|
|
187
|
+
: { ok: true, why: `excludes all ${covered.length} real top-level domains` };
|
|
148
188
|
}
|
|
149
189
|
|
|
150
|
-
const bare = (entry) => entry.replace(/^\./, "");
|
|
151
190
|
const permits = (tld) => constraints.permitted.some((entry) => bare(entry) === String(tld).toLowerCase());
|
|
152
191
|
|
|
153
192
|
const missing = tlds.filter((tld) => !permits(tld));
|