"`, NICHT der * OpenSSH-Fingerabdruck — siehe docker/terminal/bridge.py). Er ist der Grund, * warum eine Sitzung überhaupt sicher ist: ohne ihn meldet sich die Brücke * mit einem Root-Schlüssel bei irgendwem an, der gerade auf Port 22 antwortet. * * Geschrieben wird die Datei von `/usr/local/sbin/clupilot-host-step * install-server-terminal`, also von der root-eigenen Hälfte, die das * Dienstkonto nicht anfassen kann. Gelesen wird sie hier. */ final readonly class ServerTerminalSetup { public function __construct( public string $address, public string $fingerprint, public ?string $installedAt, ) {} public static function path(): string { return storage_path('app/deploy/server-terminal.json'); } /** * Die Einrichtung, oder null, wenn es keine brauchbare gibt. * * Fehlt die Datei, ist das der Normalfall vor dem ersten * `sudo bash deploy/install-agent.sh` — dazu gibt es nichts zu sagen. Ist * sie aber DA und trotzdem unbrauchbar, wird das protokolliert: sonst sehen * „nie eingerichtet" und „Einrichtung kaputt" von der Seite aus gleich aus, * und die Anleitung, die dann dort steht, hilft im zweiten Fall nicht. */ public static function read(): ?self { $path = self::path(); if (! File::exists($path)) { return null; } try { $data = json_decode(File::get($path), true, flags: JSON_THROW_ON_ERROR); } catch (Throwable $e) { Log::warning('Die Einrichtung des Server-Terminals ist nicht lesbar.', [ 'path' => $path, 'error' => $e->getMessage(), ]); return null; } $address = is_array($data) ? (string) ($data['address'] ?? '') : ''; $fingerprint = is_array($data) ? (string) ($data['fingerprint'] ?? '') : ''; // Geprüft, nicht geglaubt. Eine leere Adresse ergäbe ein Fenster, das // nie verbindet; ein leerer Fingerabdruck ist das Gegenteil von // harmlos — die naheliegendste Lesart davon („nichts zu prüfen") wäre // eine ungepinnte Root-Sitzung. if (filter_var($address, FILTER_VALIDATE_IP) === false || ! str_starts_with($fingerprint, 'SHA256:')) { Log::warning('Die Einrichtung des Server-Terminals ist unvollständig.', ['path' => $path]); return null; } return new self( address: $address, fingerprint: $fingerprint, installedAt: ($data['installed_at'] ?? null) ? (string) $data['installed_at'] : null, ); } }