feat(api, web): enhance port ACL handling and documentation
- Updated the `evofw-firewall.sh` script to refine the port ACL logic, ensuring the correct order of operations for deny and allow rules. - Introduced a new structure for port ACL rows in the UI, allowing for better management of system and EvoFW rules. - Enhanced the documentation to clarify the new port ACL behavior, including implicit drops for open ports and the distinction between EvoFW and system rules. - Improved the handling of port ranges and source addresses in the UI, ensuring accurate representation of firewall rules. These changes improve the functionality and clarity of port ACL management, enhancing user experience and system reliability.
This commit is contained in:
@@ -438,7 +438,7 @@ apply_nft() {
|
||||
done
|
||||
((${#batch[@]})) && nft_add_chunk "$table" "$name" allow_v4 "${batch[@]}"
|
||||
|
||||
# Unified chain: deny → allow → default_action
|
||||
# Unified chain: deny → Port ACL (close/open/implicit) → allow → default_action
|
||||
if [[ "$DEFAULT_ACTION" == "drop" ]]; then
|
||||
nft add chain "$table" "$name" input '{ type filter hook input priority 0; policy drop; }'
|
||||
else
|
||||
@@ -461,9 +461,9 @@ apply_nft() {
|
||||
else
|
||||
nft add rule "$table" "$name" input ip saddr @deny_v4 counter drop
|
||||
fi
|
||||
nft add rule "$table" "$name" input ip saddr @allow_v4 counter accept
|
||||
# Port ACL: close (drop) then open (accept), before default.
|
||||
# Port ACL before L3 allow so open+list is exclusive (implicit drop per open port).
|
||||
apply_nft_port_acl "$table" "$name"
|
||||
nft add rule "$table" "$name" input ip saddr @allow_v4 counter accept
|
||||
if [[ "$DEFAULT_ACTION" == "drop" ]]; then
|
||||
nft add rule "$table" "$name" input counter drop
|
||||
else
|
||||
@@ -499,41 +499,68 @@ try:
|
||||
except Exception:
|
||||
rules = []
|
||||
safe_id = re.compile(r"[^a-zA-Z0-9_]")
|
||||
for r in rules:
|
||||
|
||||
def parse_rule(r):
|
||||
rid = safe_id.sub("_", str(r.get("id") or "x"))[:40]
|
||||
action = r.get("action") or "open"
|
||||
proto = r.get("protocol") or "tcp"
|
||||
if proto not in ("tcp", "udp"):
|
||||
continue
|
||||
return None
|
||||
ps = int(r.get("port_start") or 0)
|
||||
pe = int(r.get("port_end") or ps)
|
||||
if ps < 1 or pe > 65535 or pe < ps:
|
||||
continue
|
||||
return None
|
||||
cidrs = [c for c in (r.get("src_cidrs") or []) if c and ":" not in c]
|
||||
if not cidrs:
|
||||
continue
|
||||
verdict = "drop" if action == "close" else "accept"
|
||||
return None
|
||||
dport = f"{ps}" if ps == pe else f"{ps}-{pe}"
|
||||
comment = f"evofw-port-{rid}"
|
||||
is_all = any(c in ("0.0.0.0/0", "0.0.0.0") for c in cidrs)
|
||||
if is_all:
|
||||
return {
|
||||
"rid": rid,
|
||||
"action": action,
|
||||
"proto": proto,
|
||||
"dport": dport,
|
||||
"cidrs": cidrs,
|
||||
"is_all": any(c in ("0.0.0.0/0", "0.0.0.0") for c in cidrs),
|
||||
}
|
||||
|
||||
def emit(p):
|
||||
verdict = "drop" if p["action"] == "close" else "accept"
|
||||
comment = f"evofw-port-{p['rid']}"
|
||||
proto, dport = p["proto"], p["dport"]
|
||||
if p["is_all"]:
|
||||
print(f'nft add rule inet evofw input {proto} dport {dport} counter {verdict} comment "{comment}"')
|
||||
continue
|
||||
setname = f"port_src_{rid}"
|
||||
return
|
||||
setname = f"port_src_{p['rid']}_{p['proto']}"
|
||||
print(f"nft add set inet evofw {setname} '{{ type ipv4_addr; flags interval; }}'")
|
||||
chunk = []
|
||||
for c in cidrs:
|
||||
for c in p["cidrs"]:
|
||||
chunk.append(c)
|
||||
if len(chunk) >= 32:
|
||||
joined = ", ".join(chunk)
|
||||
print(f"nft add element inet evofw {setname} '{{ {joined} }}'")
|
||||
print(f"nft add element inet evofw {setname} '{{ {', '.join(chunk)} }}'")
|
||||
chunk = []
|
||||
if chunk:
|
||||
joined = ", ".join(chunk)
|
||||
print(f"nft add element inet evofw {setname} '{{ {joined} }}'")
|
||||
print(f"nft add element inet evofw {setname} '{{ {', '.join(chunk)} }}'")
|
||||
print(
|
||||
f'nft add rule inet evofw input ip saddr @{setname} {proto} dport {dport} counter {verdict} comment "{comment}"'
|
||||
)
|
||||
|
||||
parsed = [p for p in (parse_rule(r) for r in rules) if p]
|
||||
closes = [p for p in parsed if p["action"] == "close"]
|
||||
opens = [p for p in parsed if p["action"] != "close"]
|
||||
for p in closes:
|
||||
emit(p)
|
||||
for p in opens:
|
||||
emit(p)
|
||||
seen = set()
|
||||
for p in opens:
|
||||
key = (p["proto"], p["dport"])
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
comment = f"evofw-port-implicit-{p['proto']}-{p['dport']}"
|
||||
print(
|
||||
f'nft add rule inet evofw input {p["proto"]} dport {p["dport"]} counter drop comment "{comment}"'
|
||||
)
|
||||
PY
|
||||
local cmd
|
||||
while IFS= read -r cmd; do
|
||||
|
||||
Reference in New Issue
Block a user