add initializer for nft
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s

This commit is contained in:
2026-01-10 18:48:25 +01:00
parent 6285e53c2c
commit 721640817c

View File

@@ -24,7 +24,6 @@ import re
import tempfile
import os
import logging
import sys
# Router and logger ---------------------------------------------------------
router = APIRouter(prefix="/nft", tags=["nftables"])
@@ -87,99 +86,13 @@ def run_nft(args: List[str]) -> Tuple[str, str]:
logger.error("nft failed: %s -- %s", " ".join(e.cmd), e.stderr.strip())
raise RuntimeError(f"nft failed: {' '.join(e.cmd)} -- {e.stderr.strip()}")
def _safe_run(cmd: List[str]) -> Tuple[int, str, str]:
"""Run arbitrary command and capture exitcode, stdout, stderr. Never raise."""
try:
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
return p.returncode, p.stdout.strip(), p.stderr.strip()
except Exception as ex:
return 255, "", f"exception: {ex}"
def _try_modprobe(module: str) -> Tuple[bool, str]:
"""Try to modprobe the kernel module. Returns (ok, message)."""
if not shutil_which("modprobe"):
return False, "modprobe not found"
code, out, err = _safe_run(["modprobe", module])
if code == 0:
return True, out or "ok"
return False, err or f"exit {code}"
def shutil_which(cmd: str) -> Optional[str]:
"""Small local replacement for shutil.which to avoid extra import in some constrained envs."""
from shutil import which
return which(cmd)
def enable_bridge_sysctls() -> None:
"""Attempt to enable sysctls needed for bridge->netfilter interaction."""
changed = []
for key, want in [
("net.bridge.bridge-nf-call-iptables", "1"),
("net.bridge.bridge-nf-call-ip6tables", "1"),
]:
try:
proc = subprocess.run(["/bin/sh", "-c", f"sysctl -w {key}={want}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
if proc.returncode == 0:
logger.info("set sysctl %s=%s", key, want)
changed.append(key)
else:
# fallback: try write to /proc directly (requires root)
try:
path = "/proc/sys/" + key.replace(".", "/")
if os.path.exists(path):
with open(path, "w") as f:
f.write(want)
logger.info("wrote %s to %s", want, path)
changed.append(key)
else:
logger.warning("sysctl %s not present (and sysctl failed): %s", key, proc.stderr.strip())
except Exception as e:
logger.warning("failed to write sysctl %s: %s", key, e)
except Exception as e:
logger.debug("sysctl attempt failed for %s: %s", key, e)
if changed:
logger.debug("sysctls changed: %s", changed)
def try_ensure_kernel_bridge_support() -> None:
"""
Try to load kernel modules and enable sysctls that are commonly required for 'bridge' family nftables use.
This function logs everything but does not raise. It is best-effort.
"""
logger.debug("attempting to ensure kernel bridge/netfilter support (modprobe + sysctl)")
# try modprobe bridge and br_netfilter
for mod in ("bridge", "br_netfilter"):
if shutil_which("modprobe"):
code, out, err = _safe_run(["modprobe", mod])
if code == 0:
logger.info("loaded kernel module: %s", mod)
else:
logger.debug("modprobe %s returned code=%s stderr=%s", mod, code, err)
else:
logger.debug("modprobe not available on this system; skipping module load for %s", mod)
# enable bridge sysctls so bridged IP packets are seen by netfilter
enable_bridge_sysctls()
def ensure_table_chain(family: str, table: str, chain: str) -> None:
"""
Ensure the nft table and chain exist. On serious failures this raises RuntimeError.
This function:
- calls try_ensure_kernel_bridge_support() first (best-effort)
- attempts `nft add table` and `nft add chain`
- if those fail, tries to apply a tiny nft script with `nft -f` to create table and chain
"""
# best-effort kernel prep (modprobe + sysctl)
try:
try_ensure_kernel_bridge_support()
except Exception as e:
logger.debug("kernel prep raised an exception (continuing): %s", e)
logger.info("ensuring table %s.%s exists", family, table)
# Try simple add table first