feat: fix cross-cell service access — DNS DNAT, service DNAT, Caddy routing
DNS A records now return the WireGuard server IP (10.0.0.1) instead of Docker bridge VIPs so cross-cell peers resolve service names correctly regardless of their bridge subnet. DNAT rules (wg0:53→cell-dns:53 and wg0:80→cell-caddy:80) are applied at startup. Caddy routes by Host header, eliminating the Docker bridge subnet conflict. Firewall cell rules allow DNS and service (Caddy) traffic from linked cell subnets. Split-tunnel AllowedIPs now dynamically includes connected-cell VPN subnets and drops the 172.20.0.0/16 range. Peers with route_via set now receive full-tunnel config (0.0.0.0/0) so all their traffic exits via the remote cell. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -203,8 +203,25 @@ class WireGuardManager(BaseServiceManager):
|
||||
return SERVER_NETWORK
|
||||
|
||||
def get_split_tunnel_ips(self) -> str:
|
||||
"""Return split-tunnel AllowedIPs: VPN subnet + Docker bridge."""
|
||||
return f'{self._get_configured_network()}, 172.20.0.0/16'
|
||||
"""Return split-tunnel AllowedIPs: local VPN subnet + all connected cell VPN subnets.
|
||||
|
||||
172.20.0.0/16 is intentionally excluded — all services are accessed via the
|
||||
WG server IP (ensure_service_dnat routes wg0:80 to Caddy). Including the
|
||||
Docker bridge subnet would cause routing conflicts when cells share the same range.
|
||||
"""
|
||||
local_net = self._get_configured_network()
|
||||
cell_links_file = os.path.join(self.data_dir, 'api', 'cell_links.json')
|
||||
cell_nets = []
|
||||
try:
|
||||
with open(cell_links_file) as f:
|
||||
links = json.load(f)
|
||||
for link in links:
|
||||
subnet = link.get('vpn_subnet', '')
|
||||
if subnet and subnet != local_net:
|
||||
cell_nets.append(subnet)
|
||||
except Exception:
|
||||
pass
|
||||
return ', '.join([local_net] + cell_nets)
|
||||
|
||||
def _load_registered_peers(self) -> list:
|
||||
"""Read active peers from peers.json for wg0.conf reconstruction after bootstrap."""
|
||||
@@ -733,7 +750,14 @@ class WireGuardManager(BaseServiceManager):
|
||||
if allowed_ips is None:
|
||||
allowed_ips = self.FULL_TUNNEL_IPS
|
||||
server_keys = self.get_keys()
|
||||
peer_dns = _resolve_peer_dns()
|
||||
# Use WG server IP for DNS: ensure_dns_dnat() routes wg0:53 → cell-dns.
|
||||
# This works for both split-tunnel (10.0.x.x in AllowedIPs) and cross-cell peers.
|
||||
addr_str = self._get_configured_address()
|
||||
try:
|
||||
import ipaddress as _ipaddress
|
||||
peer_dns = str(_ipaddress.ip_interface(addr_str).ip)
|
||||
except Exception:
|
||||
peer_dns = _resolve_peer_dns()
|
||||
port = self._get_configured_port()
|
||||
endpoint = server_endpoint if ':' in server_endpoint else f'{server_endpoint}:{port}'
|
||||
addr = peer_ip if '/' in peer_ip else f'{peer_ip}/32'
|
||||
|
||||
Reference in New Issue
Block a user