80 lines
2.8 KiB
PHP
80 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Provisioning\Steps\Host;
|
|
|
|
use App\Models\Host;
|
|
use App\Models\ProvisioningRun;
|
|
use App\Provisioning\StepResult;
|
|
use App\Services\Ssh\RemoteShell;
|
|
use App\Services\Wireguard\WireguardHub;
|
|
|
|
/**
|
|
* Installs WireGuard on the host, allocates a management IP, registers the host
|
|
* as a peer on the CluPilot hub, and verifies the tunnel is up.
|
|
*/
|
|
class ConfigureWireguard extends HostStep
|
|
{
|
|
public function __construct(private RemoteShell $shell, private WireguardHub $hub) {}
|
|
|
|
public function key(): string
|
|
{
|
|
return 'configure_wireguard';
|
|
}
|
|
|
|
public function execute(ProvisioningRun $run): StepResult
|
|
{
|
|
$host = $this->host($run);
|
|
|
|
// Idempotent: tunnel already provisioned and peer registered.
|
|
if (filled($host->wg_ip) && $this->hasResource($run, 'wg_peer')) {
|
|
return StepResult::advance();
|
|
}
|
|
|
|
$this->keyLogin($this->shell, $host);
|
|
$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');
|
|
}
|
|
|
|
$wgIp = $host->wg_ip ?: $this->hub->allocateIp();
|
|
$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_ip' => $wgIp, 'wg_pubkey' => $publicKey]);
|
|
$this->recordResource($run, $host, 'wg_peer', $publicKey);
|
|
|
|
$hubIp = (string) config('provisioning.wireguard.hub_ip', '10.66.0.1');
|
|
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',
|
|
'',
|
|
]);
|
|
}
|
|
}
|