moshcode 0.86.0 → 0.87.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 +62 -3
package/package.json
CHANGED
package/src/dns-service.mjs
CHANGED
|
@@ -130,10 +130,12 @@ export function serviceUnit({
|
|
|
130
130
|
function run(command, args) {
|
|
131
131
|
return new Promise((resolve) => {
|
|
132
132
|
const child = spawn(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
133
|
+
let out = "";
|
|
133
134
|
let err = "";
|
|
135
|
+
child.stdout.on("data", (d) => (out += d));
|
|
134
136
|
child.stderr.on("data", (d) => (err += d));
|
|
135
137
|
child.on("error", (error) => resolve({ ok: false, error: error.message }));
|
|
136
|
-
child.on("exit", (code) => resolve({ ok: code === 0, error: err.trim() }));
|
|
138
|
+
child.on("exit", (code) => resolve({ ok: code === 0, stdout: out, error: err.trim() }));
|
|
137
139
|
});
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -277,6 +279,38 @@ export function proxyServiceUnit({
|
|
|
277
279
|
return lines.join("\n");
|
|
278
280
|
}
|
|
279
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Is the local root the old shape, minted for a list of endings?
|
|
284
|
+
*
|
|
285
|
+
* moshpit-proxy used to constrain its root by naming what it could certify.
|
|
286
|
+
* That root works only for the endings it happened to name, which is why a
|
|
287
|
+
* machine could reach `.2600` over HTTPS and not `.hacker` — and why the list
|
|
288
|
+
* could never be right, since the registry keeps selling more.
|
|
289
|
+
*
|
|
290
|
+
* The root it mints now excludes the real internet instead and covers the whole
|
|
291
|
+
* namespace. But a machine that ran the old proxy still has the old root on
|
|
292
|
+
* disk, and moshpit-proxy will not replace a root that already exists — so
|
|
293
|
+
* without this, upgrading leaves the narrow root in place and `.hacker` keeps
|
|
294
|
+
* failing with nothing to explain why.
|
|
295
|
+
*
|
|
296
|
+
* A permitted DNS subtree is the tell. The new root has none by design.
|
|
297
|
+
*/
|
|
298
|
+
export async function rootIsNarrow({
|
|
299
|
+
home = operatorHome(),
|
|
300
|
+
exec = run,
|
|
301
|
+
exists = existsSync,
|
|
302
|
+
} = {}) {
|
|
303
|
+
const file = join(home, ".moshpit", "ca", "ca.crt");
|
|
304
|
+
if (!exists(file)) return { narrow: false, reason: "no root yet", file };
|
|
305
|
+
const described = await exec("openssl", ["x509", "-noout", "-text", "-in", file]);
|
|
306
|
+
// Unreadable is not narrow. Deleting a root because openssl was missing would
|
|
307
|
+
// throw away a working setup to fix a problem nobody had.
|
|
308
|
+
if (!described.ok) return { narrow: false, reason: "could not read it", file };
|
|
309
|
+
return described.stdout && /Permitted:/i.test(described.stdout)
|
|
310
|
+
? { narrow: true, reason: "constrained to a list of endings", file }
|
|
311
|
+
: { narrow: false, reason: "already covers the namespace", file };
|
|
312
|
+
}
|
|
313
|
+
|
|
280
314
|
/** Where moshpit-proxy's installer puts its wrapper, if it ran. */
|
|
281
315
|
export function proxyWrapperPath({ home = operatorHome(), exists = existsSync } = {}) {
|
|
282
316
|
const candidate = join(home, ".local/bin/moshpit-proxy");
|
|
@@ -304,6 +338,8 @@ export async function ensureProxyService({
|
|
|
304
338
|
tlds = [],
|
|
305
339
|
port = 443,
|
|
306
340
|
exec = run,
|
|
341
|
+
narrowRoot = rootIsNarrow,
|
|
342
|
+
paths = proxyServicePaths,
|
|
307
343
|
read = async (f) => (await import("node:fs/promises")).readFile(f, "utf8"),
|
|
308
344
|
listening = defaultPortHeld,
|
|
309
345
|
waitMs = 30000,
|
|
@@ -311,7 +347,7 @@ export async function ensureProxyService({
|
|
|
311
347
|
const wrapper = proxyWrapperPath({ home });
|
|
312
348
|
if (!wrapper) return { ok: false, reason: "not-installed", steps: [] };
|
|
313
349
|
|
|
314
|
-
const { path, systemctl } =
|
|
350
|
+
const { path, systemctl } = paths();
|
|
315
351
|
const unit = proxyServiceUnit({ wrapper, nodeDir, home, user, port, tlds });
|
|
316
352
|
// Read before writing so the manifest can put back whatever was here — which
|
|
317
353
|
// is usually nothing, and "nothing" has to be recorded as precisely as
|
|
@@ -319,6 +355,22 @@ export async function ensureProxyService({
|
|
|
319
355
|
const before = await read(path).catch(() => null);
|
|
320
356
|
|
|
321
357
|
const steps = [];
|
|
358
|
+
|
|
359
|
+
// A root minted by the old proxy names the endings it may certify, and
|
|
360
|
+
// moshpit-proxy will not replace a root that already exists. Left alone, an
|
|
361
|
+
// upgraded machine keeps the narrow root and `.hacker` keeps failing — so it
|
|
362
|
+
// goes, and the proxy mints the current shape on its next start.
|
|
363
|
+
//
|
|
364
|
+
// Safe to remove: it is a local root, regenerated in seconds, and `dns enable`
|
|
365
|
+
// installs the replacement into the trust stores in the same run. Removing it
|
|
366
|
+
// without that would be the destructive half on its own, which is why this
|
|
367
|
+
// lives here and not in the installer.
|
|
368
|
+
const narrow = await narrowRoot({ home, exec });
|
|
369
|
+
if (narrow.narrow) {
|
|
370
|
+
await rm(join(home, ".moshpit", "ca"), { recursive: true, force: true }).catch(() => {});
|
|
371
|
+
steps.push({ step: `reminted the local root — the old one was ${narrow.reason}`, ok: true });
|
|
372
|
+
}
|
|
373
|
+
|
|
322
374
|
try {
|
|
323
375
|
await mkdir(dirname(path), { recursive: true });
|
|
324
376
|
await writeFile(path, unit);
|
|
@@ -328,7 +380,14 @@ export async function ensureProxyService({
|
|
|
328
380
|
}
|
|
329
381
|
|
|
330
382
|
const [cmd, ...flags] = systemctl;
|
|
331
|
-
|
|
383
|
+
// `enable --now` starts a stopped unit and does nothing to a running one, so
|
|
384
|
+
// an upgrade would leave the previous process — and the previous root, and
|
|
385
|
+
// the previous namespace — in place. Restart is what makes an upgrade take.
|
|
386
|
+
for (const args of [
|
|
387
|
+
[...flags, "daemon-reload"],
|
|
388
|
+
[...flags, "enable", PROXY_UNIT_NAME],
|
|
389
|
+
[...flags, "restart", PROXY_UNIT_NAME],
|
|
390
|
+
]) {
|
|
332
391
|
const result = await exec(cmd, args);
|
|
333
392
|
steps.push({ step: `${cmd} ${args.join(" ")}`, ok: result.ok, error: result.error });
|
|
334
393
|
if (!result.ok) return { ok: false, reason: "systemctl-failed", before, steps, path, unit };
|