diff --git a/api/app.py b/api/app.py index a13b051..d5e1698 100644 --- a/api/app.py +++ b/api/app.py @@ -264,6 +264,39 @@ def _configured_domain() -> str: return config_manager.configs.get('_identity', {}).get('domain', 'cell') +def _restore_cell_wg_peers(cell_links): + """Re-add any cell link [Peer] blocks that are missing from wg0.conf. + + WireGuard peer blocks can be lost if the container is rebuilt or wg0.conf + is regenerated from scratch. Cell link data in cell_links.json is the + authoritative source — this function reconciles the conf file against it. + """ + try: + conf_content = wireguard_manager._read_config() + restored = 0 + for link in cell_links: + pk = link.get('public_key', '') + if not pk or pk in conf_content: + continue + name = link.get('cell_name', '') + endpoint = link.get('endpoint', '') + vpn_subnet = link.get('vpn_subnet', '') + ok = wireguard_manager.add_cell_peer( + name=name, public_key=pk, + endpoint=endpoint, vpn_subnet=vpn_subnet, + ) + if ok: + logger.info(f"Restored missing WG peer block for cell '{name}'") + restored += 1 + else: + logger.warning(f"Failed to restore WG peer block for cell '{name}'") + if restored: + # Reload conf into the running WireGuard interface + wireguard_manager._syncconf() + except Exception as e: + logger.warning(f"_restore_cell_wg_peers failed (non-fatal): {e}") + + def _apply_startup_enforcement(): try: peers = peer_registry.list_peers() @@ -272,6 +305,9 @@ def _apply_startup_enforcement(): firewall_manager.apply_all_peer_rules(peers) firewall_manager.apply_all_cell_rules(cell_links) firewall_manager.ensure_cell_api_dnat() + # Restore any cell link WireGuard peers that were lost from wg0.conf + # (happens if the container was rebuilt, wg0.conf was reset, etc.) + _restore_cell_wg_peers(cell_links) wireguard_manager.sync_cell_routes() firewall_manager.apply_all_dns_rules(peers, COREFILE_PATH, _configured_domain(), cell_links=cell_links)