fix: DNS split-horizon in DDNS mode, service access filter, health check, verbosity persistence
Unit Tests / test (push) Successful in 7m32s
Unit Tests / test (push) Successful in 7m32s
- DNS (critical): add _configured_dns_params() that returns (primary_domain, split_horizon_zones) from config_manager so all apply_all_dns_rules() callers pass the correct primary zone (e.g. 'pic.ngo') and split-horizon list (e.g. ['pic1.pic.ngo']) instead of the FQDN as the primary — fixes DNS_PROBE_FINISHED_BAD_CONFIG for all external domains when on VPN - firewall_manager: add split_horizon_zones param to apply_all_dns_rules() and forward it to generate_corefile() - Peers: filter service_access list to installed services only; peers.py derives valid services from config_manager.get_installed_services() with the email→mail ID mapping; Peers.jsx fetches from /api/store/installed and filters the checkboxes and defaults accordingly - Health check: fix file_manager→'files' ID mapping so files service health is checked when installed (was silently skipped due to 'file' vs 'files') - Verbosity persistence: move log_levels.json from non-mounted /app/api/config/ to CONFIG_DIR (/app/config/) which maps to config/api/ on the host; both load (managers.py) and save (routes/services.py) updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+19
-4
@@ -298,6 +298,19 @@ def _configured_domain() -> str:
|
||||
return identity.get('domain_name') or identity.get('domain', 'cell')
|
||||
|
||||
|
||||
def _configured_dns_params():
|
||||
"""Return (primary_domain, split_horizon_zones) for Corefile generation.
|
||||
|
||||
In DDNS mode the primary CoreDNS zone is the parent domain (e.g. 'pic.ngo')
|
||||
and the cell's FQDN (e.g. 'pic1.pic.ngo') is a separate split-horizon block
|
||||
so LAN clients resolve *.pic1.pic.ngo to the internal Caddy IP.
|
||||
In LAN mode both values are the same so split_horizon_zones is empty.
|
||||
"""
|
||||
primary = config_manager.get_internal_domain()
|
||||
effective = config_manager.get_effective_domain()
|
||||
return primary, ([effective] if effective != primary else [])
|
||||
|
||||
|
||||
def _restore_cell_wg_peers(cell_links):
|
||||
"""Re-add any cell link [Peer] blocks that are missing from wg0.conf.
|
||||
|
||||
@@ -359,8 +372,10 @@ def _apply_startup_enforcement():
|
||||
# (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)
|
||||
_dns_primary, _dns_szones = _configured_dns_params()
|
||||
firewall_manager.apply_all_dns_rules(peers, COREFILE_PATH, _dns_primary,
|
||||
cell_links=cell_links,
|
||||
split_horizon_zones=_dns_szones)
|
||||
logger.info(f"Applied enforcement rules for {len(peers)} peers, {len(cell_links)} cells on startup")
|
||||
# Phase 3: reapply policy routing rules for peers whose internet traffic is
|
||||
# routed through an exit cell (ip rule entries don't survive container restart)
|
||||
@@ -529,12 +544,12 @@ def perform_health_check():
|
||||
# email/calendar/files are optional store services — only check them when installed
|
||||
_installed_store_ids = set(config_manager.get_installed_services())
|
||||
_OPTIONAL_STORE_MANAGERS = frozenset({'email_manager', 'calendar_manager', 'file_manager'})
|
||||
_MANAGER_TO_STORE_ID = {'email_manager': 'email', 'calendar_manager': 'calendar', 'file_manager': 'files'}
|
||||
|
||||
# Get health from each service
|
||||
for service_name in service_bus.list_services():
|
||||
if service_name in _OPTIONAL_STORE_MANAGERS:
|
||||
# Map manager name to store service id (strip _manager suffix)
|
||||
store_id = service_name.replace('_manager', '')
|
||||
store_id = _MANAGER_TO_STORE_ID[service_name]
|
||||
if store_id not in _installed_store_ids:
|
||||
continue
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user