--- Menu="Utilities" Type="xmenu" Title="MUA" Icon="cubes" --- true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_HTTPHEADER => $headers]; if ($method === 'POST') { $options[CURLOPT_POST] = true; if ($payload !== null) { $options[CURLOPT_POSTFIELDS] = json_encode($payload); $headers[] = 'Content-Type: application/json'; $options[CURLOPT_HTTPHEADER] = $headers; } } curl_setopt_array($ch, $options); $raw = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return [$code, $raw !== false ? json_decode($raw, true) : null]; } function csrf_value($token, $offset = 0) { return hash_hmac('sha256', gmdate('Y-m-d-H', time() + $offset * 3600), $token); } function mua_health($port) { $ch = curl_init('http://127.0.0.1:' . $port . '/health'); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 3, CURLOPT_CONNECTTIMEOUT => 2]); $raw = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $code === 200 && $raw !== false ? json_decode($raw, true) : null; } $running = false; $pid = null; if (file_exists($pidfile)) { $pid = trim(file_get_contents($pidfile)); $running = $pid && is_numeric($pid) && @posix_kill((int)$pid, 0); } $health_data = mua_health($port); $health_ok = is_array($health_data) && ($health_data['status'] ?? '') === 'ok'; $running_version = $health_data['version'] ?? ''; [$config_code, $config] = mua_api('GET', '/config', null, $admin_token, $config_port); $config_ok = $config_code === 200 && is_array($config); $flash_msg = ''; $flash_type = ''; $new_api_key = ''; $csrf = csrf_value($admin_token); $readonly = [ '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', 'unraid_system_shell_readonly', ]; $operator = array_merge($readonly, [ 'unraid_network_audit_tcp', 'unraid_network_lan_probe', 'unraid_docker_start', 'unraid_docker_stop', 'unraid_docker_restart', ]); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $posted_csrf = $_POST['mua_csrf'] ?? ''; $csrf_ok = $admin_token && (hash_equals(csrf_value($admin_token), $posted_csrf) || hash_equals(csrf_value($admin_token, -1), $posted_csrf)); if (!$csrf_ok) { $flash_msg = 'Sicherheitsprüfung fehlgeschlagen. Seite neu laden und erneut versuchen.'; $flash_type = 'err'; } else { $action = $_POST['action'] ?? ''; $payload = null; if ($action === 'generate_key') $payload = ['generate' => true]; if ($action === 'preset_readonly') $payload = ['enabledTools' => $readonly, 'allToolsEnabled' => false]; if ($action === 'preset_operator') $payload = ['enabledTools' => $operator, 'allToolsEnabled' => false]; if ($action === 'preset_none') $payload = ['enabledTools' => [], 'allToolsEnabled' => false]; if ($action === 'preset_all') $payload = ['enabledTools' => [], 'allToolsEnabled' => true]; if ($action === 'save_tools') { $selected = []; foreach (($config['allTools'] ?? []) as $tool) { $name = is_array($tool) ? ($tool['name'] ?? '') : $tool; if ($name && isset($_POST['tool_' . $name])) $selected[] = $name; } $payload = ['enabledTools' => $selected, 'allToolsEnabled' => false]; } if ($action === 'restart') { [$code, $result] = mua_api('POST', '/restart', null, $admin_token, $config_port); $flash_msg = $code === 200 ? 'MUA wird neu gestartet.' : 'Neustart fehlgeschlagen (HTTP ' . $code . ').'; $flash_type = $code === 200 ? 'ok' : 'err'; } elseif ($payload !== null) { [$code, $result] = mua_api('POST', '/config', $payload, $admin_token, $config_port); if ($code === 200 && is_array($result) && ($result['ok'] ?? false)) { $new_api_key = $result['generatedApiKey'] ?? ''; $flash_msg = $new_api_key ? 'Neuer API-Key erzeugt. Jetzt kopieren – er wird nur dieses eine Mal angezeigt.' : 'Sicherheitseinstellungen gespeichert.'; $flash_type = 'ok'; [$config_code, $config] = mua_api('GET', '/config', null, $admin_token, $config_port); $config_ok = $config_code === 200 && is_array($config); } else { $flash_msg = 'Speichern fehlgeschlagen (HTTP ' . $code . ').'; $flash_type = 'err'; } } } } $enabled_tools = $config['enabledTools'] ?? []; $all_tools_enabled = (bool)($config['allToolsEnabled'] ?? false); $all_tools = $config['allTools'] ?? []; $risk_groups = ['read' => [], 'active' => [], 'write' => [], 'critical' => []]; foreach ($all_tools as $tool) { if (!is_array($tool)) $tool = ['name' => $tool, 'description' => '', 'risk' => 'read']; $risk = $tool['risk'] ?? 'read'; if (!isset($risk_groups[$risk])) $risk = 'read'; $risk_groups[$risk][] = $tool; } $risk_labels = [ 'read' => ['Nur Lesen', 'Liest Status und Diagnoseinformationen.', 'mua-risk-safe'], 'active' => ['Aktive Prüfungen', 'Baut gezielt Netzwerkverbindungen für Tests auf.', 'mua-risk-notice'], 'write' => ['Betrieb steuern', 'Startet, stoppt oder startet Container neu.', 'mua-risk-warn'], 'critical' => ['Kritischer Zugriff', 'Verändert Container oder führt uneingeschränkte Root-Befehle aus.', 'mua-risk-danger'], ]; $active_count = $all_tools_enabled ? count($all_tools) : count($enabled_tools); ?>

MUA · Mikes Unraid Agent

Sicherer MCP-Zugriff auf Docker, Netzwerk und Unraid-Systemfunktionen
Neuer API-Key – einmalige Anzeige

Dienst

Version · PID

Zugriffsprofil

von Werkzeugen aktiv

Authentifizierung

MCP-Endpunkt

Streamable HTTP · JSON-RPC 2.0 · Bearer-Authentifizierung erforderlich

Schnelle Sicherheitsprofile

„Nur Lesen“ ist die empfohlene Dauerstellung. Weitergehende Rechte nur bei konkretem Bedarf freigeben.

">

Werkzeuge im Detail

$tools): $meta=$risk_labels[$risk]; ?>

API-Key

Der vollständige Schlüssel wird nicht dauerhaft angezeigt. Beim Erzeugen erscheint er genau einmal; danach ist nur noch die Endung sichtbar.

Technische Details

Version
MCP-Port
Verwaltung127.0.0.1: · separater Admin-Token
Konfiguration/boot/config/plugins/mua/mua.conf
Audit-Protokoll/var/log/plugins/mua-audit.log (keine Argumente oder Inhalte)