moshcode 0.81.0 → 0.83.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 -0
- package/src/dns.mjs +86 -1
package/package.json
CHANGED
package/src/dns-service.mjs
CHANGED
|
@@ -67,6 +67,7 @@ export function serviceUnit({
|
|
|
67
67
|
port,
|
|
68
68
|
registryBase = null,
|
|
69
69
|
upstreams = [],
|
|
70
|
+
proxy = null,
|
|
70
71
|
user = process.env.USER || process.env.LOGNAME,
|
|
71
72
|
} = {}) {
|
|
72
73
|
if (!entry) throw new Error("serviceUnit needs the entry script to run");
|
|
@@ -80,6 +81,13 @@ export function serviceUnit({
|
|
|
80
81
|
// registry. Recorded here, while a working resolver is still around to be
|
|
81
82
|
// asked, rather than rediscovered at boot when it cannot be.
|
|
82
83
|
if (upstreams.length) args.push("--upstream", upstreams.join(","));
|
|
84
|
+
// Without this the supervised bridge answers every name with its origin, and
|
|
85
|
+
// the origin serves a certificate no CA signed — so `https://` fails on a
|
|
86
|
+
// machine where the pinned-TLS proxy is installed, trusted and running.
|
|
87
|
+
// `dns enable` has always probed for the proxy and passed it through;
|
|
88
|
+
// `dns service` did not, which made a service-managed bridge the one way to
|
|
89
|
+
// run Moshpit where HTTPS could never work.
|
|
90
|
+
if (proxy) args.push("--proxy", proxy);
|
|
83
91
|
const exec = [execPath, ...args].map((part) => (/\s/.test(part) ? JSON.stringify(part) : part)).join(" ");
|
|
84
92
|
|
|
85
93
|
const lines = [
|
package/src/dns.mjs
CHANGED
|
@@ -1664,6 +1664,37 @@ export function dnsmasqCatchAllConf({ host = DEFAULT_HOST, port = DEFAULT_PORT }
|
|
|
1664
1664
|
* Repeatable and comma-separated both work. `address#port` matches resolv.conf
|
|
1665
1665
|
* and dnsmasq rather than inventing a third spelling.
|
|
1666
1666
|
*/
|
|
1667
|
+
/**
|
|
1668
|
+
* Which name to detect the pinned-TLS proxy with.
|
|
1669
|
+
*
|
|
1670
|
+
* The probe is a TLS handshake with the name in SNI, and the proxy can only
|
|
1671
|
+
* present a certificate for a name that really exists — it fetches the
|
|
1672
|
+
* registry pin to mint one. A synthesised `a.<ending>` is never real, so the
|
|
1673
|
+
* handshake yields no certificate and a proxy that is installed, trusted and
|
|
1674
|
+
* listening reports as absent. Measured against a running proxy:
|
|
1675
|
+
*
|
|
1676
|
+
* a.moshpit -> no certificate
|
|
1677
|
+
* a.2600 -> no certificate
|
|
1678
|
+
* alt.2600 -> issuer=CN=Moshpit Local CA
|
|
1679
|
+
*
|
|
1680
|
+
* No registry endpoint lists names, so a real one cannot be discovered here.
|
|
1681
|
+
* `--proxy-probe <name>` supplies it. A bare flag is a typo rather than a
|
|
1682
|
+
* request to probe with nothing, and is reported instead of silently falling
|
|
1683
|
+
* back to the synthetic name that cannot work.
|
|
1684
|
+
*/
|
|
1685
|
+
export function proxyProbeFromArgs(args = [], claimed = []) {
|
|
1686
|
+
const at = args.indexOf("--proxy-probe");
|
|
1687
|
+
if (at >= 0) {
|
|
1688
|
+
const value = args[at + 1];
|
|
1689
|
+
if (value === undefined || value.startsWith("--")) return { name: null, invalid: true };
|
|
1690
|
+
return { name: value, invalid: false };
|
|
1691
|
+
}
|
|
1692
|
+
// The historical default. Kept because it is right on a machine whose proxy
|
|
1693
|
+
// serves every ending, and because removing it would turn "detected nothing"
|
|
1694
|
+
// into "refused to look".
|
|
1695
|
+
return { name: claimed[0] ? `a.${claimed[0]}` : null, invalid: false };
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1667
1698
|
export function upstreamsFromArgs(args = []) {
|
|
1668
1699
|
const servers = [];
|
|
1669
1700
|
const invalid = [];
|
|
@@ -2377,6 +2408,8 @@ const USAGE = `moshcode dns — resolve Moshpit names on this machine
|
|
|
2377
2408
|
running across reboots; --write installs and
|
|
2378
2409
|
starts it, --system for a system unit rather
|
|
2379
2410
|
than this user's, --remove takes it away
|
|
2411
|
+
--proxy-probe NAME a real Moshpit name to detect
|
|
2412
|
+
the pinned-TLS proxy with (it cannot serve a made-up one)
|
|
2380
2413
|
|
|
2381
2414
|
moshcode dns filter block ads, trackers, malware and phishing at the
|
|
2382
2415
|
resolver — \`moshcode dns filter help\` for the verbs
|
|
@@ -2788,7 +2821,59 @@ export async function dnsCommand(args = [], out = console.log, deps = {}) {
|
|
|
2788
2821
|
const upstreams = upstreamsFromArgs(rest).servers.length
|
|
2789
2822
|
? upstreamsFromArgs(rest).servers
|
|
2790
2823
|
: await discoverUpstreams();
|
|
2791
|
-
|
|
2824
|
+
// Probed the same way `enable` probes it, and for the same reason: a name
|
|
2825
|
+
// that resolves but cannot complete a TLS handshake reads as broken to the
|
|
2826
|
+
// person who typed the URL. Asked now, while this command can ask; the unit
|
|
2827
|
+
// it writes runs at boot and has no way to find out later.
|
|
2828
|
+
// The probe is a TLS handshake with the name in SNI, and the proxy can only
|
|
2829
|
+
// present a certificate for a name that actually exists — it fetches the
|
|
2830
|
+
// registry pin to make one. A synthesised `a.<ending>` is never a real name,
|
|
2831
|
+
// so the handshake yields no certificate and a proxy that is installed,
|
|
2832
|
+
// trusted and listening reports as absent. Measured on a running proxy:
|
|
2833
|
+
//
|
|
2834
|
+
// a.moshpit -> no certificate
|
|
2835
|
+
// a.2600 -> no certificate
|
|
2836
|
+
// alt.2600 -> issuer=CN=Moshpit Local CA
|
|
2837
|
+
//
|
|
2838
|
+
// There is no registry endpoint that lists names, so the tool cannot find a
|
|
2839
|
+
// real one by itself. `--proxy-probe <name>` supplies one. The issuer check
|
|
2840
|
+
// still runs against it: naming a probe says which name to ask about, never
|
|
2841
|
+
// that a proxy is there.
|
|
2842
|
+
let proxy = null;
|
|
2843
|
+
if (!rest.includes("--no-proxy")) {
|
|
2844
|
+
const explicit = proxyProbeFromArgs(rest);
|
|
2845
|
+
if (explicit.invalid) {
|
|
2846
|
+
out("! --proxy-probe needs a Moshpit name, e.g. --proxy-probe blue.eggs");
|
|
2847
|
+
out("");
|
|
2848
|
+
}
|
|
2849
|
+
// The registry is only consulted when no name was given: fetching 18000
|
|
2850
|
+
// endings to build a probe that cannot work is pure cost.
|
|
2851
|
+
const claimed = explicit.name ? [] : await fetchTlds({ registryBase }).catch(() => []);
|
|
2852
|
+
const probeName = proxyProbeFromArgs(rest, claimed).name;
|
|
2853
|
+
if (probeName) {
|
|
2854
|
+
const local = await findLocalProxyImpl(probeName).catch(() => ({ found: false }));
|
|
2855
|
+
if (local.found) proxy = local.address.v4 || local.address.v6;
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
const unit = serviceUnit({ system, entry: cliEntry(), port, registryBase, upstreams, proxy });
|
|
2860
|
+
|
|
2861
|
+
if (proxy) {
|
|
2862
|
+
out(`ok pinned-TLS proxy on ${proxy}:${PROXY_PORT} — names will answer there, so https:// verifies`);
|
|
2863
|
+
out("");
|
|
2864
|
+
} else if (!rest.includes("--no-proxy")) {
|
|
2865
|
+
out("! no pinned-TLS proxy found on this machine");
|
|
2866
|
+
out(" names will answer their origin, and a stock client cannot verify those —");
|
|
2867
|
+
out(" https:// will fail even though the name resolves.");
|
|
2868
|
+
out("");
|
|
2869
|
+
out(" If it is not installed:");
|
|
2870
|
+
out(" curl -fsSL https://raw.githubusercontent.com/profullstack/moshpit-proxy/main/install.sh | sh");
|
|
2871
|
+
out("");
|
|
2872
|
+
out(" If it IS installed and listening, the probe used a name that does not");
|
|
2873
|
+
out(" exist — the proxy can only serve a certificate for a real one. Name one:");
|
|
2874
|
+
out(" moshcode dns service --proxy-probe <a.real.name> --write");
|
|
2875
|
+
out("");
|
|
2876
|
+
}
|
|
2792
2877
|
|
|
2793
2878
|
if (!upstreams.length) {
|
|
2794
2879
|
out("! no upstream nameservers found, and none given");
|