wg_ip ?: $hub->allocateIp(); // Der Reihenfolge nach: erst den neuen Peer aufnehmen, dann den alten // entfernen. Dieselbe Regel wie beim Schlüsseltausch in §6 — dazwischen // darf es keinen Moment geben, in dem gar kein Peer eingetragen ist. $previous = $host->wg_pubkey; $hub->addPeer($keypair->publicKey, $ip); if (filled($previous) && $previous !== $keypair->publicKey) { $hub->removePeer($previous); } $code = Str::random(32); $host->forceFill([ 'wg_ip' => $ip, 'wg_pubkey' => $keypair->publicKey, 'enrolment_code_hash' => self::hash($code), 'enrolment_expires_at' => now()->addHours(self::LIFETIME_HOURS), // Ein neuer Code macht den alten wertlos. Sonst hätte eine Maschine, // die im Rettungssystem hängengeblieben ist, weiter einen gültigen // Ausweis für einen Host, den der Betreiber gerade neu übernimmt. 'enrolment_used_at' => null, ])->save(); return [ 'code' => $code, 'private_key' => $keypair->privateKey, 'public_key' => $keypair->publicKey, 'wg_ip' => $ip, ]; } /** * Löst einen gültigen, unverbrauchten Code auf, OHNE ihn zu verbrauchen. * * Das ist der Weg für `/host/progress`: die Fortschrittsmeldungen kommen vor * der Registrierung, und würden sie den Code verbrauchen, könnte der Host * sich nach seiner ersten Meldung nie mehr registrieren. */ public static function resolve(string $code): ?Host { if ($code === '') { return null; } return Host::query() ->where('enrolment_code_hash', self::hash($code)) ->whereNull('enrolment_used_at') ->where('enrolment_expires_at', '>', now()) ->first(); } /** Löst einen Code auf und verbraucht ihn. Der Weg für `/host/register`. */ public static function claim(string $code): ?Host { $host = self::resolve($code); if ($host === null) { return null; } $host->forceFill(['enrolment_used_at' => now()])->save(); return $host; } }