feat: route PIC services as subdomains of the cell's effective domain
Unit Tests / test (push) Successful in 11m33s
Unit Tests / test (push) Successful in 11m33s
In DDNS modes (pic_ngo, cloudflare, duckdns, http01), all built-in services are now reachable as subdomains of the cell domain, e.g. calendar.pic1.pic.ngo instead of pic1.pic.ngo/calendar. Key changes: - CaddyManager._build_core_service_routes(): new helper generates Caddy named-matcher host blocks for calendar, mail/webmail, files, webdav, and api subdomains within the wildcard TLS server block. - All ACME modes (pic_ngo, cloudflare, duckdns) use the new subdomain matchers; http01 emits a dedicated server block per service. - http01: installed store-plugin services whose name clashes with a core service are skipped to prevent duplicate server blocks. - routes/config.py: ip_utils.write_caddyfile() is skipped in non-LAN modes so LAN Caddy config never overwrites the ACME config. - firewall_manager.generate_corefile(): new split_horizon_zones param adds local authoritative file zones so LAN clients resolve *.pic1.pic.ngo to the internal Caddy IP without hairpin NAT. - NetworkManager.update_split_horizon_zone(): writes the wildcard zone file and regenerates the Corefile with the split-horizon block; called automatically after every identity change in non-LAN mode. - Added @ to allowed record-name chars in update_dns_zone validation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+18
-2
@@ -710,7 +710,8 @@ def _build_acl_block(blocked_peers_by_service: Dict[str, List[str]],
|
||||
|
||||
def generate_corefile(peers: List[Dict[str, Any]], corefile_path: str = COREFILE_PATH,
|
||||
domain: str = 'cell',
|
||||
cell_links: Optional[List[Dict[str, Any]]] = None) -> bool:
|
||||
cell_links: Optional[List[Dict[str, Any]]] = None,
|
||||
split_horizon_zones: Optional[List[str]] = None) -> bool:
|
||||
"""
|
||||
Rewrite the CoreDNS Corefile with per-peer ACL rules and reload plugin.
|
||||
The file is written to corefile_path (API-side path mapped into CoreDNS container).
|
||||
@@ -718,6 +719,10 @@ def generate_corefile(peers: List[Dict[str, Any]], corefile_path: str = COREFILE
|
||||
cell_links: optional list of cell-to-cell DNS forwarding entries, each a dict with
|
||||
'domain' and 'dns_ip' keys (same shape as CellLinkManager.list_connections()).
|
||||
When non-empty, a forwarding stanza is appended for each entry.
|
||||
split_horizon_zones: optional list of FQDNs (e.g. ['pic1.pic.ngo']) for which a
|
||||
local authoritative zone block is added so LAN clients resolve
|
||||
service subdomains to the internal Caddy IP without hairpin NAT.
|
||||
Each zone must have a corresponding zone file under /data/<fqdn>.zone.
|
||||
"""
|
||||
try:
|
||||
# Collect which peers block which services
|
||||
@@ -748,6 +753,17 @@ def generate_corefile(peers: List[Dict[str, Any]], corefile_path: str = COREFILE
|
||||
|
||||
{primary_zone_block}"""
|
||||
|
||||
# Split-horizon zones for DDNS/public domains — LAN clients resolve
|
||||
# *.pic1.pic.ngo to the internal Caddy IP without hairpin NAT.
|
||||
if split_horizon_zones:
|
||||
for sz in split_horizon_zones:
|
||||
corefile += (
|
||||
f'\n{sz} {{\n'
|
||||
f' file /data/{sz}.zone\n'
|
||||
f' log\n'
|
||||
f'}}\n'
|
||||
)
|
||||
|
||||
# Append cell-to-cell DNS forwarding stanzas if provided
|
||||
if cell_links:
|
||||
for link in cell_links:
|
||||
@@ -762,7 +778,7 @@ def generate_corefile(peers: List[Dict[str, Any]], corefile_path: str = COREFILE
|
||||
f' log\n'
|
||||
f'}}\n'
|
||||
)
|
||||
else:
|
||||
elif not split_horizon_zones:
|
||||
corefile += '\n'
|
||||
|
||||
# local.{domain} block intentionally omitted: /data/local.zone does not exist
|
||||
|
||||
Reference in New Issue
Block a user