whereNotNull('wg_ip')->pluck('wg_ip')->all()); $hubIp = (string) config('provisioning.wireguard.hub_ip'); [$network, $bits] = explode('/', config('provisioning.wireguard.subnet', '10.66.0.0/24')); $bits = (int) $bits; $hostBits = 32 - $bits; $size = 2 ** $hostBits; $base = ip2long($network) & (0xFFFFFFFF << $hostBits) & 0xFFFFFFFF; // Skip the network address (offset 0) and broadcast (offset size-1). for ($offset = 1; $offset < $size - 1; $offset++) { $ip = long2ip($base + $offset); if ($ip !== $hubIp && ! isset($used[$ip])) { return $ip; } } throw new RuntimeException('WireGuard management subnet exhausted.'); } public function addPeer(string $publicKey, string $ip): void { Process::run(['wg', 'set', 'wg0', 'peer', $publicKey, 'allowed-ips', $ip.'/32'])->throw(); Process::run('wg-quick save wg0')->throw(); // persist, else the peer is lost on restart } public function removePeer(string $publicKey): void { Process::run(['wg', 'set', 'wg0', 'peer', $publicKey, 'remove'])->throw(); Process::run('wg-quick save wg0')->throw(); // persist, else the peer is lost on restart } public function endpoint(): string { return (string) config('provisioning.wireguard.endpoint'); } public function publicKey(): string { return (string) config('provisioning.wireguard.hub_public_key'); } }