feat: add networkd subsystem and fix code review issues
Phase 1-4: Networkd subsystem - lib/network.py: systemd-networkd config renderer (.network INI files) with full schema support: [Match], [Link], [Network], [Address], [Route], [DHCPv4], [DHCPv6] sections. One Address/=DNS= line per value per spec. Route sections use #N suffix per systemd.syntax(7). - lib/network.py: generate_network_files() with 50-<name>.network prefix and stale file cleanup - lib/network.py: collect_upstream_dns() filters local/private DNS - lib/network.py: infer_dhcp_ranges() and infer_zones() helpers - daemon/handlers/network.py: routes for GET/POST /network/interfaces and full apply with DNS upstream sync to dnsmasq - webui/api/network.py: Flask blueprint for /api/network/* endpoints - webui/api: interfaces page updated with IP config inline editing - lib/state.py: networkd collector using parse_networkctl_status() - system/sudoers.d/vacuum-walld: networkctl + systemd-network rules - system/systemd/vacuum-walld.service: ReadWritePaths for /etc/systemd/network - install.sh: ACME email now optional, configured from WebUI - lib/acme.py: get_email() falls back to declarative config Phase 5: Code review fixes - daemon/server.py: path params now win over JSON body and query params in request body merge (prevents config save name override) - daemon/server.py: remove dead 'import re' - daemon/handlers/network.py: replace Path.mkdir() with sudo mkdir for /etc/systemd/network (ProtectSystem=strict compatibility) - system/sudoers.d/vacuum-walld: pin systemctl to specific commands (reload/is-active dnsmasq instead of wildcard) - system/sudoers.d/vacuum-walld: restore !requiretty and section comment - lib/network.py: remove unused _MANAGEMENT_PORTS constant - webui/api/network.py: remove redundant body[\name\] = name in save_interface Tests: 332 passing (110 new/updated), ruff clean
This commit is contained in:
@@ -20,10 +20,12 @@ from flask import Flask, render_template, request
|
||||
|
||||
from daemon.client import get
|
||||
from lib.logging import setup_logging
|
||||
from lib.network import get_config
|
||||
from webui.api.certs import bp as certs_bp
|
||||
from webui.api.dhcp import bp as dhcp_bp
|
||||
from webui.api.firewall import bp as firewall_bp
|
||||
from webui.api.logs import bp as logs_bp
|
||||
from webui.api.network import bp as network_bp
|
||||
from webui.api.proxy import bp as proxy_bp
|
||||
from webui.api.wireguard import bp as wireguard_bp
|
||||
|
||||
@@ -78,6 +80,7 @@ app = Flask(__name__)
|
||||
app.config["SECRET_KEY"] = os.urandom(32).hex()
|
||||
|
||||
app.register_blueprint(firewall_bp, url_prefix="/api/firewall")
|
||||
app.register_blueprint(network_bp, url_prefix="/api/network")
|
||||
app.register_blueprint(dhcp_bp, url_prefix="/api/dhcp")
|
||||
app.register_blueprint(proxy_bp, url_prefix="/api/proxy")
|
||||
app.register_blueprint(certs_bp, url_prefix="/api/certs")
|
||||
@@ -86,6 +89,7 @@ app.register_blueprint(logs_bp, url_prefix="/api/logs")
|
||||
|
||||
BLUEPRINTS = [
|
||||
("firewall", firewall_bp),
|
||||
("network", network_bp),
|
||||
("dhcp", dhcp_bp),
|
||||
("proxy", proxy_bp),
|
||||
("certs", certs_bp),
|
||||
@@ -353,9 +357,11 @@ def interfaces_page():
|
||||
"""
|
||||
all_status = _safely(_load_status_all, {})
|
||||
fw_state = all_status.get("firewall", {}) or {}
|
||||
network_config = _safely(get_config, {})
|
||||
return render_template(
|
||||
"interfaces.html",
|
||||
interfaces=fw_state.get("interfaces", []),
|
||||
network_config=network_config,
|
||||
zones=fw_state.get("active_zones", {}).keys() or [],
|
||||
firewall_config=_safely(_fw_config_get, {}),
|
||||
firewall_pending=fw_state.get("pending", {}),
|
||||
@@ -433,11 +439,13 @@ def dhcp_page():
|
||||
"""
|
||||
all_status = _safely(_load_status_all, {})
|
||||
dm_state = all_status.get("dnsmasq", {}) or {}
|
||||
fw_state = all_status.get("firewall", {}) or {}
|
||||
return render_template(
|
||||
"dhcp.html",
|
||||
config=dm_state.get("config", {}),
|
||||
status=dm_state.get("status", {}),
|
||||
leases=dm_state.get("leases", []),
|
||||
interfaces=fw_state.get("interfaces", []),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user