feat(installer): Laufzeitpruefung, Host-Ueberwachung, Zugang fuer Adminkonten
Drei Dinge, die beim ersten Lauf des Installers auf einer Linux-Maschine auffielen. 1. Die Release-Ablage wies Administratorkonten ab. ReleaseGuard nahm nur die Rolle 'installer' in die .htpasswd auf, waehrend Installskripte und Agent ausdruecklich sagten, ein Administratorkonto tue es auch: Anmeldung und Katalog gelangen, erst der Download endete mit 401 - und die Meldung sprach von abgelaufenen Lizenzen, die es bei einer Erstinstallation gar nicht geben kann. Adminkonten zaehlen jetzt zu den Installationskonten. FORMAT_VERSION auf 3, damit reconcile() die Dateien sofort neu schreibt statt erst beim naechsten turnusmaessigen Lauf; ein neu angelegtes Konto landet ausserdem unabhaengig von seiner Rolle sofort darin. Bei einem 401 mit Benutzerzugangsdaten nennt der Client jetzt Konto und zugangsberechtigte Rollen, und der Agent bricht ab, statt ueber die API weiterzusuchen und dieselbe Meldung ein paar Schritte spaeter ein zweites Mal zu zeigen. 2. Der Installer prueft die .NET-Laufzeit. Bisher endete eine gelungene Installation auf einer Maschine ohne .NET mit einer Anwendung, die sich nicht starten laesst - und die Fehlersuche begann beim Deploymentcenter, weil das der letzte bewusste Schritt war. Gelesen wird die runtimeconfig.json der Anwendung und mit "dotnet --list-runtimes" verglichen; fehlt etwas, nennt der Installer den Installationsbefehl fuer diese Plattform. Eigenstaendig veroeffentlichte Pakete werden nicht bemaengelt, rollForward wird beachtet. 3. Die Ueberwachung der Maschine entsteht im Installer. Zwei Fragen - Name im Dashboard und ob eingeplant werden soll - statt fuenf Schritten in der Oberflaeche an einem anderen Rechner. Monitor, Token mit genau watchdog:ping, Skript, Dateirechte, ein Heartbeat zur Probe und der Cron-Eintrag bzw. die geplante Aufgabe entstehen daraus. Fuer Maschinen ohne Installation: --action monitor. Die Agent-Skripte werden jetzt in src/Modules/Watchdog/AgentScript.php erzeugt - von Oberflaeche und Installer gemeinsam - und melden Last, Speicher, Plattenbelegung und Laufzeit mit, statt nur "status: ok". Beim Ausfuehren fielen zwei Fehler auf, die dort behoben sind: df -P verrutscht bei Geraetenamen mit Leerzeichen (gezaehlt wird jetzt von hinten), und ohne LC_ALL=C erzeugt awk auf einem deutschen System "12,5" und damit kaputtes JSON. Neu: POST /api/setup/v1/agent. SDK 2.6.0 mit SetupClient.RequestWatchdogAgentAsync(). Die OpenAPI-Beschreibung des neuen Endpunkts bleibt zunaechst aussen vor: public/api/openapi.php traegt gerade auch fremde, noch nicht committete Aenderungen aus einer parallel laufenden Arbeit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fc9b698141
commit
687ee0cefc
+29
-42
@@ -35,6 +35,7 @@ use Deploymentcenter\Modules\License\KeyGen;
|
||||
use Deploymentcenter\Modules\UpdateService\ReleaseGuard;
|
||||
use Deploymentcenter\Modules\UpdateService\ReleaseSigner;
|
||||
use Deploymentcenter\Modules\UpdateService\UpdateManager;
|
||||
use Deploymentcenter\Modules\Watchdog\AgentScript;
|
||||
use Deploymentcenter\Modules\Watchdog\Evaluator;
|
||||
use Deploymentcenter\Modules\Watchdog\EventLog;
|
||||
use Deploymentcenter\Modules\Watchdog\MonitorRepo;
|
||||
@@ -365,46 +366,18 @@ if (($_GET['action'] ?? '') === 'download_agent') {
|
||||
$os = trim((string)($_GET['os'] ?? 'windows'));
|
||||
$token = trim((string)($_GET['token'] ?? ''));
|
||||
|
||||
// Nur unkritische Zeichen, damit nichts in das erzeugte Skript
|
||||
// hineingeschmuggelt werden kann.
|
||||
$source = preg_replace('/[^A-Za-z0-9._ -]/', '', $source) ?? 'server-node';
|
||||
$token = preg_replace('/[^A-Za-z0-9._-]/', '', $token) ?? '';
|
||||
$endpoint = Http::baseUrl() . '/api/watchdog/v1/ping';
|
||||
|
||||
if ($os === 'windows') {
|
||||
$script = "# Deploymentcenter Watchdog Agent (Windows PowerShell)\n"
|
||||
. "# Als geplante Aufgabe minuetlich ausfuehren.\n\n"
|
||||
. '$WatchdogUrl = "' . $endpoint . "\"\n"
|
||||
. '$Token = "' . $token . "\"\n"
|
||||
. '$Source = "' . $source . "\"\n\n"
|
||||
. '$body = @{ source = $Source; status = "ok"; message = "Heartbeat via PowerShell Task"; interval = 60 } | ConvertTo-Json' . "\n"
|
||||
. 'try {' . "\n"
|
||||
. ' Invoke-RestMethod -Uri $WatchdogUrl -Method Post -Body $body -ContentType "application/json" -Headers @{ "X-Agent-Token" = $Token } | Out-Null' . "\n"
|
||||
. ' Write-Host "[OK] Heartbeat gesendet."' . "\n"
|
||||
. '} catch {' . "\n"
|
||||
. ' Write-Error "Heartbeat fehlgeschlagen: $_"' . "\n"
|
||||
. ' exit 1' . "\n"
|
||||
. '}' . "\n";
|
||||
$filename = 'watchdog-agent-' . $source . '.ps1';
|
||||
} else {
|
||||
$script = "#!/usr/bin/env bash\n"
|
||||
. "# Deploymentcenter Watchdog Agent (Linux)\n"
|
||||
. "# Per Cron minuetlich ausfuehren.\n\n"
|
||||
. 'set -euo pipefail' . "\n\n"
|
||||
. 'WATCHDOG_URL="' . $endpoint . "\"\n"
|
||||
. 'TOKEN="' . $token . "\"\n"
|
||||
. 'SOURCE="' . $source . "\"\n\n"
|
||||
. 'curl -fsS -X POST "$WATCHDOG_URL" \\' . "\n"
|
||||
. ' -H "Content-Type: application/json" \\' . "\n"
|
||||
. ' -H "X-Agent-Token: $TOKEN" \\' . "\n"
|
||||
. ' -d "{\"source\": \"$SOURCE\", \"status\": \"ok\", \"message\": \"Heartbeat via Cron\", \"interval\": 60}" \\' . "\n"
|
||||
. ' > /dev/null && echo "[OK] Heartbeat gesendet."' . "\n";
|
||||
$filename = 'watchdog-agent-' . $source . '.sh';
|
||||
}
|
||||
// Erzeugt wird in AgentScript - dieselbe Fassung, die der Installer ueber
|
||||
// den Setup-Weg bekommt. Das Saeubern der Eingaben passiert dort mit.
|
||||
$agent = AgentScript::build(
|
||||
$os,
|
||||
$source,
|
||||
$token,
|
||||
Http::baseUrl() . '/api/watchdog/v1/ping'
|
||||
);
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
header('Content-Disposition: attachment; filename="' . preg_replace('/[^A-Za-z0-9._-]/', '', $filename) . '"');
|
||||
echo $script;
|
||||
header('Content-Disposition: attachment; filename="' . $agent['filename'] . '"');
|
||||
echo $agent['script'];
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -1379,10 +1352,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
// Installationskonten stehen in jeder .htpasswd - ohne sie
|
||||
// waere eine Erstinstallation nicht moeglich, weil es dabei
|
||||
// noch keinen Lizenzschluessel gibt.
|
||||
if ($newRole === 'installer') {
|
||||
dc_guard_releases($pdo);
|
||||
}
|
||||
// noch keinen Lizenzschluessel gibt. Administratoren zaehlen
|
||||
// dazu; die Bedingung stand hier noch aus der Zeit, als nur
|
||||
// die Rolle 'installer' Zugang hatte, und liess ein frisches
|
||||
// Administratorkonto bis zum naechsten turnusmaessigen Lauf
|
||||
// vor verschlossener Ablage stehen.
|
||||
dc_guard_releases($pdo);
|
||||
|
||||
dc_flash(sprintf(
|
||||
'Konto "%s" mit der Rolle %s angelegt.%s',
|
||||
@@ -3407,6 +3382,12 @@ chmod +x update-agent
|
||||
man Nutzer sonst warnt.
|
||||
</p>
|
||||
|
||||
<p style="color:var(--text-muted); font-size:0.875rem; margin:1rem 0 0.75rem;">
|
||||
Nur die <strong>Maschine überwachen</strong>, ohne etwas zu installieren —
|
||||
legt Monitor und Token an, schreibt das Agent-Skript und plant es ein:
|
||||
</p>
|
||||
<div class="prompt-box">./update-agent --action monitor</div>
|
||||
|
||||
<p style="color:var(--text-muted); font-size:0.8rem; margin-top:0.75rem;">
|
||||
Für die Installation wird ein Konto der Rolle <strong>installer</strong>
|
||||
gebraucht — anzulegen unter <em>System → 👤 Benutzer</em>. Ein
|
||||
@@ -3713,6 +3694,10 @@ chmod +x update-agent
|
||||
'resolved' => 'Gelöst',
|
||||
'closed' => 'Geschlossen',
|
||||
'rejected' => 'Abgelehnt',
|
||||
// Meldungen aus einem anderen Projekt. Ohne eigenen
|
||||
// Filtereintrag wären sie nur unter "Alle Status" zu
|
||||
// finden - ein Eingang, den niemand sieht, ist keiner.
|
||||
'triage_external' => '📥 Fremdmeldungen',
|
||||
];
|
||||
foreach ($statusOptions as $value => $label): ?>
|
||||
<option value="<?= e($value) ?>" <?= $btFilters['status'] === $value ? 'selected' : '' ?>><?= e($label) ?></option>
|
||||
@@ -3783,6 +3768,7 @@ chmod +x update-agent
|
||||
'in_progress' => '<span class="badge badge-warning">IN BEARBEITUNG</span>',
|
||||
'resolved' => '<span class="badge badge-up">GELÖST</span>',
|
||||
'rejected' => '<span class="badge badge-stopped">ABGELEHNT</span>',
|
||||
'triage_external' => '<span class="badge" style="background:rgba(120,160,255,0.2); color:#7aa2ff; border:1px solid #7aa2ff;">📥 FREMDMELDUNG</span>',
|
||||
default => '<span class="badge badge-stopped">' . e(strtoupper((string)$item['status'])) . '</span>',
|
||||
};
|
||||
|
||||
@@ -5841,6 +5827,7 @@ SYSTEM
|
||||
<option value="resolved">Gelöst / Umgesetzt</option>
|
||||
<option value="closed">Geschlossen</option>
|
||||
<option value="rejected">Abgelehnt</option>
|
||||
<option value="triage_external">📥 Fremdmeldung (ungesichtet)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
Reference in New Issue
Block a user