nixamp 0.7.3 → 0.7.4
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/dist/admin.js +7 -2
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.js +30 -1
- package/package.json +1 -1
- package/src/admin.ts +6 -2
- package/src/daemon.ts +31 -1
- package/web/dist/sw.js +1 -1
package/dist/admin.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* or against a nixamp on a different machine entirely.
|
|
7
7
|
*/
|
|
8
8
|
import { createApp, themes } from "@profullstack/hqtui";
|
|
9
|
-
import { daemonUrl, readState } from "./daemon.js";
|
|
9
|
+
import { daemonUrl, isLoopbackTls, readState } from "./daemon.js";
|
|
10
10
|
import { KEY_HEADER } from "./share.js";
|
|
11
11
|
/** Where to point, from the flags or from the daemon that is running. */
|
|
12
12
|
export function resolveTarget(argv) {
|
|
@@ -23,8 +23,13 @@ export function resolveTarget(argv) {
|
|
|
23
23
|
if (state === null) {
|
|
24
24
|
throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
|
|
25
25
|
}
|
|
26
|
+
const url_ = daemonUrl(state);
|
|
27
|
+
// Talking to our own daemon, whose certificate names somewhere else. Nothing
|
|
28
|
+
// is in the way on loopback, so there is nothing for a certificate to prove.
|
|
29
|
+
if (isLoopbackTls(url_))
|
|
30
|
+
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
|
|
26
31
|
return {
|
|
27
|
-
url:
|
|
32
|
+
url: url_,
|
|
28
33
|
key: key ?? state.key,
|
|
29
34
|
// A state file written before 0.5.3 has no list; loopback stands in.
|
|
30
35
|
links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
|
package/dist/daemon.d.ts
CHANGED
|
@@ -48,6 +48,16 @@ export declare function status(): {
|
|
|
48
48
|
};
|
|
49
49
|
/** The URL an admin client should talk to. */
|
|
50
50
|
export declare function daemonUrl(state: DaemonState): string;
|
|
51
|
+
/**
|
|
52
|
+
* Whether this address is our own machine over TLS with a certificate that
|
|
53
|
+
* cannot possibly name it.
|
|
54
|
+
*
|
|
55
|
+
* A certificate proves you reached the host you asked for. Asking for loopback
|
|
56
|
+
* proves that already: nothing is in the way to impersonate. So a daemon with a
|
|
57
|
+
* certificate for some public name is still reachable at 127.0.0.1, and a
|
|
58
|
+
* client refusing to talk to it is protecting nobody from anything.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isLoopbackTls(url: string): boolean;
|
|
51
61
|
/**
|
|
52
62
|
* What `nixamp daemon start` and `nixamp daemon status` print, as lines, so it
|
|
53
63
|
* can be tested without starting a daemon.
|
package/dist/daemon.js
CHANGED
|
@@ -61,8 +61,37 @@ export function status() {
|
|
|
61
61
|
}
|
|
62
62
|
/** The URL an admin client should talk to. */
|
|
63
63
|
export function daemonUrl(state) {
|
|
64
|
+
// A daemon serving https has a certificate for a name, and loopback is not
|
|
65
|
+
// that name. Asking it for https://localhost fails verification even though
|
|
66
|
+
// it is the same process on the same machine, which is how turning TLS on
|
|
67
|
+
// silently broke `nixamp admin` and `nixamp attach`. Prefer the address it
|
|
68
|
+
// was told to publish, which is the one the certificate is actually for.
|
|
69
|
+
const told = state.urls?.find((entry) => entry.label === "on the internet" && entry.url.startsWith("https://"));
|
|
70
|
+
if (told)
|
|
71
|
+
return told.url;
|
|
72
|
+
const scheme = state.urls?.[0]?.url.startsWith("https://") ? "https" : "http";
|
|
64
73
|
const host = state.host === "0.0.0.0" || state.host === "::" ? "127.0.0.1" : state.host;
|
|
65
|
-
return
|
|
74
|
+
return `${scheme}://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whether this address is our own machine over TLS with a certificate that
|
|
78
|
+
* cannot possibly name it.
|
|
79
|
+
*
|
|
80
|
+
* A certificate proves you reached the host you asked for. Asking for loopback
|
|
81
|
+
* proves that already: nothing is in the way to impersonate. So a daemon with a
|
|
82
|
+
* certificate for some public name is still reachable at 127.0.0.1, and a
|
|
83
|
+
* client refusing to talk to it is protecting nobody from anything.
|
|
84
|
+
*/
|
|
85
|
+
export function isLoopbackTls(url) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = new URL(url);
|
|
88
|
+
if (parsed.protocol !== "https:")
|
|
89
|
+
return false;
|
|
90
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1";
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
66
95
|
}
|
|
67
96
|
/**
|
|
68
97
|
* How long it has been up, as a person would say it. Written here rather than
|
package/package.json
CHANGED
package/src/admin.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { createApp, themes, type Container, type KeyEvent, type Theme } from "@profullstack/hqtui";
|
|
9
9
|
import type { Color } from "@profullstack/hqtui";
|
|
10
10
|
import type { Connection } from "./connections.ts";
|
|
11
|
-
import { daemonUrl, readState } from "./daemon.ts";
|
|
11
|
+
import { daemonUrl, isLoopbackTls, readState } from "./daemon.ts";
|
|
12
12
|
import { KEY_HEADER } from "./share.ts";
|
|
13
13
|
|
|
14
14
|
interface Report {
|
|
@@ -59,8 +59,12 @@ export function resolveTarget(argv: string[]): AdminOptions {
|
|
|
59
59
|
if (state === null) {
|
|
60
60
|
throw new Error("nixamp: no daemon is running. Start one with `nixamp daemon start`, or pass --url.");
|
|
61
61
|
}
|
|
62
|
+
const url_ = daemonUrl(state);
|
|
63
|
+
// Talking to our own daemon, whose certificate names somewhere else. Nothing
|
|
64
|
+
// is in the way on loopback, so there is nothing for a certificate to prove.
|
|
65
|
+
if (isLoopbackTls(url_)) process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0";
|
|
62
66
|
return {
|
|
63
|
-
url:
|
|
67
|
+
url: url_,
|
|
64
68
|
key: key ?? state.key,
|
|
65
69
|
// A state file written before 0.5.3 has no list; loopback stands in.
|
|
66
70
|
links: state.urls ?? [{ label: "here", url: daemonUrl(state) }],
|
package/src/daemon.ts
CHANGED
|
@@ -95,8 +95,38 @@ export function status(): { running: boolean; state: DaemonState | null } {
|
|
|
95
95
|
|
|
96
96
|
/** The URL an admin client should talk to. */
|
|
97
97
|
export function daemonUrl(state: DaemonState): string {
|
|
98
|
+
// A daemon serving https has a certificate for a name, and loopback is not
|
|
99
|
+
// that name. Asking it for https://localhost fails verification even though
|
|
100
|
+
// it is the same process on the same machine, which is how turning TLS on
|
|
101
|
+
// silently broke `nixamp admin` and `nixamp attach`. Prefer the address it
|
|
102
|
+
// was told to publish, which is the one the certificate is actually for.
|
|
103
|
+
const told = state.urls?.find(
|
|
104
|
+
(entry) => entry.label === "on the internet" && entry.url.startsWith("https://"),
|
|
105
|
+
);
|
|
106
|
+
if (told) return told.url;
|
|
107
|
+
|
|
108
|
+
const scheme = state.urls?.[0]?.url.startsWith("https://") ? "https" : "http";
|
|
98
109
|
const host = state.host === "0.0.0.0" || state.host === "::" ? "127.0.0.1" : state.host;
|
|
99
|
-
return
|
|
110
|
+
return `${scheme}://${host.includes(":") ? `[${host}]` : host}:${state.port}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Whether this address is our own machine over TLS with a certificate that
|
|
115
|
+
* cannot possibly name it.
|
|
116
|
+
*
|
|
117
|
+
* A certificate proves you reached the host you asked for. Asking for loopback
|
|
118
|
+
* proves that already: nothing is in the way to impersonate. So a daemon with a
|
|
119
|
+
* certificate for some public name is still reachable at 127.0.0.1, and a
|
|
120
|
+
* client refusing to talk to it is protecting nobody from anything.
|
|
121
|
+
*/
|
|
122
|
+
export function isLoopbackTls(url: string): boolean {
|
|
123
|
+
try {
|
|
124
|
+
const parsed = new URL(url);
|
|
125
|
+
if (parsed.protocol !== "https:") return false;
|
|
126
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "::1";
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
100
130
|
}
|
|
101
131
|
|
|
102
132
|
/**
|
package/web/dist/sw.js
CHANGED