Ferndiagnose (MCP): Verbindungscode + abgesicherter Fernzugriff + stdio-Bridge
Neue Funktion, um Diagnose eines laufenden Servers aus der Ferne zu ermoeglichen:
Hilfe -> Remote-Support -> "Ferndiagnose (MCP)". Erzeugt einen Verbindungscode,
den der Assistent nutzt, um Status, Logs, Fehler und Accounts read-only zu lesen.
App-Seite:
- Live (re)startbarer Debug-Server ohne App-Neustart (restartDebugServer wartet auf
'close' + closeAllConnections, behandelt EADDRINUSE).
- IP-Allowlist (debug_allowlist.txt, exakte IP + CIDR), erzwungen VOR der Auth.
Fail-closed: Netzwerk-Bind (0.0.0.0) ohne Allowlist akzeptiert nur Loopback.
- One-Click Aktivieren/Aktualisieren/Deaktivieren + Token-Rotation (alter Code sofort
ungueltig). Sichtbarkeit waehlbar: "Nur lokal" (Tunnel-Empfehlung) vs "Im Netzwerk".
- Verbindungscode rddiag:v1:base64url({v,h,p,t,n?,fp?,s?}); oeffentlicher Host frei
waehlbar, Netzwerk-IPs als Schnellauswahl.
- Neue IPC: get/enable/disable/rotate Remote-Diagnostics; Controller-Methoden; Typen.
Bridge (tools/rd-diagnostics-mcp, standalone, KEINE App-Dependency):
- stdio MCP-Server (@modelcontextprotocol/sdk) mit 14 Tools, proxyt die bestehende
HTTP-Debug-API. Multi-Server ueber code/server/RDDIAG_CODE/RDDIAG_SERVERS.
- TLS-Fingerprint-Pinning auf secureConnect (vor Token-Versand), falls https genutzt.
- test/harness.mjs: faehrt einen Fake-Debug-Server hoch und treibt die Bridge als
echten stdio-Child per JSON-RPC -> voller Protokollpfad gruen.
Sicherheit (Audit): keine persistenten Secrets in den Logs der Debug-API (Passwoerter
redigiert, keine Debrid-Keys/aufgeloesten Download-URLs geloggt; settings/accounts
redigiert). Empfohlener Transport: Loopback + privater Tunnel; Direkt-Bind nur mit
Allowlist in vertrauenswuerdigen Netzen.
Tests: connection-code-Cross-Check (App-Encoder <-> Bridge-Decoder), Allowlist-Matrix
(Loopback, exakt, CIDR, fail-closed, Live-Restart). Volle Suite 905 gruen, tsc=6.
This commit is contained in:
+28
-1
@@ -1,7 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { app, BrowserWindow, clipboard, dialog, ipcMain, IpcMainInvokeEvent, Menu, shell, Tray } from "electron";
|
||||
import { AddLinksPayload, AppSettings, DebridProvider, UpdateInstallProgress } from "../shared/types";
|
||||
import { AddLinksPayload, AppSettings, DebridProvider, EnableRemoteDiagnosticsInput, UpdateInstallProgress } from "../shared/types";
|
||||
import { AppController } from "./app-controller";
|
||||
import { IPC_CHANNELS } from "../shared/ipc";
|
||||
import { getLogFilePath, logger } from "./logger";
|
||||
@@ -671,6 +671,33 @@ function registerIpcHandlers(): void {
|
||||
return { path: rotated.path };
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.GET_REMOTE_DIAGNOSTICS, async () => {
|
||||
return controller.getRemoteDiagnostics();
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.ENABLE_REMOTE_DIAGNOSTICS, async (_event: IpcMainInvokeEvent, input: EnableRemoteDiagnosticsInput) => {
|
||||
if (!input || (input.hostMode !== "local" && input.hostMode !== "network")) {
|
||||
throw new Error("hostMode muss 'local' oder 'network' sein");
|
||||
}
|
||||
const allowlist = Array.isArray(input.allowlist) ? input.allowlist.map((entry) => String(entry)) : [];
|
||||
return controller.enableRemoteDiagnostics({
|
||||
hostMode: input.hostMode,
|
||||
publicHost: String(input.publicHost || ""),
|
||||
port: input.port ? Number(input.port) : undefined,
|
||||
allowlist,
|
||||
name: input.name ? String(input.name) : undefined,
|
||||
rotateToken: Boolean(input.rotateToken)
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.DISABLE_REMOTE_DIAGNOSTICS, async () => {
|
||||
return controller.disableRemoteDiagnostics();
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.ROTATE_REMOTE_DIAGNOSTICS_TOKEN, async () => {
|
||||
return controller.rotateRemoteDiagnosticsToken();
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_CHANNELS.OPEN_ITEM_LOG, async (_event: IpcMainInvokeEvent, itemId: string) => {
|
||||
validateString(itemId, "itemId");
|
||||
const logPath = controller.getItemLogPath(itemId);
|
||||
|
||||
Reference in New Issue
Block a user