fix json encoding
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 11s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 11s
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
Unrestricted nftables FastAPI service using textual nft commands only.
|
||||
|
||||
Endpoints (high level):
|
||||
GET /firewall/rules -> return textual ruleset (raw nft output)
|
||||
GET /firewall/rules -> return textual ruleset (raw nft output or parsed JSON when nft returned JSON)
|
||||
POST /firewall/rules -> execute a textual nft command (convenience)
|
||||
DELETE /firewall/rules/{handle} -> delete rule by handle via textual nft command
|
||||
POST /firewall/raw -> execute arbitrary textual nft command (executes)
|
||||
@@ -22,6 +22,7 @@ from fastapi import FastAPI, APIRouter, HTTPException, status
|
||||
from pydantic import BaseModel, Field
|
||||
import logging
|
||||
import re
|
||||
import json
|
||||
|
||||
# libnftables (we call textual commands through its .cmd() method)
|
||||
from nftables import Nftables # type: ignore
|
||||
@@ -47,7 +48,7 @@ class NftManager:
|
||||
def __init__(self) -> None:
|
||||
self.nft = Nftables()
|
||||
try:
|
||||
# prefer seeing JSON when python-nftables prints it, but we only use .cmd() below
|
||||
# Prefer JSON output where python-nftables may produce it, but we still treat stdout textually.
|
||||
self.nft.set_json_output(True)
|
||||
except Exception:
|
||||
logger.debug("set_json_output not available or ignored")
|
||||
@@ -98,19 +99,66 @@ class RawCmdRequest(BaseModel):
|
||||
|
||||
|
||||
class RulesetOut(BaseModel):
|
||||
ruleset: str
|
||||
# ruleset can be either a parsed JSON object (dict/list) or a string
|
||||
ruleset: Any
|
||||
|
||||
|
||||
# ---------- PREVIEW helpers (unchanged) ----------
|
||||
def parse_text_cmd_summary(cmd: str) -> Dict[str, Any]:
|
||||
summary: Dict[str, Any] = {"operation": None, "family": None, "table": None, "chain": None, "remainder": cmd}
|
||||
s = cmd.strip()
|
||||
tokens = s.split()
|
||||
if len(tokens) >= 1:
|
||||
summary["operation"] = tokens[0].lower()
|
||||
m = re.match(r'^(add|insert|delete|replace)\s+rule\s+(\S+)\s+(\S+)\s+(\S+)\b', s, flags=re.I)
|
||||
if m:
|
||||
summary["operation"] = m.group(1).lower()
|
||||
summary["family"] = m.group(2)
|
||||
summary["table"] = m.group(3)
|
||||
summary["chain"] = m.group(4)
|
||||
summary["remainder"] = s[m.end():].strip()
|
||||
return summary
|
||||
m2 = re.match(r'^(delete)\s+rule\s+(\S+)\s+(\S+)\s+(\S+)\s+handle\s+(\d+)', s, flags=re.I)
|
||||
if m2:
|
||||
summary["operation"] = m2.group(1).lower()
|
||||
summary["family"] = m2.group(2)
|
||||
summary["table"] = m2.group(3)
|
||||
summary["chain"] = m2.group(4)
|
||||
summary["remainder"] = f"handle {m2.group(5)}"
|
||||
return summary
|
||||
try:
|
||||
idx = next(i for i,t in enumerate(tokens) if t.lower() == "rule")
|
||||
if len(tokens) > idx + 3:
|
||||
summary["family"] = tokens[idx+1]
|
||||
summary["table"] = tokens[idx+2]
|
||||
summary["chain"] = tokens[idx+3]
|
||||
summary["remainder"] = " ".join(tokens[idx+4:]) if len(tokens) > idx+4 else ""
|
||||
except StopIteration:
|
||||
pass
|
||||
return summary
|
||||
|
||||
|
||||
# ---------- Routes ----------
|
||||
|
||||
# GET /firewall/rules -> textual nft ruleset output
|
||||
# GET /firewall/rules -> textual nft ruleset output or parsed JSON when applicable
|
||||
@router.get("/rules", response_model=RulesetOut)
|
||||
def list_rules():
|
||||
"""
|
||||
Returns the textual nft ruleset output (as a single string).
|
||||
Use clients to parse as needed.
|
||||
Returns the textual nft ruleset output (as a string) or native JSON if nft returned JSON.
|
||||
Clients should handle both cases.
|
||||
"""
|
||||
try:
|
||||
text = mgr.list_rules_text()
|
||||
if text is None:
|
||||
return {"ruleset": None}
|
||||
s = text.strip()
|
||||
# Try to interpret as JSON. nft will produce JSON when set_json_output(True) is active.
|
||||
try:
|
||||
parsed = json.loads(s)
|
||||
# Return parsed JSON (will be serialized as JSON by FastAPI)
|
||||
return {"ruleset": parsed}
|
||||
except json.JSONDecodeError:
|
||||
# Not JSON — return raw text
|
||||
return {"ruleset": text}
|
||||
except NftError as e:
|
||||
logger.exception("list_rules failed")
|
||||
|
||||
Reference in New Issue
Block a user