@camstack/system 1.2.419 → 1.2.420

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.
@@ -1,5 +1,5 @@
1
1
  import { Pn as storageOccupancyCapability, Y as backupCapability, Zr as toNodeId, cr as BaseAddon, ei as EventCategory, en as mayWriteToLocation, zr as nodePin } from "../../dist-C0wYvAHC.mjs";
2
- import { a as BackupSnapshotRefusedError, c as BackupKeyFormatError, d as generateBackupKeyPair, f as parseBackupPublicKey, i as BackupCancelledError, n as precheckRestoreKey, o as SystemBackupService, p as parseBackupSecretKey, r as prepareRestoreArchive, u as ENCRYPTED_BACKUP_SUFFIX } from "../../restore-archive-gGHgAZsz.mjs";
2
+ import { a as BackupSnapshotRefusedError, c as BackupKeyFormatError, d as generateBackupKeyPair, f as parseBackupPublicKey, i as BackupCancelledError, n as precheckRestoreKey, o as SystemBackupService, p as parseBackupSecretKey, r as prepareRestoreArchive, u as ENCRYPTED_BACKUP_SUFFIX } from "../../restore-archive-Bt65Em24.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import * as fsp from "node:fs/promises";
5
5
  import * as path$1 from "node:path";
@@ -86720,15 +86720,66 @@ import_moleculer.default.Validators;
86720
86720
  //#endregion
86721
86721
  //#region src/kernel/moleculer/mesh-mtls/tls-tcp-transporter.ts
86722
86722
  /**
86723
- * Moleculer TCP transporter with a mutual-TLS listener (D826).
86723
+ * Moleculer TCP transporter with a mutual-TLS listener (D826, D848).
86724
86724
  *
86725
86725
  * Hub: `hubTlsOnly` — no clear TCP reader and no UDP discovery; TLS 1.3 on
86726
- * `tlsPort` (default 6443) once mesh material is ready. Agents without mesh
86727
- * material use clear TCP on their own listen port and dial the hub on TLS when
86728
- * enrolled. On a TLS socket, any packet whose `sender` ≠ the peer certificate CN
86729
- * is dropped.
86726
+ * `tlsPort` (default 6443) once mesh material is ready, and every peer is
86727
+ * dialled on mutual TLS too (a TLS-only hub has no clear peer to talk to).
86728
+ *
86729
+ * Enrolled agent: `hubOnlyPeers` (D848) — the hub is its ONLY mesh peer. No
86730
+ * clear TCP reader; the TLS listener accepts the hub's certificate only; the
86731
+ * writer dials the hub only; and the nodes the hub's gossip names are not
86732
+ * adopted, so the agent never learns, dials or broadcasts to another agent.
86733
+ * An agent without mesh material keeps clear TCP (it can reach only a
86734
+ * pre-D826 hub).
86735
+ *
86736
+ * On a TLS socket, any packet whose `sender` ≠ the peer certificate CN is
86737
+ * dropped. A TLS-listening node advertises its listen port in the gossip
86738
+ * HELLO it sends on every connection (`meshTlsPort`,
86739
+ * {@link MESH_TLS_PORT_HELLO_FIELD}); a peer dials it there, else on the
86740
+ * cluster-wide default (6443) — so two nodes on one host need not collide.
86730
86741
  */
86742
+ /** Field of the gossip HELLO payload carrying the sender's mesh-TLS listen port. */
86743
+ var MESH_TLS_PORT_HELLO_FIELD = "meshTlsPort";
86744
+ /** Moleculer packet types (`packets.PACKET_GOSSIP_*`). */
86745
+ var PACKET_GOSSIP_RES = "GOSSIP_RES";
86746
+ var PACKET_GOSSIP_HELLO = "GOSSIP_HELLO";
86747
+ /** The hub's node id — the only peer of a `hubOnlyPeers` agent. */
86748
+ var DEFAULT_HUB_NODE_ID = "hub";
86731
86749
  var TcpTransporterBase = Transporters.TCP;
86750
+ /** The mesh-TLS port a gossip HELLO payload advertises, or null. */
86751
+ function helloMeshTlsPort(payload) {
86752
+ if (payload === null || typeof payload !== "object") return null;
86753
+ const port = Reflect.get(payload, MESH_TLS_PORT_HELLO_FIELD);
86754
+ return typeof port === "number" && Number.isInteger(port) && port > 0 && port < 65536 ? port : null;
86755
+ }
86756
+ /**
86757
+ * A gossip response with every node but `keep` removed. With nothing to
86758
+ * remove the payload is returned unchanged.
86759
+ */
86760
+ function keepOnlyGossipNodes(payload, keep) {
86761
+ if (payload === null || typeof payload !== "object") return {
86762
+ payload,
86763
+ removed: []
86764
+ };
86765
+ const removed = [];
86766
+ const filtered = { ...payload };
86767
+ for (const section of ["online", "offline"]) {
86768
+ const entries = Reflect.get(payload, section);
86769
+ if (entries === null || typeof entries !== "object") continue;
86770
+ const kept = {};
86771
+ for (const [nodeId, value] of Object.entries(entries)) if (keep.has(nodeId)) kept[nodeId] = value;
86772
+ else removed.push(nodeId);
86773
+ filtered[section] = kept;
86774
+ }
86775
+ return removed.length === 0 ? {
86776
+ payload,
86777
+ removed
86778
+ } : {
86779
+ payload: filtered,
86780
+ removed
86781
+ };
86782
+ }
86732
86783
  /**
86733
86784
  * Extract `sender` from a Moleculer TCP packet without fully trusting it —
86734
86785
  * used only to compare against the TLS peer CN.
@@ -86770,6 +86821,13 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86770
86821
  tlsPeers = /* @__PURE__ */ new Map();
86771
86822
  clearDisabled = false;
86772
86823
  hubTlsOnly;
86824
+ hubOnlyPeers;
86825
+ hubNodeId;
86826
+ peerTlsPortOption;
86827
+ /** Peers already reported as not adopted (D848) — one line each. */
86828
+ ignoredPeers = /* @__PURE__ */ new Set();
86829
+ /** Node id → the mesh-TLS port its last HELLO advertised. */
86830
+ helloTlsPorts = /* @__PURE__ */ new Map();
86773
86831
  /** Clear-text `TcpWriter.connect` captured once before the TLS wrap (A→B). */
86774
86832
  clearWriterConnect = null;
86775
86833
  writerTlsPatched = false;
@@ -86778,8 +86836,15 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86778
86836
  this.meshTls = opts.meshTls ?? null;
86779
86837
  this.crl = opts.crl ?? processMeshCrl();
86780
86838
  this.hubTlsOnly = opts.hubTlsOnly === true;
86839
+ this.hubOnlyPeers = opts.hubOnlyPeers === true;
86840
+ this.hubNodeId = opts.hubNodeId ?? DEFAULT_HUB_NODE_ID;
86841
+ this.peerTlsPortOption = opts.peerTlsPort;
86781
86842
  if (this.opts.tlsPort === void 0) this.opts.tlsPort = require_constants$8.DEFAULT_HUB_TLS_TCP_PORT;
86782
86843
  }
86844
+ /** Clear TCP is never served: the TLS-only hub, or an agent whose only peer is the hub. */
86845
+ get tlsOnly() {
86846
+ return this.hubTlsOnly || this.hubOnlyPeers;
86847
+ }
86783
86848
  startTcpServer() {
86784
86849
  const started = super.startTcpServer();
86785
86850
  if (this.meshTls !== null) this.patchWriterForTlsClient();
@@ -86789,8 +86854,8 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86789
86854
  return this.Promise.resolve().then(() => {
86790
86855
  if (this.opts.urls) return this.loadUrls();
86791
86856
  }).then(() => this.startTcpServer()).then(() => {
86792
- if (this.hubTlsOnly) this.disableClearTransport();
86793
- }).then(() => this.startTlsServerIfReady()).then(() => this.hubTlsOnly ? this.Promise.resolve() : this.startUdpServer()).then(() => this.startTimers()).then(() => {
86857
+ if (this.tlsOnly) this.disableClearTransport();
86858
+ }).then(() => this.startTlsServerIfReady()).then(() => this.tlsOnly ? this.Promise.resolve() : this.startUdpServer()).then(() => this.startTimers()).then(() => {
86794
86859
  this.logger.info("TCP Transporter started.");
86795
86860
  this.connected = true;
86796
86861
  this.nodes.localNode.port = this.opts.port;
@@ -86798,17 +86863,19 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86798
86863
  }).then(() => this.onConnected());
86799
86864
  }
86800
86865
  /**
86801
- * Outbound dial uses mTLS when the peer is already known on TLS (inbound
86802
- * handshake), or when dialling the hub node id (agent → hub). Everyone else
86803
- * (runners, unenrolled agents) stays on the clear writer — the hub must not
86804
- * TLS-wrap every `connect` after deferred `enableMeshTls`.
86866
+ * Outbound dial uses mTLS when dialling the hub node id (agent → hub), on a
86867
+ * TLS-only hub (it has no clear listener, so no clear peer exists to dial —
86868
+ * D848 closes the hub → agent clear leg), or when the peer is already known
86869
+ * on TLS (inbound handshake).
86805
86870
  */
86806
86871
  shouldDialPeerWithTls(nodeID) {
86807
- if (nodeID === "hub") return true;
86872
+ if (nodeID === this.hubNodeId) return true;
86873
+ if (this.hubTlsOnly) return true;
86808
86874
  return this.tlsPeers.has(nodeID);
86809
86875
  }
86810
- peerTlsDialPort() {
86811
- return this.opts.tlsPort ?? 6443;
86876
+ /** The peer's advertised mesh-TLS port, else the configured dial port, else 6443. */
86877
+ peerTlsDialPort(nodeID) {
86878
+ return this.helloTlsPorts.get(nodeID) ?? this.peerTlsPortOption ?? this.opts.tlsPort ?? 6443;
86812
86879
  }
86813
86880
  /**
86814
86881
  * Wrap `TcpWriter.connect` so TLS peers are dialed with mutual TLS on the
@@ -86828,13 +86895,14 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86828
86895
  const crl = this.crl;
86829
86896
  const self = this;
86830
86897
  writer.connect = (nodeID) => {
86898
+ if (self.hubOnlyPeers && nodeID !== self.hubNodeId) return writer.Promise.reject(/* @__PURE__ */ new Error(`mesh: an enrolled agent dials only "${self.hubNodeId}", not "${nodeID}"`));
86831
86899
  if (!self.shouldDialPeerWithTls(nodeID)) return clearConnect(nodeID);
86832
86900
  const material = self.meshTls;
86833
86901
  if (material === null) return clearConnect(nodeID);
86834
86902
  const node = writer.transporter.getNode(nodeID);
86835
86903
  if (!node) return writer.Promise.reject(/* @__PURE__ */ new Error(`Missing node info for '${nodeID}'!`));
86836
86904
  const host = writer.transporter.getNodeAddress(node);
86837
- const port = self.peerTlsDialPort();
86905
+ const port = self.peerTlsDialPort(nodeID);
86838
86906
  writer.logger.debug(`Connecting (TLS) to '${nodeID}' via ${host}:${port}`);
86839
86907
  return new writer.Promise((resolve, reject) => {
86840
86908
  const socket = node_tls.connect({
@@ -86940,7 +87008,7 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86940
87008
  }
86941
87009
  /** The switch back: listen on the clear port again and restart UDP discovery. Idempotent. */
86942
87010
  async enableClearTransport() {
86943
- if (this.hubTlsOnly) return;
87011
+ if (this.tlsOnly) return;
86944
87012
  if (!this.clearDisabled) return;
86945
87013
  this.clearDisabled = false;
86946
87014
  if (this.reader !== null && this.reader.server?.listening !== true) await this.reader.listen();
@@ -86972,7 +87040,53 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
86972
87040
  this.tlsPeers.clear();
86973
87041
  return super.disconnect();
86974
87042
  }
87043
+ /**
87044
+ * D848: an enrolled agent adopts no node from the hub's gossip but the hub
87045
+ * itself, so it never learns — and therefore never dials or broadcasts to —
87046
+ * another agent. Everything else is Moleculer's own handling.
87047
+ */
87048
+ receive(cmd, data, socket) {
87049
+ if (cmd === PACKET_GOSSIP_HELLO) this.noteHelloTlsPort(data);
87050
+ if (!this.hubOnlyPeers || cmd !== PACKET_GOSSIP_RES) return super.receive(cmd, data, socket);
87051
+ let payload;
87052
+ try {
87053
+ payload = this.broker.serializer.deserialize(data, cmd);
87054
+ } catch {
87055
+ return super.receive(cmd, data, socket);
87056
+ }
87057
+ const filtered = keepOnlyGossipNodes(payload, new Set([this.hubNodeId, this.nodeID]));
87058
+ if (filtered.removed.length === 0) return super.receive(cmd, data, socket);
87059
+ for (const nodeId of filtered.removed) {
87060
+ if (this.ignoredPeers.has(nodeId)) continue;
87061
+ this.ignoredPeers.add(nodeId);
87062
+ this.logger.info(`Mesh: not adopting node '${nodeId}' from the hub's gossip — an enrolled agent's only peer is the hub (D848)`);
87063
+ }
87064
+ return super.receive(cmd, this.broker.serializer.serialize(filtered.payload, cmd), socket);
87065
+ }
87066
+ /** Remember the TLS port a peer's HELLO advertises (absent: an older node → the default). */
87067
+ noteHelloTlsPort(data) {
87068
+ let payload;
87069
+ try {
87070
+ payload = this.broker.serializer.deserialize(data, PACKET_GOSSIP_HELLO);
87071
+ } catch {
87072
+ return;
87073
+ }
87074
+ const sender = payload !== null && typeof payload === "object" ? Reflect.get(payload, "sender") : null;
87075
+ if (typeof sender !== "string") return;
87076
+ const port = helloMeshTlsPort(payload);
87077
+ if (port === null) this.helloTlsPorts.delete(sender);
87078
+ else this.helloTlsPorts.set(sender, port);
87079
+ }
87080
+ /** A TLS-listening node says where in every HELLO it sends. */
87081
+ publish(packet) {
87082
+ if (packet.type === PACKET_GOSSIP_HELLO && this.tlsServer !== null) packet.payload[MESH_TLS_PORT_HELLO_FIELD] = this.opts.tlsPort;
87083
+ return super.publish(packet);
87084
+ }
86975
87085
  onIncomingMessage(type, message, socket) {
87086
+ if (this.hubOnlyPeers && (socket === void 0 || !isTlsSocket(socket))) {
87087
+ this.logger.warn("Dropping mesh packet — an enrolled agent accepts TLS from the hub only");
87088
+ return;
87089
+ }
86976
87090
  if (socket !== void 0 && isTlsSocket(socket)) {
86977
87091
  const tlsSock = socket;
86978
87092
  const identity = tlsSock.meshPeerCn ? {
@@ -87043,6 +87157,11 @@ var TlsTcpTransporter = class extends TcpTransporterBase {
87043
87157
  socket.destroy();
87044
87158
  return;
87045
87159
  }
87160
+ if (this.hubOnlyPeers && identity.cn !== this.hubNodeId) {
87161
+ this.logger.warn(`Mesh TLS client rejected — an enrolled agent accepts only '${this.hubNodeId}', not CN=${identity.cn} (D848)`);
87162
+ socket.destroy();
87163
+ return;
87164
+ }
87046
87165
  socket.meshPeerCn = identity.cn;
87047
87166
  socket.meshPeerSerial = identity.serial;
87048
87167
  this.tlsSockets.add(socket);
@@ -87745,6 +87864,7 @@ exports.AGENT_UI_UNLOCK_VERSION = AGENT_UI_UNLOCK_VERSION;
87745
87864
  exports.Errors = Errors;
87746
87865
  exports.JoinTokenJtiStore = JoinTokenJtiStore;
87747
87866
  exports.MESH_AUTH_MODE_FIELD = MESH_AUTH_MODE_FIELD;
87867
+ exports.MESH_TLS_PORT_HELLO_FIELD = MESH_TLS_PORT_HELLO_FIELD;
87748
87868
  exports.MeshCaKeeper = MeshCaKeeper;
87749
87869
  exports.MeshCrl = MeshCrl;
87750
87870
  exports.SYSTEM_CONFIG_CLUSTER_SECTION_ID = SYSTEM_CONFIG_CLUSTER_SECTION_ID;