dhcp: auto-populate gateway from interface IP for DHCP ranges
Add get_interface_ip() helper to resolve an interface's IPv4 address via 'ip -o addr show'. Use it to back-propagate gateway into DHCP ranges so clients receive their default route. - set_dhcp_range() resolves gateway: explicit > existing range > iface IP - DnsToFirewallSync and FirewallToDhcpSync sync ensure gateways are set - Remove automatic masquerade toggle from DnsToFirewallSync - Fix dnsmasq lease file path to /var/lib/misc/dnsmasq.leases - Rename lease state field expires_at -> expires (ISO string) - Add 'ip -o addr show' to sudo whitelist
This commit is contained in:
@@ -30,6 +30,7 @@ from lib.common import (
|
|||||||
config_hash,
|
config_hash,
|
||||||
deep_merge,
|
deep_merge,
|
||||||
ensure_dirs,
|
ensure_dirs,
|
||||||
|
get_interface_ip,
|
||||||
load_json,
|
load_json,
|
||||||
run,
|
run,
|
||||||
save_json,
|
save_json,
|
||||||
@@ -44,7 +45,7 @@ DATA_DIR = PROJECT_DIR / "data" / "dnsmasq"
|
|||||||
CONFIG_PATH = CONFIG_DIR / "config.json"
|
CONFIG_PATH = CONFIG_DIR / "config.json"
|
||||||
FRAGMENTS_DIR = DATA_DIR / "fragments"
|
FRAGMENTS_DIR = DATA_DIR / "fragments"
|
||||||
DNSMASQ_CONF = "/etc/dnsmasq.d/vacuum-wall.conf"
|
DNSMASQ_CONF = "/etc/dnsmasq.d/vacuum-wall.conf"
|
||||||
LEASE_FILE = "/var/lib/dnsmasq/dnsmasq.leases"
|
LEASE_FILE = "/var/lib/misc/dnsmasq.leases"
|
||||||
|
|
||||||
ENV = Environment(
|
ENV = Environment(
|
||||||
loader=FileSystemLoader(str(PROJECT_DIR / "system")),
|
loader=FileSystemLoader(str(PROJECT_DIR / "system")),
|
||||||
@@ -225,15 +226,30 @@ def set_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
|||||||
Endpoint: POST /dnsmasq/ranges/add
|
Endpoint: POST /dnsmasq/ranges/add
|
||||||
|
|
||||||
Add or update a DHCP pool range by interface. Raises ValueError on invalid input.
|
Add or update a DHCP pool range by interface. Raises ValueError on invalid input.
|
||||||
|
Auto-populates ``gateway`` from the interface's IPv4 address when not provided.
|
||||||
"""
|
"""
|
||||||
if not body:
|
if not body:
|
||||||
raise ValueError("Request body required")
|
raise ValueError("Request body required")
|
||||||
iface = body.get("interface", "").strip() or ""
|
iface = (body.get("interface") or "").strip()
|
||||||
start = body.get("start", "").strip()
|
start = body.get("start", "").strip()
|
||||||
end = body.get("end", "").strip()
|
end = body.get("end", "").strip()
|
||||||
lease_time = body.get("lease_time", "12h")
|
lease_time = body.get("lease_time", "12h")
|
||||||
if not start or not end:
|
if not start or not end:
|
||||||
raise ValueError("'start' and 'end' are required")
|
raise ValueError("'start' and 'end' are required")
|
||||||
|
|
||||||
|
# Resolve gateway: explicit value > existing range value > interface IP
|
||||||
|
gateway = body.get("gateway")
|
||||||
|
if not gateway:
|
||||||
|
# Check existing range for same interface
|
||||||
|
cfg_tmp = _get_config()
|
||||||
|
for r in cfg_tmp["dhcp"]["ranges"]:
|
||||||
|
if r.get("interface") == iface:
|
||||||
|
gateway = r.get("gateway")
|
||||||
|
break
|
||||||
|
# Fall back to interface's own IP
|
||||||
|
if not gateway and iface:
|
||||||
|
gateway = get_interface_ip(iface)
|
||||||
|
|
||||||
cfg = _get_config()
|
cfg = _get_config()
|
||||||
ranges = cfg["dhcp"]["ranges"]
|
ranges = cfg["dhcp"]["ranges"]
|
||||||
found = False
|
found = False
|
||||||
@@ -245,8 +261,8 @@ def set_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
|||||||
"end": end,
|
"end": end,
|
||||||
"lease_time": lease_time,
|
"lease_time": lease_time,
|
||||||
}
|
}
|
||||||
if body.get("gateway"):
|
if gateway:
|
||||||
ranges[i]["gateway"] = body["gateway"]
|
ranges[i]["gateway"] = gateway
|
||||||
if body.get("dns"):
|
if body.get("dns"):
|
||||||
ranges[i]["dns"] = body["dns"]
|
ranges[i]["dns"] = body["dns"]
|
||||||
found = True
|
found = True
|
||||||
@@ -258,8 +274,8 @@ def set_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
|||||||
"end": end,
|
"end": end,
|
||||||
"lease_time": lease_time,
|
"lease_time": lease_time,
|
||||||
}
|
}
|
||||||
if body.get("gateway"):
|
if gateway:
|
||||||
entry["gateway"] = body["gateway"]
|
entry["gateway"] = gateway
|
||||||
if body.get("dns"):
|
if body.get("dns"):
|
||||||
entry["dns"] = body["dns"]
|
entry["dns"] = body["dns"]
|
||||||
ranges.append(entry)
|
ranges.append(entry)
|
||||||
|
|||||||
@@ -169,11 +169,31 @@ def ensure_dirs(*dirs: Path) -> None:
|
|||||||
d.mkdir(parents=True, exist_ok=True)
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_interface_ip(iface: str) -> str | None:
|
||||||
|
"""Return the primary IPv4 address of *iface* (without CIDR), or ``None``.
|
||||||
|
|
||||||
|
Uses ``ip -o addr show`` which is in the daemon sudo whitelist.
|
||||||
|
"""
|
||||||
|
if not iface:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
raw = run(["ip", "-o", "addr", "show", iface], sudo=True)
|
||||||
|
for line in raw.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
# -o format: "NUM: IFACE inet/6 ADDR/MASK ..."
|
||||||
|
if len(parts) >= 4 and parts[2] == "inet":
|
||||||
|
return parts[3].split("/", 1)[0]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"_APPLY_HASH_KEY",
|
"_APPLY_HASH_KEY",
|
||||||
"config_hash",
|
"config_hash",
|
||||||
"deep_merge",
|
"deep_merge",
|
||||||
"ensure_dirs",
|
"ensure_dirs",
|
||||||
|
"get_interface_ip",
|
||||||
"load_json",
|
"load_json",
|
||||||
"run",
|
"run",
|
||||||
"run_proc",
|
"run_proc",
|
||||||
|
|||||||
+2
-2
@@ -523,7 +523,7 @@ def _collect_dnsmasq() -> dict[str, Any]:
|
|||||||
CONFIG_DIR = PROJECT_DIR / "config" / "dnsmasq"
|
CONFIG_DIR = PROJECT_DIR / "config" / "dnsmasq"
|
||||||
CONFIG_PATH = CONFIG_DIR / "config.json"
|
CONFIG_PATH = CONFIG_DIR / "config.json"
|
||||||
DNSMASQ_CONF = "/etc/dnsmasq.d/vacuum-wall.conf"
|
DNSMASQ_CONF = "/etc/dnsmasq.d/vacuum-wall.conf"
|
||||||
LEASE_FILE = "/var/lib/dnsmasq/dnsmasq.leases"
|
LEASE_FILE = "/var/lib/misc/dnsmasq.leases"
|
||||||
|
|
||||||
DEFAULT_CFG: dict[str, Any] = {
|
DEFAULT_CFG: dict[str, Any] = {
|
||||||
"dhcp": {"ranges": [], "static_leases": []},
|
"dhcp": {"ranges": [], "static_leases": []},
|
||||||
@@ -575,7 +575,7 @@ def _collect_dnsmasq() -> dict[str, Any]:
|
|||||||
ts = None
|
ts = None
|
||||||
leases.append(
|
leases.append(
|
||||||
{
|
{
|
||||||
"expires_at": ts,
|
"expires": ts.isoformat() if ts else "",
|
||||||
"mac": parts[1],
|
"mac": parts[1],
|
||||||
"ip": parts[2],
|
"ip": parts[2],
|
||||||
"hostname": parts[3] if len(parts) > 3 else "",
|
"hostname": parts[3] if len(parts) > 3 else "",
|
||||||
|
|||||||
+95
-22
@@ -10,6 +10,8 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from lib.common import get_interface_ip
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -235,7 +237,13 @@ def _safe_name(obj: object) -> str:
|
|||||||
|
|
||||||
|
|
||||||
class DnsToFirewallSync:
|
class DnsToFirewallSync:
|
||||||
"""Sync subscriber: dnsmasq config_saved → update firewall config."""
|
"""Sync subscriber: dnsmasq config_saved → update firewall config.
|
||||||
|
|
||||||
|
Adds/removes dhcp and dns services on firewall zones based on
|
||||||
|
which interfaces have DHCP ranges. Back-propagates: ensures
|
||||||
|
DHCP ranges carry the gateway (interface IP) so clients get
|
||||||
|
their default route.
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def on_dnsmasq_config_saved(cls, event: SyncEvent) -> SyncResult | None:
|
def on_dnsmasq_config_saved(cls, event: SyncEvent) -> SyncResult | None:
|
||||||
@@ -243,7 +251,12 @@ class DnsToFirewallSync:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from lib.dnsmasq import get_config as _get_dnsmasq_cfg
|
from lib.dnsmasq import (
|
||||||
|
get_config as _get_dnsmasq_cfg,
|
||||||
|
)
|
||||||
|
from lib.dnsmasq import (
|
||||||
|
save_config as _save_dnsmasq_cfg,
|
||||||
|
)
|
||||||
from lib.firewall import get_config as _get_fw_cfg
|
from lib.firewall import get_config as _get_fw_cfg
|
||||||
from lib.firewall import save_config as _save_fw_cfg
|
from lib.firewall import save_config as _save_fw_cfg
|
||||||
|
|
||||||
@@ -280,9 +293,10 @@ class DnsToFirewallSync:
|
|||||||
continue
|
continue
|
||||||
zone_ifaces[zname] = set(zdata.get("interfaces", []))
|
zone_ifaces[zname] = set(zdata.get("interfaces", []))
|
||||||
|
|
||||||
changes: list[str] = []
|
firewall_changes: list[str] = []
|
||||||
|
dnsmasq_changes: list[str] = []
|
||||||
|
|
||||||
# For active zones: ensure dhcp/dns services and masquerade
|
# For active zones: ensure dhcp/dns services
|
||||||
for zname, _ifaces in active_zones.items():
|
for zname, _ifaces in active_zones.items():
|
||||||
zdata = zones.get(zname, {})
|
zdata = zones.get(zname, {})
|
||||||
if not isinstance(zdata, dict):
|
if not isinstance(zdata, dict):
|
||||||
@@ -295,15 +309,14 @@ class DnsToFirewallSync:
|
|||||||
added.append(svc)
|
added.append(svc)
|
||||||
zdata["services"] = services
|
zdata["services"] = services
|
||||||
if added:
|
if added:
|
||||||
changes.append(
|
firewall_changes.append(
|
||||||
f"Added {', '.join(added)} service(s) to zone '{zname}'"
|
f"Added {', '.join(added)} service(s) to zone '{zname}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not zdata.get("masquerade"):
|
# Back-propagate: ensure DHCP ranges have gateway set
|
||||||
zdata["masquerade"] = True
|
cls._ensure_gateways(dnsmasq_cfg, zname, _ifaces, dnsmasq_changes)
|
||||||
changes.append(f"Enabled masquerade on zone '{zname}'")
|
|
||||||
|
|
||||||
# For inactive zones: remove dhcp/dns services, disable masquerade
|
# For inactive zones: remove dhcp/dns services
|
||||||
for zname, zdata in zones.items():
|
for zname, zdata in zones.items():
|
||||||
if not isinstance(zdata, dict):
|
if not isinstance(zdata, dict):
|
||||||
continue
|
continue
|
||||||
@@ -320,26 +333,52 @@ class DnsToFirewallSync:
|
|||||||
removed.append(svc)
|
removed.append(svc)
|
||||||
zdata["services"] = services
|
zdata["services"] = services
|
||||||
if removed:
|
if removed:
|
||||||
changes.append(
|
firewall_changes.append(
|
||||||
f"Removed {', '.join(removed)} service(s) from zone '{zname}'"
|
f"Removed {', '.join(removed)} service(s) from zone '{zname}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
if zdata.get("masquerade"):
|
result = SyncResult(changes=firewall_changes + dnsmasq_changes)
|
||||||
zdata["masquerade"] = False
|
|
||||||
changes.append(f"Disabled masquerade on zone '{zname}'")
|
|
||||||
|
|
||||||
if changes:
|
# Save firewall config if changed
|
||||||
|
if firewall_changes:
|
||||||
fw_cfg["zones"] = zones
|
fw_cfg["zones"] = zones
|
||||||
_save_fw_cfg(fw_cfg)
|
_save_fw_cfg(fw_cfg)
|
||||||
return SyncResult(
|
result.affected_subsystems.append("firewall")
|
||||||
affected_subsystems=["firewall"],
|
|
||||||
changes=changes,
|
# Save dnsmasq config if gateways were set
|
||||||
)
|
if dnsmasq_changes:
|
||||||
return SyncResult(changes=changes)
|
_save_dnsmasq_cfg(dnsmasq_cfg)
|
||||||
|
result.affected_subsystems.append("dnsmasq")
|
||||||
|
|
||||||
|
return result
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("DnsToFirewallSync failed")
|
logger.exception("DnsToFirewallSync failed")
|
||||||
return SyncResult()
|
return SyncResult()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _ensure_gateways(
|
||||||
|
dnsmasq_cfg: dict[str, Any],
|
||||||
|
zone_name: str,
|
||||||
|
zone_ifaces: set[str],
|
||||||
|
changes: list[str],
|
||||||
|
) -> None:
|
||||||
|
"""Ensure DHCP ranges for *zone_ifaces* carry the gateway (interface IP)."""
|
||||||
|
from lib.common import get_interface_ip
|
||||||
|
|
||||||
|
ranges = dnsmasq_cfg.get("dhcp", {}).get("ranges", [])
|
||||||
|
for r in ranges:
|
||||||
|
iface = r.get("interface", "")
|
||||||
|
if iface not in zone_ifaces:
|
||||||
|
continue
|
||||||
|
if r.get("gateway"):
|
||||||
|
continue
|
||||||
|
ip = get_interface_ip(iface)
|
||||||
|
if ip:
|
||||||
|
r["gateway"] = ip
|
||||||
|
changes.append(
|
||||||
|
f"Set DHCP gateway {ip} for range on '{iface}' (zone '{zone_name}')"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WgToFirewallSync:
|
class WgToFirewallSync:
|
||||||
"""Sync subscriber: wireguard config_saved → update firewall config."""
|
"""Sync subscriber: wireguard config_saved → update firewall config."""
|
||||||
@@ -510,8 +549,9 @@ class FirewallToDhcpSync:
|
|||||||
"""Sync subscriber: firewall config_saved → sync dnsmasq DHCP ranges.
|
"""Sync subscriber: firewall config_saved → sync dnsmasq DHCP ranges.
|
||||||
|
|
||||||
Removes DHCP ranges whose interface no longer belongs to any firewall
|
Removes DHCP ranges whose interface no longer belongs to any firewall
|
||||||
zone. Also logs warnings for zones with dhcp service but no range
|
zone. When masquerade is enabled on a zone, ensures DHCP ranges on
|
||||||
(cannot auto-create a range without knowing IP addresses).
|
that zone's interfaces carry the gateway (interface IP).
|
||||||
|
Logs warnings for zones with dhcp service but no range.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -570,6 +610,39 @@ class FirewallToDhcpSync:
|
|||||||
)
|
)
|
||||||
changes.append(f"Removed stale DHCP range on interface '{iface}'")
|
changes.append(f"Removed stale DHCP range on interface '{iface}'")
|
||||||
|
|
||||||
|
# When masquerade is enabled on a zone, ensure DHCP ranges have gateway
|
||||||
|
changed = False
|
||||||
|
for zname, zdata in zones.items():
|
||||||
|
if not isinstance(zdata, dict):
|
||||||
|
continue
|
||||||
|
if not zdata.get("masquerade"):
|
||||||
|
continue
|
||||||
|
# Find DHCP ranges on this zone's interfaces
|
||||||
|
z_ifaces = set(zdata.get("interfaces", []))
|
||||||
|
ranges = dnsmasq_cfg.get("dhcp", {}).get("ranges", [])
|
||||||
|
for r in ranges:
|
||||||
|
iface = r.get("interface", "")
|
||||||
|
if not iface or iface not in z_ifaces:
|
||||||
|
continue
|
||||||
|
if r.get("gateway"):
|
||||||
|
continue
|
||||||
|
ip = get_interface_ip(iface)
|
||||||
|
if ip:
|
||||||
|
r["gateway"] = ip
|
||||||
|
changed = True
|
||||||
|
logger.info(
|
||||||
|
"Set DHCP gateway %s on '%s' (masquerade on zone '%s')",
|
||||||
|
ip,
|
||||||
|
iface,
|
||||||
|
zname,
|
||||||
|
)
|
||||||
|
changes.append(
|
||||||
|
f"Set DHCP gateway {ip} for range on '{iface}' (zone '{zname}')"
|
||||||
|
)
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
_save_dnsmasq_cfg(dnsmasq_cfg)
|
||||||
|
|
||||||
# Zones with dhcp service but no matching range (warn only)
|
# Zones with dhcp service but no matching range (warn only)
|
||||||
for iface in sorted(dhcp_service_ifaces - range_ifaces):
|
for iface in sorted(dhcp_service_ifaces - range_ifaces):
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -579,7 +652,7 @@ class FirewallToDhcpSync:
|
|||||||
changes.append(f"Zone has dhcp service on '{iface}' but no DHCP range")
|
changes.append(f"Zone has dhcp service on '{iface}' but no DHCP range")
|
||||||
|
|
||||||
return SyncResult(
|
return SyncResult(
|
||||||
affected_subsystems=["dnsmasq"] if stale_ifaces else [],
|
affected_subsystems=["dnsmasq"] if changed or stale_ifaces else [],
|
||||||
changes=changes,
|
changes=changes,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Defaults:{{ USER_DAEMON_NAME }} secure_path="/usr/local/sbin:/usr/local/bin:/usr
|
|||||||
# Dnsmasq management
|
# Dnsmasq management
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/systemctl restart dnsmasq
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/systemctl restart dnsmasq
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/systemctl is-active dnsmasq
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/systemctl is-active dnsmasq
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/cat /var/lib/dnsmasq/dnsmasq.leases
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/cat /var/lib/misc/dnsmasq.leases
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/mkdir -p /etc/dnsmasq.d
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/mkdir -p /etc/dnsmasq.d
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/cp * /etc/dnsmasq.d/*
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/bin/cp * /etc/dnsmasq.d/*
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ Defaults:{{ USER_DAEMON_NAME }} secure_path="/usr/local/sbin:/usr/local/bin:/usr
|
|||||||
# Network interface queries
|
# Network interface queries
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/sbin/ip -o link show
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/sbin/ip -o link show
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/sbin/ip -o addr show
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/sbin/ip -o addr show
|
||||||
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /usr/sbin/ip -o addr show *
|
||||||
|
|
||||||
# Networkd management
|
# Networkd management
|
||||||
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /bin/networkctl status *
|
{{ USER_DAEMON_NAME }} ALL=(root) NOPASSWD: /bin/networkctl status *
|
||||||
|
|||||||
+1
-3
@@ -229,7 +229,7 @@ class TestDnsToFirewallSync:
|
|||||||
@patch("lib.firewall.save_config")
|
@patch("lib.firewall.save_config")
|
||||||
@patch("lib.firewall.get_config")
|
@patch("lib.firewall.get_config")
|
||||||
@patch("lib.dnsmasq.get_config")
|
@patch("lib.dnsmasq.get_config")
|
||||||
def test_adds_dhcp_dns_masquerade(self, mock_dm_get, mock_fw_get, mock_fw_save):
|
def test_adds_dhcp_dns(self, mock_dm_get, mock_fw_get, mock_fw_save):
|
||||||
mock_dm_get.return_value = {
|
mock_dm_get.return_value = {
|
||||||
"dhcp": {
|
"dhcp": {
|
||||||
"ranges": [
|
"ranges": [
|
||||||
@@ -262,7 +262,6 @@ class TestDnsToFirewallSync:
|
|||||||
saved_cfg = mock_fw_save.call_args[0][0]
|
saved_cfg = mock_fw_save.call_args[0][0]
|
||||||
assert "dhcp" in saved_cfg["zones"]["internal"]["services"]
|
assert "dhcp" in saved_cfg["zones"]["internal"]["services"]
|
||||||
assert "dns" in saved_cfg["zones"]["internal"]["services"]
|
assert "dns" in saved_cfg["zones"]["internal"]["services"]
|
||||||
assert saved_cfg["zones"]["internal"]["masquerade"] is True
|
|
||||||
|
|
||||||
@patch("lib.firewall.save_config")
|
@patch("lib.firewall.save_config")
|
||||||
@patch("lib.firewall.get_config")
|
@patch("lib.firewall.get_config")
|
||||||
@@ -288,7 +287,6 @@ class TestDnsToFirewallSync:
|
|||||||
saved_cfg = mock_fw_save.call_args[0][0]
|
saved_cfg = mock_fw_save.call_args[0][0]
|
||||||
assert "dhcp" not in saved_cfg["zones"]["internal"]["services"]
|
assert "dhcp" not in saved_cfg["zones"]["internal"]["services"]
|
||||||
assert "dns" not in saved_cfg["zones"]["internal"]["services"]
|
assert "dns" not in saved_cfg["zones"]["internal"]["services"]
|
||||||
assert saved_cfg["zones"]["internal"]["masquerade"] is False
|
|
||||||
|
|
||||||
@patch("lib.firewall.save_config")
|
@patch("lib.firewall.save_config")
|
||||||
@patch("lib.firewall.get_config")
|
@patch("lib.firewall.get_config")
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default definePage({
|
|||||||
onClick=${() => MultiSelectModal({
|
onClick=${() => MultiSelectModal({
|
||||||
title: 'Interfaces: ' + name,
|
title: 'Interfaces: ' + name,
|
||||||
url: '/api/firewall/zones/' + enc(name) + '/interfaces',
|
url: '/api/firewall/zones/' + enc(name) + '/interfaces',
|
||||||
options: state.firewall.data?.interfaces || [],
|
options: (state.firewall.data?.interfaces || []).map(i => i.name),
|
||||||
selected: ifacesArr,
|
selected: ifacesArr,
|
||||||
fieldKey: 'interfaces',
|
fieldKey: 'interfaces',
|
||||||
successMsg: 'Interfaces updated',
|
successMsg: 'Interfaces updated',
|
||||||
|
|||||||
Reference in New Issue
Block a user