get('ssh.private_key'); // Lieber hier scheitern als ein Fenster, das aufgeht und schweigt: ohne // Schlüssel kann die Brücke sich nicht anmelden, und der Betreiber sähe // nur einen Vorspann, der nie weicht. if (blank($key)) { throw new RuntimeException('Kein SSH-Schlüssel hinterlegt — ohne ihn kann keine Terminalsitzung entstehen.'); } $ticket = bin2hex(random_bytes(32)); // ALS JSON, nicht als PHP-Array. Laravel legt einen Cache-Wert sonst // PHP-serialisiert ab, und der Container, der ihn liest, ist Python — // der kann damit nichts anfangen. Das Format ist hier eine // Schnittstelle zwischen zwei Sprachen, keine interne Ablage. Cache::put(self::PREFIX.$ticket, json_encode([ 'operator_id' => $for->id, 'host_uuid' => $host->uuid, // Die Tunneladresse. Die öffentliche IP wäre der Weg, den // SecureHostFirewall ausdrücklich zumacht. 'ip' => $host->wg_ip, 'user' => 'root', 'private_key' => $key, 'fingerprint' => $host->ssh_host_key, ], JSON_THROW_ON_ERROR), self::TTL_SECONDS); return $ticket; } /** * Liest das Ticket und löscht es im selben Zug. * * @return array{operator_id: int, host_uuid: string, ip: ?string, user: string, private_key: string, fingerprint: ?string}|null */ public static function redeem(string $ticket): ?array { $raw = Cache::pull(self::PREFIX.$ticket); return $raw === null ? null : json_decode((string) $raw, true, flags: JSON_THROW_ON_ERROR); } }