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
+34 -1
View File
@@ -202,7 +202,8 @@ def _bootstrap_dns():
identity = config_manager.configs.get('_identity', {})
cell_name = identity.get('cell_name', os.environ.get('CELL_NAME', 'mycell'))
domain = identity.get('domain', os.environ.get('CELL_DOMAIN', 'cell'))
network_manager.bootstrap_dns_records(cell_name, domain)
ip_range = identity.get('ip_range', os.environ.get('CELL_IP_RANGE', '172.20.0.0/16'))
network_manager.bootstrap_dns_records(cell_name, domain, ip_range)
except Exception as e:
logger.warning(f"DNS bootstrap failed (non-fatal): {e}")
@@ -476,6 +477,38 @@ def update_config():
all_restarted.extend(cn_result.get('restarted', []))
all_warnings.extend(cn_result.get('warnings', []))
# Apply ip_range change: regenerate DNS records, update virtual IPs + firewall rules
if identity_updates.get('ip_range'):
import ip_utils
new_range = identity_updates['ip_range']
old_range = old_identity.get('ip_range', os.environ.get('CELL_IP_RANGE', '172.20.0.0/16'))
cur_identity = config_manager.configs.get('_identity', {})
cur_cell_name = cur_identity.get('cell_name', os.environ.get('CELL_NAME', 'mycell'))
cur_domain = cur_identity.get('domain', os.environ.get('CELL_DOMAIN', 'cell'))
# Update DNS zone records
ip_result = network_manager.apply_ip_range(new_range, cur_cell_name, cur_domain)
all_restarted.extend(ip_result.get('restarted', []))
all_warnings.extend(ip_result.get('warnings', []))
# Update firewall virtual IPs (iptables) and Caddy virtual IPs
firewall_manager.update_service_ips(new_range)
firewall_manager.ensure_caddy_virtual_ips()
# Try to update docker-compose.yml (only works outside container / dev mode)
compose_candidates = [
os.environ.get('COMPOSE_FILE', ''),
'/app/../docker-compose.yml',
os.path.join(os.path.dirname(__file__), '..', 'docker-compose.yml'),
]
compose_updated = False
for cpath in compose_candidates:
if cpath and ip_utils.update_docker_compose_ips(old_range, new_range, cpath):
all_warnings.append(
'docker-compose.yml updated — run `make restart` to apply container IP changes')
compose_updated = True
break
if not compose_updated:
all_warnings.append(
'docker-compose.yml not updated (run `make reinstall` to apply container IP changes)')
logger.info(f"Updated config, restarted: {all_restarted}")
return jsonify({
"message": "Configuration updated and applied",