ops(meshcentral): diagnose and fix HW Connect stuck at Setup (stale CIRA tunnel); add relay probe

This commit is contained in:
vh
2026-10-02 22:49:27 -07:00
parent 55004090d1
commit ad0322bf83
3 changed files with 64 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
// Probe an Intel AMT device through MeshCentral's web relay (webrelay.ashx), which is the same path the
// browser's "HW Connect" (KVM), SOL, IDE-R and the embedded MeshCommander use.
// KVMR | SOL | IDER : StartRedirectionSession, then the authentication-methods query. It never
// authenticates, so no session is opened on the target.
// HTTP : one empty WS-Man POST (any HTTP reply proves channel + TLS + auth injection).
// A healthy device answers in well under a second. Silence until the timeout means the relay channel is dead.
// Seen 2026-10-02: a stale second CIRA tunnel (servers/pfi-tacticalrmm/README.md).
//
// Runs ON pfi-tacticalrmm as `tactical` (needs MeshCentral's `ws` module). stdin lines: login user, login pass,
// node id, mode. Nothing secret is in this file. Example (from eshpfi-management on nh3-dev):
// scp scripts/meshcentral-amt-relay-probe.js infra-ops@10.250.50.57:/tmp/p.js
// { secret get pfi-tacticalrmm/meshcentral-login-token | python3 -c "import sys; f=sys.stdin.read().split(); print(f[1]); print(f[3])"
// printf '%s\n%s\n' '<node id>' KVMR; } | ssh infra-ops@10.250.50.57 'sudo -n -u tactical node /tmp/p.js; rm -f /tmp/p.js'
// Always run a known-good device as a positive control next to the one under suspicion.
const [U, P, NODE, MODE] = require("fs").readFileSync(0, "utf8").split("\n");
const WebSocket = require("/meshcentral/node_modules/ws");
const t0 = Date.now();
const log = (...a) => console.log(`[+${((Date.now() - t0) / 1000).toFixed(1)}s]`, ...a);
const start = {
KVMR: [0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52],
SOL: [0x10, 0x00, 0x00, 0x00, 0x53, 0x4f, 0x4c, 0x20],
IDER: [0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52],
}[MODE.trim()];
const HTTP = MODE.trim() === "HTTP";
const url = "wss://rmm-mesh.phasefinal.com/webrelay.ashx?p=" + (HTTP ? 1 : 2) + "&host=" + encodeURIComponent(NODE.trim()) + "&port=" + (HTTP ? 16993 : 16995) + "&tls=1&tls1only=0";
const ws = new WebSocket(url, { headers: { "x-meshauth": Buffer.from(U).toString("base64") + "," + Buffer.from(P).toString("base64") } });
ws.on("open", () => {
if (HTTP) { log("relay websocket open (p=1); sending HTTP POST /wsman"); ws.send(Buffer.from("POST /wsman HTTP/1.1\r\nHost: amt:16993\r\nContent-Type: application/soap+xml\r\nContent-Length: 0\r\n\r\n")); return; }
log("relay websocket open; sending StartRedirectionSession", MODE.trim()); ws.send(Buffer.from(start));
});
ws.on("message", (d, isBinary) => {
const s = isBinary ? d.toString("latin1") : d.toString("utf8");
if (HTTP) { log("AMT HTTP ->", JSON.stringify(s.split("\r\n").slice(0, 3))); ws.close(); return; }
const b = Array.from(s, (c) => c.charCodeAt(0));
log("AMT ->", b.length, "bytes:", b.slice(0, 24).map((x) => x.toString(16).padStart(2, "0")).join(" "));
if (b[0] === 0x11) {
log("StartRedirectionSessionReply status", b[1], b[1] === 0 ? "(success)" : "(FAIL)");
if (b[1] === 0) ws.send(Buffer.from([0x13, 0, 0, 0, 0, 0, 0, 0, 0]));
} else if (b[0] === 0x14) {
log("AuthenticateSessionReply status", b[1], "auth types offered:", b.slice(9).join(","), "(4=digest, 3=kerberos? per Intel)");
ws.close();
}
});
ws.on("close", (c, r) => { log("closed", c, String(r)); process.exit(0); });
ws.on("error", (e) => log("error", e.message));
setTimeout(() => { log("TIMEOUT: no further reply in 25 s"); process.exit(0); }, 25000);
+6
View File
@@ -56,5 +56,11 @@ after). Address disks by `/dev/disk/by-id/…` (serial), never `nvmeXn1`.
two keep sharing one IP. two keep sharing one IP.
- Power schemes offered: "Mobile: ON in S0" and "ON in S0, ME Wake in S3, S4-5 (AC only)". Not checked which is - Power schemes offered: "Mobile: ON in S0" and "ON in S0, ME Wake in S3, S4-5 (AC only)". Not checked which is
active. Its twin nh3-pve-2 kept its tunnel while powered off (2026-10-02 2221), so this one probably does too. active. Its twin nh3-pve-2 kept its tunnel while powered off (2026-10-02 2221), so this one probably does too.
- ⚠ **If "HW Connect" sticks at "Setup…" after a power cycle, look for a stale second tunnel** (2026-10-02 2247;
fix in `servers/pfi-tacticalrmm/README.md`). Because AMT shares `nic1`, the link drops whenever Linux
starts or stops, and AMT can leave its old tunnel half-open.
- Remote desktop works only while the host is ON (AMT mirrors the iGPU). Power it up from MeshCentral first. AMT
itself stays reachable when the host is off (both MS-03s, 2026-10-02 2229).
- Display: a 1080p HDMI plug is fitted (`HDMI-A-1` connected, 1920x1080, 2026-10-02 2241).
- **KVM needs an active iGPU output.** A 4K dummy plug blacked the AMT console on nh3-pve-2 once Linux took the - **KVM needs an active iGPU output.** A 4K dummy plug blacked the AMT console on nh3-pve-2 once Linux took the
display; use a 1080p plug. display; use a 1080p plug.
+12
View File
@@ -54,6 +54,18 @@ Monitors and manages endpoints, pushes patches, runs scripts, etc.
has none and its two first connects did not crash. That is inferred, not measured. Reconnects of has none and its two first connects did not crash. That is inferred, not measured. Reconnects of
an existing device take the other branch and are safe. **Onboard new AMTs at a quiet hour**; expect one ~7 s an existing device take the other branch and are safe. **Onboard new AMTs at a quiet hour**; expect one ~7 s
MeshCentral restart. MeshCentral restart.
- ⚠ **"HW Connect" stuck at "Setup…" = a stale CIRA tunnel** (2026-10-02 2247, esh-pve-2-amt; Prime power-cycled
the host several times). The AMT opened a new tunnel without closing the old one. MeshCentral then held two:
the live one (power actions and the AMT manager worked through it) and a dead one, `backoff:14`, nothing
received for 782 s. `webrelay.ashx` (KVM, SOL, IDE-R, MeshCommander) picked the dead one. A relay TLS failure
is only logged at debug level and never closes the websocket, so the browser waits forever. MPS's 90 s idle
timeout did not fire, because MeshCentral's own writes to the dead socket kept resetting it. TCP gives up on
its own after ~15–30 min of retransmits.
**Fix:** `sudo ss -tnio state established "( sport = :4433 )"`. Find the device's public IP with a large
`lastrcv` (ms) or a `backoff`. Healthy tunnels show `lastrcv` of seconds. Then
`sudo ss -K -tn "dst [::ffff:<ip>]:<its port>"`. Verify with `scripts/meshcentral-amt-relay-probe.js`, using
another AMT as a positive control. Likely whenever a host whose AMT shares its NIC (esh-pve-2) power-cycles.
Seen once so far.
- Before 2026-10-02: `"WANonly": true` (TacticalRMM's install default). In that mode MeshCentral SILENTLY DROPS - Before 2026-10-02: `"WANonly": true` (TacticalRMM's install default). In that mode MeshCentral SILENTLY DROPS
"Add Intel AMT computer": `meshuser.js` line 2682, `if (args.wanonly == true) return;`. No error, no "Add Intel AMT computer": `meshuser.js` line 2682, `if (args.wanonly == true) return;`. No error, no
event. LAN-mode AMT needs `WANonly` false (hybrid) + a service restart; CIRA works in WAN mode. TacticalRMM's event. LAN-mode AMT needs `WANonly` false (hybrid) + a service restart; CIRA works in WAN mode. TacticalRMM's