fix: cross-cell routing for split-tunnel peers

Three related fixes for split-tunnel peers that need to reach connected cells:

1. apply_peer_rules/apply_all_peer_rules now accept wg_subnet (actual local VPN
   subnet) and cell_subnets (connected cells' vpn_subnets) parameters instead of
   hardcoding 10.0.0.0/24. All callers (startup, add_peer, update_peer,
   apply-enforcement endpoint) pass the real values.

2. Explicit ACCEPT rules are inserted in FORWARD for each connected cell's
   subnet so split-tunnel peers (internet_access=False) can still reach
   connected cells via the wg0→wg0 path.

3. apply_ip_range in network_manager now loads cell_links.json and passes it
   to generate_corefile(), fixing a race where the bootstrap DNS thread could
   overwrite the Corefile and wipe cross-cell DNS forwarding zones on startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-04 14:36:28 -04:00
parent 8ee1d88e37
commit c2d215ee2e
6 changed files with 171 additions and 19 deletions
+20 -3
View File
@@ -38,6 +38,13 @@ def add_peer():
from app import (peer_registry, wireguard_manager, firewall_manager,
email_manager, calendar_manager, file_manager, auth_manager,
cell_link_manager, _configured_domain, COREFILE_PATH)
try:
_wg_addr = wireguard_manager._get_configured_address()
_wg_subnet = str(ipaddress.ip_network(_wg_addr, strict=False)) if _wg_addr else '10.0.0.0/24'
except Exception:
_wg_subnet = '10.0.0.0/24'
_cell_links = cell_link_manager.list_connections()
_cell_subnets = [l['vpn_subnet'] for l in _cell_links if l.get('vpn_subnet')]
data = request.get_json(silent=True)
if data is None:
return jsonify({"error": "No data provided"}), 400
@@ -118,7 +125,8 @@ def add_peer():
return jsonify({"error": f"Peer {peer_name} already exists"}), 400
peer_added_to_registry = True
firewall_manager.apply_peer_rules(peer_info['ip'], peer_info)
firewall_manager.apply_peer_rules(peer_info['ip'], peer_info,
wg_subnet=_wg_subnet, cell_subnets=_cell_subnets)
firewall_applied = True
wg_allowed = f"{assigned_ip}/32" if '/' not in assigned_ip else assigned_ip
@@ -153,7 +161,15 @@ def add_peer():
@bp.route('/api/peers/<peer_name>', methods=['PUT'])
def update_peer(peer_name):
try:
from app import peer_registry, firewall_manager, cell_link_manager, _configured_domain, COREFILE_PATH
from app import (peer_registry, wireguard_manager, firewall_manager,
cell_link_manager, _configured_domain, COREFILE_PATH)
try:
_wg_addr = wireguard_manager._get_configured_address()
_wg_subnet = str(ipaddress.ip_network(_wg_addr, strict=False)) if _wg_addr else '10.0.0.0/24'
except Exception:
_wg_subnet = '10.0.0.0/24'
_cell_links = cell_link_manager.list_connections()
_cell_subnets = [l['vpn_subnet'] for l in _cell_links if l.get('vpn_subnet')]
data = request.get_json(silent=True) or {}
existing = peer_registry.get_peer(peer_name)
if not existing:
@@ -173,7 +189,8 @@ def update_peer(peer_name):
if success:
updated_peer = peer_registry.get_peer(peer_name)
if updated_peer:
firewall_manager.apply_peer_rules(updated_peer['ip'], updated_peer)
firewall_manager.apply_peer_rules(updated_peer['ip'], updated_peer,
wg_subnet=_wg_subnet, cell_subnets=_cell_subnets)
firewall_manager.apply_all_dns_rules(peer_registry.list_peers(), COREFILE_PATH, _configured_domain(),
cell_links=cell_link_manager.list_connections())
return jsonify({"message": f"Peer {peer_name} updated", "config_changed": config_changed})