Harden deployment, authentication, firewall and media storage failure paths

This commit is contained in:
rope 2026-09-14 11:50:22 +01:00
parent 64ede7bd05
commit 7145a35927
35 changed files with 2529 additions and 499 deletions

View file

@ -377,14 +377,20 @@ def check_devices(items, self_mac, self_ip):
def check_forwards(items):
covered = set()
names = set()
for f in items:
name = str(f.get("name", "")).strip()
proto = str(f.get("protocol", "")).strip()
if not name:
raise Refused("every forward needs a name")
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9 _().-]{0,79}", name):
raise Refused("forward names need 1–80 letters, digits, spaces or _().-")
if name in names:
raise Refused(f"duplicate forward name: {name}")
names.add(name)
if proto not in ("tcp", "udp", "both"):
raise Refused(f"'{proto}' is not tcp, udp or both")
# Accept either key so re-validating an already-normalised entry works.
if f.get("port") and f.get("ports"):
raise Refused("use either port or ports, not both")
spec = str(f.get("port") or f.get("ports") or "").strip()
if not PORT_RE.match(spec):
raise Refused(f"'{spec}' is not a port or range (443, or 26901-26902)")
@ -403,12 +409,18 @@ def check_forwards(items):
else:
f["port"] = bounds[0]
dest = str(f.get("dest", "")).strip()
if dest and not re.fullmatch(r"10\.0\.0\.([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-4])", dest):
raise Refused("forward destination must be a LAN address in 10.0.0.1–254")
if dest:
f["dest"] = dest
else:
f.pop("dest", None)
for p in range(bounds[0], bounds[-1] + 1):
for pr in (("tcp", "udp") if proto == "both" else (proto,)):
if (pr, str(p)) in covered:
raise Refused(f"overlapping forward for {pr}/{p}")
if (pr, str(p)) in REQUIRED_FORWARDS and dest not in ("", ROUTER_IP):
raise Refused(f"{pr}/{p} must still reach the router ({ROUTER_IP})")
covered.add((pr, str(p)))
for need in REQUIRED_FORWARDS:
if need not in covered: