release r007 security hardening and redesigned UI

This commit is contained in:
Mikei386
2026-08-21 07:43:39 +02:00
parent 514310912c
commit 65922b7f57
12 changed files with 485 additions and 346 deletions
+63 -22
View File
@@ -5,7 +5,7 @@
* Config-Datei: /boot/config/plugins/mua/mua.conf (chmod 600)
* Format (INI):
* MUA_API_KEY=<64 hex chars>
* MUA_ENABLED_TOOLS=all | <comma-separated tool names>
* MUA_ENABLED_TOOLS=all | none | <comma-separated tool names>
*
* Auth-Flow:
* Client sendet: Authorization: Bearer ***
@@ -27,16 +27,39 @@ const CONFIG_DIR = process.env["MUA_CONFIG_DIR"] ?? "/boot/config/plugins/mua";
const CONFIG_FILE = `${CONFIG_DIR}/mua.conf`;
// ── Config-Struktur ─────────────────────────────────────────────────────
interface MUAConfig {
export interface MUAConfig {
apiKey: string;
enabledTools: string[]; // leer = alle aktiv
enabledTools: string[];
allToolsEnabled: boolean;
}
// Sichere Grundeinstellung für Neuinstallationen. Schreibzugriffe, aktive
// Netzwerkscans und die Root-Shell müssen ausdrücklich in der GUI aktiviert
// werden.
export const SAFE_DEFAULT_TOOLS = [
"unraid_docker_list",
"unraid_docker_inspect",
"unraid_docker_logs",
"unraid_docker_analyze_logs",
"unraid_docker_processes",
"unraid_docker_stats",
"unraid_docker_info",
"unraid_network_inventory",
"unraid_network_list",
"unraid_network_inspect",
"unraid_network_host_state",
"unraid_system_connection_test",
];
// ── Config laden ────────────────────────────────────────────────────────
let cachedConfig: MUAConfig | null = null;
function parseConfig(content: string): MUAConfig {
const cfg: MUAConfig = { apiKey: "", enabledTools: [] };
export function parseConfig(content: string): MUAConfig {
const cfg: MUAConfig = {
apiKey: "",
enabledTools: [...SAFE_DEFAULT_TOOLS],
allToolsEnabled: false,
};
for (const line of content.split("\n")) {
const trimmed = line.trim();
if (trimmed.startsWith("#") || trimmed === "") continue;
@@ -47,13 +70,18 @@ function parseConfig(content: string): MUAConfig {
if (key === "MUA_API_KEY") {
cfg.apiKey = value;
} else if (key === "MUA_ENABLED_TOOLS") {
if (value === "all" || value === "") {
if (value === "all") {
cfg.enabledTools = [];
cfg.allToolsEnabled = true;
} else if (value === "none" || value === "") {
cfg.enabledTools = [];
cfg.allToolsEnabled = false;
} else {
cfg.enabledTools = value
.split(",")
.map((s) => s.trim())
.filter((s) => s.length > 0);
cfg.allToolsEnabled = false;
}
}
}
@@ -66,7 +94,10 @@ function loadConfig(): MUAConfig {
// 1. Env-Override (für Testing / Docker)
const envToken = process.env["MUA_API_KEY"];
if (envToken && envToken.length > 0) {
cachedConfig = { apiKey: envToken, enabledTools: [] };
const envTools = process.env["MUA_ENABLED_TOOLS"] ?? "";
cachedConfig = parseConfig(
`MUA_API_KEY=${envToken}\nMUA_ENABLED_TOOLS=${envTools || "none"}\n`,
);
return cachedConfig;
}
@@ -84,7 +115,11 @@ function loadConfig(): MUAConfig {
// 3. Neue Config generieren
const newToken = randomBytes(32).toString("hex"); // 64 hex chars
cachedConfig = { apiKey: newToken, enabledTools: [] };
cachedConfig = {
apiKey: newToken,
enabledTools: [...SAFE_DEFAULT_TOOLS],
allToolsEnabled: false,
};
try {
mkdirSync(CONFIG_DIR, { recursive: true });
@@ -92,7 +127,7 @@ function loadConfig(): MUAConfig {
console.log(`[MUA] Neue Config generiert: ${CONFIG_FILE}`);
} catch (e) {
console.error(`[MUA] WARNUNG: Config konnte nicht gespeichert werden: ${String(e)}`);
console.error(`[MUA] API-Key (nur in diesem Log): ${newToken}`);
console.error("[MUA] Aus Sicherheitsgründen wird der flüchtige API-Key nicht protokolliert.");
}
return cachedConfig;
@@ -100,17 +135,18 @@ function loadConfig(): MUAConfig {
// ── Config schreiben ────────────────────────────────────────────────────
function writeConfigFile(cfg: MUAConfig): void {
const toolsLine =
cfg.enabledTools.length === 0
? "MUA_ENABLED_TOOLS=all"
: `MUA_ENABLED_TOOLS=${cfg.enabledTools.join(",")}`;
const toolsValue = cfg.allToolsEnabled
? "all"
: cfg.enabledTools.length === 0
? "none"
: cfg.enabledTools.join(",");
const content = [
"# MUA Configuration",
"# API-Key für MCP-HTTP-Endpunkt (Port 3002)",
"# Format: Authorization: Bearer ***",
`MUA_API_KEY=${cfg.apiKey}`,
"# Aktive Tools (all = alle, oder kommagetrennte Tool-Namen)",
toolsLine,
"# Aktive Tools (all = alle, none = keine, oder kommagetrennte Tool-Namen)",
`MUA_ENABLED_TOOLS=${toolsValue}`,
"",
].join("\n");
writeFileSync(CONFIG_FILE, content, { mode: 0o600 });
@@ -148,13 +184,18 @@ export function getEnabledTools(): string[] {
return loadConfig().enabledTools;
}
export function getAllToolsEnabled(): boolean {
return loadConfig().allToolsEnabled;
}
/**
* Setzt die aktiven Tools.
* Leer-Array = alle Tools aktiv.
* Ein leeres Array bedeutet ausdrücklich: keine Tools aktiv.
*/
export function setEnabledTools(names: string[]): void {
export function setEnabledTools(names: string[], allToolsEnabled = false): void {
const cfg = loadConfig();
cfg.enabledTools = names;
cfg.enabledTools = [...new Set(names)];
cfg.allToolsEnabled = allToolsEnabled;
saveConfig(cfg);
}
@@ -164,7 +205,7 @@ export function setEnabledTools(names: string[]): void {
*/
export function isToolEnabled(toolName: string): boolean {
const enabled = getEnabledTools();
if (enabled.length === 0) return true; // alle aktiv
if (getAllToolsEnabled()) return true;
return enabled.includes(toolName);
}
@@ -192,7 +233,7 @@ export function checkAuth(req: Request): boolean {
return diff === 0;
}
// ── Token für WebGUI-Tab (nur lesbar) ───────────────────────────────────
export function getTokenForDisplay(): string {
return getApiKey();
export function getMaskedApiKey(): string {
const key = getApiKey();
return key.length >= 8 ? `••••••••${key.slice(-8)}` : "gesetzt";
}