feat: dynamic ip_range propagation to DNS, firewall, and docker-compose

When ip_range changes in Settings, the new subnet is now applied to:
- DNS zone records (network_manager.apply_ip_range)
- Caddy virtual IPs (firewall_manager.ensure_caddy_virtual_ips)
- iptables per-service rules (firewall_manager.update_service_ips)
- docker-compose.yml static IPs if writable (ip_utils.update_docker_compose_ips)

New module ip_utils.py derives all container IPs from the subnet using
fixed offsets so the entire stack stays consistent from one setting.

321 tests pass (72 new tests added for ip_utils, apply_ip_range, update_service_ips).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 10:26:21 -04:00
parent 8e741b5729
commit 615448b875
7 changed files with 473 additions and 14 deletions
+12 -2
View File
@@ -12,14 +12,24 @@ from typing import Dict, List, Any, Optional
logger = logging.getLogger(__name__)
# Virtual IPs assigned to Caddy per service — must match Caddyfile listeners
SERVICE_IPS = {
# Virtual IPs assigned to Caddy per service — must match Caddyfile listeners.
# Populated at import time from the default subnet; call update_service_ips()
# whenever ip_range changes so all downstream callers see the new values.
SERVICE_IPS: Dict[str, str] = {
'calendar': '172.20.0.21',
'files': '172.20.0.22',
'mail': '172.20.0.23',
'webdav': '172.20.0.24',
}
def update_service_ips(ip_range: str) -> None:
"""Recalculate SERVICE_IPS from the new subnet and update in-place."""
from ip_utils import get_virtual_ips
new_ips = get_virtual_ips(ip_range)
SERVICE_IPS.clear()
SERVICE_IPS.update(new_ips)
# Internal RFC-1918 ranges (peer traffic stays inside these = cell-only access)
PRIVATE_NETS = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']