@camstack/system 1.2.438 → 1.2.440
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/addon-runner.js +743 -79
- package/dist/addon-runner.mjs +743 -79
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/{agent-https-BZIwS4xF.js → agent-https-DlpTMDqA.js} +252 -22
- package/dist/{agent-https-BPfHrI4c.mjs → agent-https-U0d4uvn2.mjs} +211 -23
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-settings-store.d.ts +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +77 -11
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +77 -12
- package/dist/builtins/core-blocks/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +197 -60
- package/dist/builtins/device-manager/device-manager.addon.mjs +196 -61
- package/dist/builtins/device-manager/device-removal-hooks.d.ts +0 -11
- package/dist/builtins/device-manager/device-tombstone-store.d.ts +12 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +29 -11
- package/dist/builtins/keychain/index.mjs +29 -11
- package/dist/builtins/keychain/keychain-service.d.ts +8 -0
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +2 -2
- package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
- package/dist/builtins/local-network/local-network.addon.js +7 -2
- package/dist/builtins/local-network/local-network.addon.mjs +7 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -7
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +69 -3
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +69 -3
- package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +31 -0
- package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +3 -3
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +4 -4
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +4 -4
- package/dist/builtins/system-config/system-config.addon.js +4 -4
- package/dist/builtins/system-config/system-config.addon.mjs +285 -285
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-DpHGCjdL.mjs → composition-sources-CS0D7imL.mjs} +1 -1
- package/dist/{composition-sources-DI3Z83vG.js → composition-sources-cBG8fBTu.js} +1 -1
- package/dist/{constants-DQK705Sb.mjs → constants-CIzPjw7L.mjs} +1 -1
- package/dist/{constants-wkcn5Ij3.js → constants-CRI3QzT4.js} +1 -1
- package/dist/{device-ca-DvIZjXIl.mjs → device-ca-DLKXYseS.mjs} +2 -2
- package/dist/{device-ca-BSvkXqyI.js → device-ca-DNHjts4_.js} +2 -2
- package/dist/{dist-Dc8GKC5H.mjs → dist-9fcsrFU3.mjs} +271 -32
- package/dist/{dist-GgG-7amw.js → dist-BKhlcYz6.js} +288 -31
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-CBLvq2oj.js → hub-mdns-ciao-3Zc9G9MS.js} +377 -69
- package/dist/{hub-mdns-ciao-DMlQTdPU.mjs → hub-mdns-ciao-Dy2YjRqC.mjs} +344 -72
- package/dist/index.js +372 -339
- package/dist/index.mjs +361 -341
- package/dist/kernel/addon-settings-store.d.ts +43 -0
- package/dist/kernel/config-manager.d.ts +0 -19
- package/dist/kernel/device-settings-store.d.ts +74 -0
- package/dist/kernel/legacy-blob-guard.d.ts +58 -0
- package/dist/kernel/moleculer/runner-addon-settings.d.ts +34 -0
- package/dist/kernel/moleculer/runner-device-settings.d.ts +39 -0
- package/dist/kernel/prefix-walk.d.ts +39 -0
- package/dist/kernel/sections-store.d.ts +20 -0
- package/dist/kernel/settings-door-view.d.ts +2 -2
- package/dist/kernel/settings-view-types.d.ts +5 -30
- package/dist/kernel/store-settings-view.d.ts +0 -3
- package/dist/{lib-NNxHSOIE.js → lib-8oZrELLm.js} +1 -1
- package/dist/{lib-BeGzVQcd.mjs → lib-DK38ompL.mjs} +1 -1
- package/dist/prefix-range-D_KueXbQ.js +27 -0
- package/dist/prefix-range-Dl0znowN.mjs +22 -0
- package/dist/prefix-walk-DAOBSyH1.mjs +88 -0
- package/dist/prefix-walk-Dv6MLZlM.js +99 -0
- package/dist/{restore-archive-CTRz8v9o.js → restore-archive-49aZwkp8.js} +1 -1
- package/dist/{restore-archive-DPg9duVY.mjs → restore-archive-DgvDecXM.mjs} +82 -82
- package/dist/{retired-settings-keys-Is0zO8gX.mjs → retired-settings-keys-Bhwe4ymb.mjs} +2 -2
- package/dist/{retired-settings-keys-Dr_4lfKu.js → retired-settings-keys-D_VwZiDt.js} +10 -4
- package/dist/tls/agent-https.d.ts +2 -2
- package/dist/tls/boot-tls.d.ts +6 -0
- package/dist/tls/cert-evaluation.d.ts +1 -1
- package/dist/tls/cert-issue.d.ts +5 -1
- package/dist/tls/cert-manager.d.ts +18 -0
- package/dist/tls/cert-name-constraints.d.ts +53 -0
- package/dist/tls/index.d.ts +5 -4
- package/dist/tls/tls-actions.d.ts +27 -7
- package/dist/tls/tls-status.d.ts +22 -0
- package/dist/tls/uploaded-cert.d.ts +36 -4
- package/dist/{wal-maintenance-Bx3lcVA7.mjs → wal-maintenance-BZGmF-3U.mjs} +0 -0
- package/dist/{wal-maintenance-mjB2i-VR.js → wal-maintenance-C041zn6V.js} +0 -0
- package/dist/{x509.es-r7Ht1thA.js → x509.es-C4cOo48M.js} +36 -0
- package/dist/{x509.es-CDvj67p1.mjs → x509.es-DK__ETIe.mjs} +1 -1
- package/package.json +7 -2
- package/dist/{custom-action-registry-Bf_YnagS.js → custom-action-registry-BKmoaqG0.js} +1 -1
- package/dist/{custom-action-registry-BQnbN0DP.mjs → custom-action-registry-CyUuhATc.mjs} +1 -1
package/dist/addon-utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_custom_action_registry = require("./custom-action-registry-
|
|
3
|
+
const require_custom_action_registry = require("./custom-action-registry-BKmoaqG0.js");
|
|
4
4
|
const require_snapshot_courtesy = require("./snapshot-courtesy-D_C5-nC0.js");
|
|
5
5
|
exports.COURTESY_DEFAULT_WIDTH = require_snapshot_courtesy.COURTESY_DEFAULT_WIDTH;
|
|
6
6
|
exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
|
package/dist/addon-utils.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as isModelDownloaded, S as isModelDownloadHttpError, _ as downloadFile, a as startEventLoopStallMonitor, f as ModelDownloadService, g as deleteModelFromDisk, h as collectModelFiles, i as attributeStall, l as parseRangeHeader, m as adoptModelRevisions, n as STALL_REPORT_MS, o as createFileDataPlaneHandler, p as ModelDownloadHttpError, r as STALL_TICK_MS, s as contentTypeFor, t as CustomActionRegistry, w as resolveFormatRevision, x as getModelFilePath, y as ensureModel } from "./custom-action-registry-
|
|
1
|
+
import { C as isModelDownloaded, S as isModelDownloadHttpError, _ as downloadFile, a as startEventLoopStallMonitor, f as ModelDownloadService, g as deleteModelFromDisk, h as collectModelFiles, i as attributeStall, l as parseRangeHeader, m as adoptModelRevisions, n as STALL_REPORT_MS, o as createFileDataPlaneHandler, p as ModelDownloadHttpError, r as STALL_TICK_MS, s as contentTypeFor, t as CustomActionRegistry, w as resolveFormatRevision, x as getModelFilePath, y as ensureModel } from "./custom-action-registry-CyUuhATc.mjs";
|
|
2
2
|
import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg, t as COURTESY_DEFAULT_WIDTH } from "./snapshot-courtesy-CyFs1M3I.mjs";
|
|
3
3
|
export { COURTESY_DEFAULT_WIDTH, CustomActionRegistry, ModelDownloadHttpError, ModelDownloadService, STALL_REPORT_MS, STALL_TICK_MS, adoptModelRevisions, attributeStall, collectModelFiles, contentTypeFor, courtesyCacheKey, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, getModelFilePath, isModelDownloadHttpError, isModelDownloaded, parseRangeHeader, renderCourtesyJpeg, renderCourtesyOverlayJpeg, resolveFormatRevision, startEventLoopStallMonitor };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_x509_es = require("./x509.es-
|
|
2
|
+
const require_x509_es = require("./x509.es-C4cOo48M.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let _camstack_types_node = require("@camstack/types/node");
|
|
5
5
|
let node_net = require("node:net");
|
|
@@ -101,6 +101,188 @@ function getProcessAgentHttpsNames() {
|
|
|
101
101
|
return value !== null && typeof value === "object" && typeof Reflect.get(value, "takenByOthers") === "function" ? value : null;
|
|
102
102
|
}
|
|
103
103
|
//#endregion
|
|
104
|
+
//#region src/tls/cert-name-constraints.ts
|
|
105
|
+
/**
|
|
106
|
+
* Name constraints of the hub's generated local CA (D873).
|
|
107
|
+
*
|
|
108
|
+
* An unconstrained private CA vouches for EVERY site on the internet to any
|
|
109
|
+
* device that trusts it, so a leaked CA key would let its holder impersonate
|
|
110
|
+
* a bank. The generated CA therefore carries a CRITICAL `nameConstraints`
|
|
111
|
+
* extension (RFC 5280 4.2.1.10) that limits what it can ever certify to the
|
|
112
|
+
* private address space and local-only names. A verifier that does not
|
|
113
|
+
* understand a critical extension must reject the certificate, so the
|
|
114
|
+
* constraint cannot be silently ignored by a conforming client.
|
|
115
|
+
*
|
|
116
|
+
* Pure: no filesystem, no clock. `cert-issue.ts` embeds the extension,
|
|
117
|
+
* `cert-manager.ts` filters leaf names through {@link permitsIdentity}.
|
|
118
|
+
*/
|
|
119
|
+
/** `id-ce-nameConstraints`. */
|
|
120
|
+
var NAME_CONSTRAINTS_OID = "2.5.29.30";
|
|
121
|
+
/** The address space the generated CA may certify. 100.64.0.0/10 is CGNAT, which Tailscale hands out. Loopback is for the hub's own `127.0.0.1` / `::1` SANs. */
|
|
122
|
+
var PERMITTED_IP_RANGES = [
|
|
123
|
+
"10.0.0.0/8",
|
|
124
|
+
"172.16.0.0/12",
|
|
125
|
+
"192.168.0.0/16",
|
|
126
|
+
"100.64.0.0/10",
|
|
127
|
+
"127.0.0.0/8",
|
|
128
|
+
"fc00::/7",
|
|
129
|
+
"::1/128"
|
|
130
|
+
];
|
|
131
|
+
/**
|
|
132
|
+
* Local-only DNS suffixes: none of them is delegated in public DNS. A leading
|
|
133
|
+
* dot permits every name under the suffix and not the suffix itself.
|
|
134
|
+
*/
|
|
135
|
+
var PERMITTED_DNS_SUFFIXES = [
|
|
136
|
+
".local",
|
|
137
|
+
".lan",
|
|
138
|
+
".home.arpa",
|
|
139
|
+
".internal",
|
|
140
|
+
".localdomain",
|
|
141
|
+
"localhost"
|
|
142
|
+
];
|
|
143
|
+
var LABEL_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/i;
|
|
144
|
+
/** A name that cannot collide with public DNS: a single label, or under a local-only suffix. */
|
|
145
|
+
function isLocalOnlyHostname(name) {
|
|
146
|
+
const lower = name.toLowerCase();
|
|
147
|
+
if (lower.length === 0 || lower.length > 253) return false;
|
|
148
|
+
if (!lower.includes(".")) return LABEL_RE.test(lower);
|
|
149
|
+
if (!PERMITTED_DNS_SUFFIXES.some((suffix) => suffix.startsWith(".") && lower.endsWith(suffix))) return false;
|
|
150
|
+
return lower.split(".").every((label) => LABEL_RE.test(label));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The constraint set for a new CA. `hostnames` are the hub's own local-only
|
|
154
|
+
* names at creation time (a single-label OS hostname has no suffix to hide
|
|
155
|
+
* under), added as exact entries; anything that could collide with public DNS
|
|
156
|
+
* is dropped here, never constrained in.
|
|
157
|
+
*/
|
|
158
|
+
function buildNameConstraintSet(hostnames) {
|
|
159
|
+
const dns = new Set(PERMITTED_DNS_SUFFIXES);
|
|
160
|
+
for (const name of hostnames) {
|
|
161
|
+
const lower = name.trim().toLowerCase();
|
|
162
|
+
if ((0, node_net.isIP)(lower) === 0 && isLocalOnlyHostname(lower)) dns.add(lower);
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
ipRanges: PERMITTED_IP_RANGES,
|
|
166
|
+
dnsNames: [...dns],
|
|
167
|
+
critical: true
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/** The `nameConstraints` extension for a CA: permitted subtrees only, marked critical. */
|
|
171
|
+
function createNameConstraintsExtension(set) {
|
|
172
|
+
const subtrees = [...set.ipRanges.map((range) => new require_x509_es.GeneralSubtree({ base: new require_x509_es.GeneralName({ iPAddress: range }) })), ...set.dnsNames.map((name) => new require_x509_es.GeneralSubtree({ base: new require_x509_es.GeneralName({ dNSName: name }) }))];
|
|
173
|
+
const value = require_x509_es.AsnConvert.serialize(new require_x509_es.NameConstraints({ permittedSubtrees: new require_x509_es.GeneralSubtrees(subtrees) }));
|
|
174
|
+
return new require_x509_es.Extension(NAME_CONSTRAINTS_OID, set.critical, value);
|
|
175
|
+
}
|
|
176
|
+
/** The CA's constraint set, or `null` when the certificate carries no `nameConstraints`. */
|
|
177
|
+
function readNameConstraints(cert) {
|
|
178
|
+
const ext = cert.extensions.find((e) => e.type === NAME_CONSTRAINTS_OID);
|
|
179
|
+
if (ext === void 0) return null;
|
|
180
|
+
const parsed = require_x509_es.AsnConvert.parse(ext.value, require_x509_es.NameConstraints);
|
|
181
|
+
const ipRanges = [];
|
|
182
|
+
const dnsNames = [];
|
|
183
|
+
for (const subtree of parsed.permittedSubtrees ?? []) {
|
|
184
|
+
if (subtree.base.iPAddress !== void 0) ipRanges.push(subtree.base.iPAddress);
|
|
185
|
+
if (subtree.base.dNSName !== void 0) dnsNames.push(subtree.base.dNSName.toLowerCase());
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
ipRanges,
|
|
189
|
+
dnsNames,
|
|
190
|
+
critical: ext.critical
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* True when a CA is safe to keep as the hub's anchor: constrained to a
|
|
195
|
+
* non-empty permitted set AND marked critical. A non-critical constraint is
|
|
196
|
+
* ignored by clients that do not know the extension, which is the same as none.
|
|
197
|
+
*/
|
|
198
|
+
function isConstrainedCa(cert) {
|
|
199
|
+
const set = readNameConstraints(cert);
|
|
200
|
+
return set !== null && set.critical && (set.ipRanges.length > 0 || set.dnsNames.length > 0);
|
|
201
|
+
}
|
|
202
|
+
function ipv4ToBigInt(address) {
|
|
203
|
+
return address.split(".").reduce((acc, octet) => acc << 8n | BigInt(Number(octet)), 0n);
|
|
204
|
+
}
|
|
205
|
+
function ipv6ToBigInt(address) {
|
|
206
|
+
const mapped = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(address);
|
|
207
|
+
let text = address;
|
|
208
|
+
if (mapped !== null) {
|
|
209
|
+
const v4 = ipv4ToBigInt(mapped[2] ?? "0.0.0.0");
|
|
210
|
+
text = `${mapped[1] ?? ""}${(v4 >> 16n & 65535n).toString(16)}:${(v4 & 65535n).toString(16)}`;
|
|
211
|
+
}
|
|
212
|
+
const halves = text.split("::");
|
|
213
|
+
if (halves.length > 2) return null;
|
|
214
|
+
const head = (halves[0] ?? "") === "" ? [] : (halves[0] ?? "").split(":");
|
|
215
|
+
const tail = halves.length === 2 && (halves[1] ?? "") !== "" ? (halves[1] ?? "").split(":") : [];
|
|
216
|
+
const fill = halves.length === 2 ? 8 - head.length - tail.length : 0;
|
|
217
|
+
if (fill < 0 || halves.length === 1 && head.length !== 8) return null;
|
|
218
|
+
return [
|
|
219
|
+
...head,
|
|
220
|
+
...Array(fill).fill("0"),
|
|
221
|
+
...tail
|
|
222
|
+
].reduce((acc, group) => acc << 16n | BigInt(Number.parseInt(group, 16)), 0n);
|
|
223
|
+
}
|
|
224
|
+
function toBigInt(address) {
|
|
225
|
+
const family = (0, node_net.isIP)(address);
|
|
226
|
+
if (family === 4) return {
|
|
227
|
+
value: ipv4ToBigInt(address),
|
|
228
|
+
bits: 32
|
|
229
|
+
};
|
|
230
|
+
if (family === 6) {
|
|
231
|
+
const value = ipv6ToBigInt(address);
|
|
232
|
+
return value === null ? null : {
|
|
233
|
+
value,
|
|
234
|
+
bits: 128
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function ipInRange(address, range) {
|
|
240
|
+
const [base, prefixText] = range.split("/");
|
|
241
|
+
if (base === void 0 || prefixText === void 0) return false;
|
|
242
|
+
const target = toBigInt(address);
|
|
243
|
+
const net = toBigInt(base);
|
|
244
|
+
const prefix = Number(prefixText);
|
|
245
|
+
if (target === null || net === null || target.bits !== net.bits) return false;
|
|
246
|
+
if (!Number.isInteger(prefix) || prefix < 0 || prefix > net.bits) return false;
|
|
247
|
+
const shift = BigInt(net.bits - prefix);
|
|
248
|
+
return target.value >> shift === net.value >> shift;
|
|
249
|
+
}
|
|
250
|
+
/** RFC 5280 DNS matching: a leading dot is a strict subtree, a bare name is itself and everything under it. */
|
|
251
|
+
function dnsPermitted(name, constraint) {
|
|
252
|
+
const lower = name.toLowerCase();
|
|
253
|
+
if (constraint.startsWith(".")) return lower.endsWith(constraint);
|
|
254
|
+
return lower === constraint || lower.endsWith(`.${constraint}`);
|
|
255
|
+
}
|
|
256
|
+
/** Is this SAN value (an IP literal or a DNS name) inside the permitted set? */
|
|
257
|
+
function permitsName(set, value) {
|
|
258
|
+
if ((0, node_net.isIP)(value) !== 0) return set.ipRanges.some((range) => ipInRange(value, range));
|
|
259
|
+
return set.dnsNames.some((constraint) => dnsPermitted(value, constraint));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* The part of an identity the CA is allowed to certify. A leaf naming
|
|
263
|
+
* anything outside the constraints makes the WHOLE chain fail validation in
|
|
264
|
+
* OpenSSL, so such names are dropped (and reported), never signed. The
|
|
265
|
+
* required lists shrink the same way: otherwise a name the CA cannot certify
|
|
266
|
+
* would count as a permanent SAN gap and reissue the leaf on every boot.
|
|
267
|
+
*/
|
|
268
|
+
function permitsIdentity(set, identity) {
|
|
269
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
270
|
+
const keep = (values) => values.filter((value) => {
|
|
271
|
+
if (permitsName(set, value)) return true;
|
|
272
|
+
dropped.add(value);
|
|
273
|
+
return false;
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
identity: {
|
|
277
|
+
dnsNames: keep(identity.dnsNames),
|
|
278
|
+
ipAddresses: keep(identity.ipAddresses),
|
|
279
|
+
requiredDnsNames: keep(identity.requiredDnsNames),
|
|
280
|
+
requiredIpAddresses: keep(identity.requiredIpAddresses)
|
|
281
|
+
},
|
|
282
|
+
dropped: [...dropped]
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
//#endregion
|
|
104
286
|
//#region src/tls/cert-evaluation.ts
|
|
105
287
|
/**
|
|
106
288
|
* The regeneration rule (D227), as a pure function.
|
|
@@ -129,7 +311,8 @@ var REASONS_REQUIRING_NEW_CA = [
|
|
|
129
311
|
"missing",
|
|
130
312
|
"unreadable",
|
|
131
313
|
"no-local-ca",
|
|
132
|
-
"ca-expiring"
|
|
314
|
+
"ca-expiring",
|
|
315
|
+
"ca-unconstrained"
|
|
133
316
|
];
|
|
134
317
|
function reasonRequiresNewCa(reason) {
|
|
135
318
|
return REASONS_REQUIRING_NEW_CA.includes(reason);
|
|
@@ -141,6 +324,14 @@ function parse(pem) {
|
|
|
141
324
|
return null;
|
|
142
325
|
}
|
|
143
326
|
}
|
|
327
|
+
/** A root without a CRITICAL name constraint (every pre-D873 install) can vouch for any site. */
|
|
328
|
+
function caIsUnconstrained(caPem) {
|
|
329
|
+
try {
|
|
330
|
+
return !isConstrainedCa(new require_x509_es.X509Certificate(caPem));
|
|
331
|
+
} catch {
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
144
335
|
function daysBetween(from, to) {
|
|
145
336
|
return (to.getTime() - from.getTime()) / MS_PER_DAY$1;
|
|
146
337
|
}
|
|
@@ -171,6 +362,7 @@ function evaluateExistingCert(input) {
|
|
|
171
362
|
const ca = parse(input.caPem);
|
|
172
363
|
if (leaf === null || ca === null) return "unreadable";
|
|
173
364
|
if (daysBetween(input.now, new Date(ca.validTo)) < 427) return "ca-expiring";
|
|
365
|
+
if (caIsUnconstrained(input.caPem)) return "ca-unconstrained";
|
|
174
366
|
if (!keyMatches(leaf, input.keyPem)) return "key-mismatch";
|
|
175
367
|
if (daysBetween(input.now, new Date(leaf.validTo)) < 30) return "expiring";
|
|
176
368
|
if (!hasServerAuthEku(leaf)) return "missing-server-auth-eku";
|
|
@@ -383,13 +575,18 @@ function subjectAltNames(identity) {
|
|
|
383
575
|
* A local root: 10 years, `pathlen:0` so it can sign leaves and nothing
|
|
384
576
|
* else, and **no EKU** — Apple rejects an anchor asserting
|
|
385
577
|
* `anyExtendedKeyUsage`, and a root with no EKU constrains nothing.
|
|
578
|
+
*
|
|
579
|
+
* It carries a CRITICAL `nameConstraints` extension (D873): private address
|
|
580
|
+
* ranges and local-only DNS names, plus `hostnames` (the hub's own single-label
|
|
581
|
+
* names at creation). A leaked key therefore cannot vouch for a public site.
|
|
386
582
|
*/
|
|
387
|
-
async function generateCa(now) {
|
|
583
|
+
async function generateCa(now, hostnames = []) {
|
|
388
584
|
const keys = await node_crypto.webcrypto.subtle.generateKey(SIGNING_ALGORITHM, true, ["sign", "verify"]);
|
|
389
585
|
const extensions = [
|
|
390
586
|
new require_x509_es.BasicConstraintsExtension(true, 0, true),
|
|
391
587
|
new require_x509_es.KeyUsagesExtension(require_x509_es.KeyUsageFlags.keyCertSign | require_x509_es.KeyUsageFlags.cRLSign, true),
|
|
392
|
-
await require_x509_es.SubjectKeyIdentifierExtension.create(keys.publicKey)
|
|
588
|
+
await require_x509_es.SubjectKeyIdentifierExtension.create(keys.publicKey),
|
|
589
|
+
createNameConstraintsExtension(buildNameConstraintSet(hostnames))
|
|
393
590
|
];
|
|
394
591
|
const cert = await require_x509_es.X509CertificateGenerator.createSelfSigned({
|
|
395
592
|
serialNumber: freshSerial(),
|
|
@@ -652,14 +849,6 @@ function describeHttpsLeaf(certPem) {
|
|
|
652
849
|
notAfter: Date.parse(x509.validTo)
|
|
653
850
|
};
|
|
654
851
|
}
|
|
655
|
-
var LOCAL_SUFFIXES = [
|
|
656
|
-
".local",
|
|
657
|
-
".lan",
|
|
658
|
-
".home.arpa",
|
|
659
|
-
".internal",
|
|
660
|
-
".localdomain"
|
|
661
|
-
];
|
|
662
|
-
var LABEL_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/i;
|
|
663
852
|
/** Private-network IPv4/IPv6: RFC 1918, CGNAT (Tailscale), link-local, ULA. */
|
|
664
853
|
function isPrivateAddress(address) {
|
|
665
854
|
const family = (0, node_net.isIP)(address);
|
|
@@ -677,14 +866,6 @@ function isPrivateAddress(address) {
|
|
|
677
866
|
function isContainerBridgeAddress(address) {
|
|
678
867
|
return /^172\.17\.\d{1,3}\.\d{1,3}$/.test(address);
|
|
679
868
|
}
|
|
680
|
-
/** A name that cannot collide with public DNS. */
|
|
681
|
-
function isLocalOnlyHostname(name) {
|
|
682
|
-
const lower = name.toLowerCase();
|
|
683
|
-
if (lower.length === 0 || lower.length > 253) return false;
|
|
684
|
-
if (!lower.includes(".")) return LABEL_RE.test(lower);
|
|
685
|
-
if (!LOCAL_SUFFIXES.some((suffix) => lower.endsWith(suffix))) return false;
|
|
686
|
-
return lower.split(".").every((label) => LABEL_RE.test(label));
|
|
687
|
-
}
|
|
688
869
|
/**
|
|
689
870
|
* The names the hub will sign for `nodeId`: its node id and the address of its live mesh
|
|
690
871
|
* socket - nothing the agent says about itself. `excluded` holds every name already taken
|
|
@@ -737,14 +918,21 @@ async function issueAgentHttpsLeaf(input) {
|
|
|
737
918
|
if (!(0, node_fs.existsSync)(caCertPath) || !(0, node_fs.existsSync)(caKeyPath)) throw new Error("the hub has no local CA to sign with (no tls/camstack-ca.crt + key)");
|
|
738
919
|
const excluded = new Set(input.takenByOthers ?? []);
|
|
739
920
|
if ((0, node_fs.existsSync)(hubLeafPath)) for (const name of certificateNames((0, node_fs.readFileSync)(hubLeafPath, "utf8"))) excluded.add(name);
|
|
740
|
-
const
|
|
921
|
+
const ca = await loadCa((0, node_fs.readFileSync)(caCertPath, "utf8"), (0, node_fs.readFileSync)(caKeyPath, "utf8"));
|
|
922
|
+
const policy = acceptAgentSans({
|
|
741
923
|
nodeId: input.nodeId,
|
|
742
924
|
observedAddress: input.observedAddress,
|
|
743
925
|
excluded,
|
|
744
926
|
...input.reportedNames !== void 0 ? { reported: input.reportedNames } : {}
|
|
745
927
|
});
|
|
928
|
+
const constraints = readNameConstraints(ca.cert);
|
|
929
|
+
const constrained = constraints === null ? null : permitsIdentity(constraints, policy.identity);
|
|
930
|
+
const sans = constrained === null ? policy : {
|
|
931
|
+
identity: constrained.identity,
|
|
932
|
+
accepted: policy.accepted.filter((name) => !constrained.dropped.includes(name)),
|
|
933
|
+
rejected: [...policy.rejected, ...constrained.dropped.map((name) => `${name} (outside the local CA's name constraints)`)]
|
|
934
|
+
};
|
|
746
935
|
if (sans.accepted.length === 0) throw new Error(`no name of node '${input.nodeId}' can be certified (${sans.rejected.join("; ") || "nothing offered"})`);
|
|
747
|
-
const ca = await loadCa((0, node_fs.readFileSync)(caCertPath, "utf8"), (0, node_fs.readFileSync)(caKeyPath, "utf8"));
|
|
748
936
|
return {
|
|
749
937
|
certPem: await issueLeafFromCsr(ca, input.csrPem, sans.identity, input.nodeId, 397, input.now ?? /* @__PURE__ */ new Date()),
|
|
750
938
|
caCertPem: ca.certPem,
|
|
@@ -805,6 +993,18 @@ Object.defineProperty(exports, "MAX_LEAF_VALIDITY_DAYS", {
|
|
|
805
993
|
return MAX_LEAF_VALIDITY_DAYS;
|
|
806
994
|
}
|
|
807
995
|
});
|
|
996
|
+
Object.defineProperty(exports, "PERMITTED_DNS_SUFFIXES", {
|
|
997
|
+
enumerable: true,
|
|
998
|
+
get: function() {
|
|
999
|
+
return PERMITTED_DNS_SUFFIXES;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
Object.defineProperty(exports, "PERMITTED_IP_RANGES", {
|
|
1003
|
+
enumerable: true,
|
|
1004
|
+
get: function() {
|
|
1005
|
+
return PERMITTED_IP_RANGES;
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
808
1008
|
Object.defineProperty(exports, "SERVER_AUTH_OID", {
|
|
809
1009
|
enumerable: true,
|
|
810
1010
|
get: function() {
|
|
@@ -823,6 +1023,12 @@ Object.defineProperty(exports, "agentHttpsPaths", {
|
|
|
823
1023
|
return agentHttpsPaths;
|
|
824
1024
|
}
|
|
825
1025
|
});
|
|
1026
|
+
Object.defineProperty(exports, "buildNameConstraintSet", {
|
|
1027
|
+
enumerable: true,
|
|
1028
|
+
get: function() {
|
|
1029
|
+
return buildNameConstraintSet;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
826
1032
|
Object.defineProperty(exports, "certificateNames", {
|
|
827
1033
|
enumerable: true,
|
|
828
1034
|
get: function() {
|
|
@@ -877,6 +1083,12 @@ Object.defineProperty(exports, "installAgentHttps", {
|
|
|
877
1083
|
return installAgentHttps;
|
|
878
1084
|
}
|
|
879
1085
|
});
|
|
1086
|
+
Object.defineProperty(exports, "isConstrainedCa", {
|
|
1087
|
+
enumerable: true,
|
|
1088
|
+
get: function() {
|
|
1089
|
+
return isConstrainedCa;
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
880
1092
|
Object.defineProperty(exports, "isContainerBridgeAddress", {
|
|
881
1093
|
enumerable: true,
|
|
882
1094
|
get: function() {
|
|
@@ -925,12 +1137,30 @@ Object.defineProperty(exports, "pemDigest", {
|
|
|
925
1137
|
return pemDigest;
|
|
926
1138
|
}
|
|
927
1139
|
});
|
|
1140
|
+
Object.defineProperty(exports, "permitsIdentity", {
|
|
1141
|
+
enumerable: true,
|
|
1142
|
+
get: function() {
|
|
1143
|
+
return permitsIdentity;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
Object.defineProperty(exports, "permitsName", {
|
|
1147
|
+
enumerable: true,
|
|
1148
|
+
get: function() {
|
|
1149
|
+
return permitsName;
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
928
1152
|
Object.defineProperty(exports, "pinnedHttpsCaFingerprint", {
|
|
929
1153
|
enumerable: true,
|
|
930
1154
|
get: function() {
|
|
931
1155
|
return pinnedHttpsCaFingerprint;
|
|
932
1156
|
}
|
|
933
1157
|
});
|
|
1158
|
+
Object.defineProperty(exports, "readNameConstraints", {
|
|
1159
|
+
enumerable: true,
|
|
1160
|
+
get: function() {
|
|
1161
|
+
return readNameConstraints;
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
934
1164
|
Object.defineProperty(exports, "reasonRequiresNewCa", {
|
|
935
1165
|
enumerable: true,
|
|
936
1166
|
get: function() {
|