host($run); $alreadyProvisioned = filled($host->wg_ip) && $this->hasResource($run, 'wg_peer'); $this->keyLogin($this->shell, $host); if (! $alreadyProvisioned) { $this->shell->run('export DEBIAN_FRONTEND=noninteractive; apt-get install -y wireguard'); $this->shell->run('test -f /etc/wireguard/privatekey || (umask 077; wg genkey > /etc/wireguard/privatekey)'); $publicKey = trim($this->shell->run('wg pubkey < /etc/wireguard/privatekey')->stdout); if (blank($publicKey)) { return StepResult::retry(20, 'could not read host WireGuard public key'); } // Allocate + reserve the management IP atomically so concurrent // onboarding runs can never receive the same address. $wgIp = Cache::lock('wireguard:allocate', 30)->block(10, function () use ($host) { $ip = $host->wg_ip ?: $this->hub->allocateIp(); if (blank($host->wg_ip)) { $host->update(['wg_ip' => $ip]); } return $ip; }); $privateKey = trim($this->shell->run('cat /etc/wireguard/privatekey')->stdout); $this->shell->putFile('/etc/wireguard/wg0.conf', $this->renderConfig($wgIp, $privateKey)); $this->shell->run('systemctl enable --now wg-quick@wg0 || wg-quick up wg0'); $this->hub->addPeer($publicKey, $wgIp); // Persist external identity BEFORE verifying/advancing (crash-safe). $host->update(['wg_pubkey' => $publicKey]); $this->recordResource($run, $host, 'wg_peer', $publicKey); } // Always verify the tunnel is up — including the idempotent replay path, // so we never advance onto Proxmox API calls over a dead tunnel. $hubIp = (string) config('provisioning.wireguard.hub_ip'); if (! $this->shell->run('ping -c1 -W2 '.escapeshellarg($hubIp))->ok()) { return StepResult::retry(15, 'WireGuard handshake not up yet'); } return StepResult::advance(); } private function renderConfig(string $wgIp, string $privateKey): string { $subnet = (string) config('provisioning.wireguard.subnet', '10.66.0.0/24'); return implode("\n", [ '[Interface]', "Address = {$wgIp}/24", "PrivateKey = {$privateKey}", '', '[Peer]', 'PublicKey = '.$this->hub->publicKey(), 'Endpoint = '.$this->hub->endpoint(), "AllowedIPs = {$subnet}", 'PersistentKeepalive = 25', '', ]); } }