feat: restore WireGuard peers after bootstrap and add VPN routing tests
apply_config() now calls _load_registered_peers() when wg0.conf is empty so all active peers from peers.json are written back into the config file after a bootstrap — preventing clients from losing tunnel access after an API restart that regenerated wg0.conf from scratch. Adds test_wireguard_vpn_routing.py (36 tests) covering: - generate_config() PostUp/PostDown rules enabling internet forwarding (MASQUERADE + FORWARD ACCEPT required for internet-through-VPN) - get_peer_config() DNS field pointing to cell-dns for domain resolution - apply_config() bootstrap peer restoration from peers.json - _load_registered_peers() filtering (inactive, missing fields, malformed) - add_peer() /32 AllowedIPs enforcement to prevent route leaks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -206,6 +206,23 @@ class WireGuardManager(BaseServiceManager):
|
||||
"""Return split-tunnel AllowedIPs: VPN subnet + Docker bridge."""
|
||||
return f'{self._get_configured_network()}, 172.20.0.0/16'
|
||||
|
||||
def _load_registered_peers(self) -> list:
|
||||
"""Read active peers from peers.json for wg0.conf reconstruction after bootstrap."""
|
||||
import json as _json
|
||||
peers_file = os.path.join(self.data_dir, 'peers.json')
|
||||
try:
|
||||
with open(peers_file) as f:
|
||||
peers = _json.load(f)
|
||||
return [
|
||||
p for p in peers
|
||||
if isinstance(p, dict)
|
||||
and p.get('active', True)
|
||||
and p.get('public_key')
|
||||
and p.get('ip')
|
||||
]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def apply_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Update wg0.conf interface fields and restart cell-wireguard."""
|
||||
restarted = []
|
||||
@@ -221,6 +238,15 @@ class WireGuardManager(BaseServiceManager):
|
||||
# Bootstrap from generate_config() if file is empty or has no [Interface]
|
||||
if not raw.strip() or '[Interface]' not in raw:
|
||||
raw = self.generate_config()
|
||||
# Restore all registered peers so clients can reconnect immediately
|
||||
for peer in self._load_registered_peers():
|
||||
raw += (
|
||||
f'\n[Peer]\n'
|
||||
f'# {peer.get("peer", "unknown")}\n'
|
||||
f'PublicKey = {peer["public_key"]}\n'
|
||||
f'AllowedIPs = {peer["ip"]}/32\n'
|
||||
f'PersistentKeepalive = 25\n'
|
||||
)
|
||||
with open(cf, 'w') as f:
|
||||
f.write(raw)
|
||||
warnings.append('wg0.conf was empty — regenerated from keys')
|
||||
|
||||
Reference in New Issue
Block a user