From d64e0aa73c0d134e8a81cb2be7d633c3bdbefb6c Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Sat, 19 Oct 2024 18:39:42 +0200 Subject: [PATCH] add: filtering table of firewall + InterfaceSelector frontend fixes and improves --- Dockerfile | 15 ++-- backend/app.py | 23 ++++-- backend/modules/firewall/models.py | 10 ++- backend/modules/firewall/nftables.py | 78 ++++++++++++------- backend/routers/firewall.py | 12 ++- backend/utils/__init__.py | 12 +-- backend/utils/loader.py | 13 +--- .../src/components/Firewall/ModeSelector.tsx | 18 +++-- frontend/src/components/Firewall/utils.ts | 6 ++ frontend/src/components/InterfaceInput.tsx | 18 +++-- frontend/src/js/utils.tsx | 2 +- frontend/src/pages/Firewall/index.tsx | 26 +++++-- 12 files changed, 147 insertions(+), 86 deletions(-) diff --git a/Dockerfile b/Dockerfile index a589938..c5b567f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,20 +8,19 @@ FROM --platform=$BUILDPLATFORM oven/bun AS frontend WORKDIR /app ADD ./frontend/package.json . ADD ./frontend/bun.lockb . -RUN bun install +RUN bun i COPY ./frontend/ . RUN bun run build #Building main conteiner FROM --platform=$TARGETARCH debian:stable-slim AS base -RUN apt-get update -qq && apt-get upgrade -qq -RUN apt-get install -qq python3-pip build-essential -RUN apt-get install -qq git libpcre2-dev libnetfilter-queue-dev -RUN apt-get install -qq libssl-dev libnfnetlink-dev libmnl-dev libcap2-bin -RUN apt-get install -qq make cmake nftables libboost-all-dev autoconf -RUN apt-get install -qq automake cargo libffi-dev libvectorscan-dev libtins-dev -RUN apt-get install -qq python3-nftables +RUN apt-get update -qq && apt-get upgrade -qq && \ + apt-get install -qq python3-pip build-essential \ + git libpcre2-dev libnetfilter-queue-dev libssl-dev \ + libnfnetlink-dev libmnl-dev libcap2-bin make cmake \ + nftables libboost-all-dev autoconf automake cargo \ + libffi-dev libvectorscan-dev libtins-dev python3-nftables WORKDIR /tmp/ RUN git clone --single-branch --branch release https://github.com/jpcre2/jpcre2 diff --git a/backend/app.py b/backend/app.py index 6ba5685..d4379ef 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,6 +1,6 @@ import uvicorn, secrets, utils -import os, asyncio -from fastapi import FastAPI, HTTPException, Depends, APIRouter +import os, asyncio, logging +from fastapi import FastAPI, HTTPException, Depends, APIRouter, Request from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import jwt from passlib.context import CryptContext @@ -34,7 +34,14 @@ app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan) utils.socketio = SocketManager(app, "/sock", socketio_path="") if DEBUG: - app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + def APP_STATUS(): return "init" if db.get("password") is None else "run" def JWT_SECRET(): return db.get("secret") @@ -132,7 +139,10 @@ async def startup_main(): db.init() if os.getenv("HEX_SET_PSW"): set_psw(bytes.fromhex(os.getenv("HEX_SET_PSW")).decode()) - sysctl.set() + try: + sysctl.set() + except Exception as e: + logging.error(f"Error setting sysctls: {e}") await startup() if not JWT_SECRET(): db.put("secret", secrets.token_hex(32)) await refresh_frontend() @@ -149,7 +159,10 @@ async def reset_firegex(form: ResetRequest): db.delete() db.init() db.put("secret", secrets.token_hex(32)) - sysctl.set() + try: + sysctl.set() + except Exception as e: + logging.error(f"Error setting sysctls: {e}") await reset(form) await refresh_frontend() return {'status': 'ok'} diff --git a/backend/modules/firewall/models.py b/backend/modules/firewall/models.py index 81e4991..97d2d01 100644 --- a/backend/modules/firewall/models.py +++ b/backend/modules/firewall/models.py @@ -3,7 +3,7 @@ from utils import PortType from pydantic import BaseModel class Rule: - def __init__(self, proto: str, src:str, dst:str, port_src_from:str, port_dst_from:str, port_src_to:str, port_dst_to:str, action:str, mode:str, **other): + def __init__(self, proto: str, src:str, dst:str, port_src_from:str, port_dst_from:str, port_src_to:str, port_dst_to:str, action:str, mode:str, table:str, **_other): self.proto = proto self.src = src self.dst = dst @@ -15,6 +15,7 @@ class Rule: self.input_mode = mode == "in" self.output_mode = mode == "out" self.forward_mode = mode == "forward" + self.table = table @classmethod def from_dict(cls, var: dict): @@ -31,6 +32,10 @@ class Mode(str, Enum): IN = "in", OUT = "out", FORWARD = "forward" + +class Table(str, Enum): + FILTER = "filter" + MANGLE = "mangle" class Action(str, Enum): ACCEPT = "accept", @@ -41,6 +46,7 @@ class RuleModel(BaseModel): active: bool name: str proto: Protocol + table: Table src: str dst: str port_src_from: PortType @@ -48,7 +54,7 @@ class RuleModel(BaseModel): port_src_to: PortType port_dst_to: PortType action: Action - mode:Mode + mode: Mode class RuleFormAdd(BaseModel): rules: list[RuleModel] diff --git a/backend/modules/firewall/nftables.py b/backend/modules/firewall/nftables.py index 64b66d4..cd3d637 100644 --- a/backend/modules/firewall/nftables.py +++ b/backend/modules/firewall/nftables.py @@ -7,12 +7,16 @@ class FiregexTables(NFTableManager): rules_chain_out = "firegex_firewall_rules_out" rules_chain_fwd = "firegex_firewall_rules_fwd" filter_table = "filter" + mangle_table = "mangle" def init_comands(self, policy:str=Action.ACCEPT, opt: FirewallSettings|None = None): rules = [ {"add":{"table":{"name":self.filter_table,"family":"ip"}}}, {"add":{"table":{"name":self.filter_table,"family":"ip6"}}}, + {"add":{"table":{"name":self.mangle_table,"family":"ip"}}}, + {"add":{"table":{"name":self.mangle_table,"family":"ip6"}}}, + {"add":{"chain":{"family":"ip","table":self.filter_table, "name":"INPUT","type":"filter","hook":"input","prio":0,"policy":policy}}}, {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"INPUT","type":"filter","hook":"input","prio":0,"policy":policy}}}, {"add":{"chain":{"family":"ip","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":policy}}}, @@ -20,12 +24,22 @@ class FiregexTables(NFTableManager): {"add":{"chain":{"family":"ip","table":self.filter_table,"name":"OUTPUT","type":"filter","hook":"output","prio":0,"policy":Action.ACCEPT}}}, {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"OUTPUT","type":"filter","hook":"output","prio":0,"policy":Action.ACCEPT}}}, + {"add":{"chain":{"family":"ip","table":self.mangle_table, "name":"PREROUTING","type":"filter","hook":"prerouting","prio":-150,"policy":Action.ACCEPT}}}, + {"add":{"chain":{"family":"ip6","table":self.mangle_table,"name":"PREROUTING","type":"filter","hook":"prerouting","prio":-150,"policy":Action.ACCEPT}}}, + {"add":{"chain":{"family":"ip","table":self.mangle_table, "name":"POSTROUTING","type":"filter","hook":"postrouting","prio":-150,"policy":Action.ACCEPT}}}, + {"add":{"chain":{"family":"ip6","table":self.mangle_table,"name":"POSTROUTING","type":"filter","hook":"postrouting","prio":-150,"policy":Action.ACCEPT}}}, + {"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_in}}}, {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_in}}}, {"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_out}}}, {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_out}}}, {"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_fwd}}}, {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_fwd}}}, + + {"add":{"chain":{"family":"ip","table":self.mangle_table,"name":self.rules_chain_in}}}, + {"add":{"chain":{"family":"ip6","table":self.mangle_table,"name":self.rules_chain_in}}}, + {"add":{"chain":{"family":"ip","table":self.mangle_table,"name":self.rules_chain_out}}}, + {"add":{"chain":{"family":"ip6","table":self.mangle_table,"name":self.rules_chain_out}}}, ] if opt is None: return rules @@ -157,43 +171,50 @@ class FiregexTables(NFTableManager): return rules def __init__(self): - super().__init__(self.init_comands(),[ - {"add":{"chain":{"family":"ip","table":self.filter_table, "name":"INPUT","type":"filter","hook":"input","prio":0,"policy":Action.ACCEPT}}}, - {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"INPUT","type":"filter","hook":"input","prio":0,"policy":Action.ACCEPT}}}, - {"add":{"chain":{"family":"ip","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":Action.ACCEPT}}}, - {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":Action.ACCEPT}}}, + super().__init__(self.init_comands(),[ {"flush":{"chain":{"table":self.filter_table,"family":"ip", "name":self.rules_chain_in}}}, {"flush":{"chain":{"table":self.filter_table,"family":"ip", "name":self.rules_chain_out}}}, {"flush":{"chain":{"table":self.filter_table,"family":"ip", "name":self.rules_chain_fwd}}}, {"flush":{"chain":{"table":self.filter_table,"family":"ip6", "name":self.rules_chain_in}}}, {"flush":{"chain":{"table":self.filter_table,"family":"ip6", "name":self.rules_chain_out}}}, {"flush":{"chain":{"table":self.filter_table,"family":"ip6", "name":self.rules_chain_fwd}}}, + + {"flush":{"chain":{"table":self.mangle_table,"family":"ip", "name":self.rules_chain_in}}}, + {"flush":{"chain":{"table":self.mangle_table,"family":"ip", "name":self.rules_chain_out}}}, + {"flush":{"chain":{"table":self.mangle_table,"family":"ip6", "name":self.rules_chain_in}}}, + {"flush":{"chain":{"table":self.mangle_table,"family":"ip6", "name":self.rules_chain_out}}} ]) - def chain_to_firegex(self, chain:str): - match chain: - case "INPUT": return self.rules_chain_in - case "OUTPUT": return self.rules_chain_out - case "FORWARD": return self.rules_chain_fwd + def chain_to_firegex(self, chain:str, table:str): + if table == self.filter_table: + match chain: + case "INPUT": return self.rules_chain_in + case "OUTPUT": return self.rules_chain_out + case "FORWARD": return self.rules_chain_fwd + elif table == self.mangle_table: + match chain: + case "PREROUTING": return self.rules_chain_in + case "POSTROUTING": return self.rules_chain_out return None def insert_firegex_chains(self): - rules:list[dict] = list(self.list_rules(tables=[self.filter_table], chains=["INPUT", "OUTPUT", "FORWARD"])) - for family in ["ip", "ip6"]: - for chain in ["INPUT", "OUTPUT", "FORWARD"]: - found = False - rule_to_add = [{ "jump": { "target": self.chain_to_firegex(chain) }}] - for r in rules: - if r.get("family") == family and r.get("table") == "filter" and r.get("chain") == chain and r.get("expr") == rule_to_add: - found = True - break - if found: continue - yield { "add":{ "rule": { - "family": family, - "table": self.filter_table, - "chain": chain, - "expr": rule_to_add - }}} + rules:list[dict] = list(self.list_rules(tables=[self.filter_table, self.mangle_table], chains=["INPUT", "OUTPUT", "FORWARD", "PREROUTING", "POSTROUTING"])) + for table in [self.filter_table, self.mangle_table]: + for family in ["ip", "ip6"]: + for chain in ["INPUT", "OUTPUT", "FORWARD"] if table == self.filter_table else ["PREROUTING", "POSTROUTING"]: + found = False + rule_to_add = [{ "jump": { "target": self.chain_to_firegex(chain, table) }}] + for r in rules: + if r.get("family") == family and r.get("table") == table and r.get("chain") == chain and r.get("expr") == rule_to_add: + found = True + break + if found: continue + yield { "add":{ "rule": { + "family": family, + "table": table, + "chain": chain, + "expr": rule_to_add + }}} def set(self, srvs:list[Rule], policy:str=Action.ACCEPT, opt:FirewallSettings = None): srvs = list(srvs) @@ -209,7 +230,8 @@ class FiregexTables(NFTableManager): port_src_to=65535, port_dst_to=65535, action=Action.REJECT, - mode=Mode.IN + mode=Mode.IN, + table=Table.FILTER )) rules = self.init_comands(policy, opt) + list(self.insert_firegex_chains()) + self.get_rules(*srvs) @@ -261,7 +283,7 @@ class FiregexTables(NFTableManager): for fam in families: rules.append({ "add":{ "rule": { "family": fam, - "table": self.filter_table, + "table": srv.table, "chain": self.rules_chain_out if srv.output_mode else self.rules_chain_in if srv.input_mode else self.rules_chain_fwd, "expr": ip_filters + port_filters + end_rules }}}) diff --git a/backend/routers/firewall.py b/backend/routers/firewall.py index 42f200c..9f3d311 100644 --- a/backend/routers/firewall.py +++ b/backend/routers/firewall.py @@ -11,6 +11,7 @@ db = SQLite('db/firewall-rules.db', { 'rules': { 'rule_id': 'INT PRIMARY KEY CHECK (rule_id >= 0)', 'mode': 'VARCHAR(10) NOT NULL CHECK (mode IN ("in", "out", "forward"))', + '`table`': 'VARCHAR(10) NOT NULL CHECK (`table` IN ("filter", "mangle", "raw"))', 'name': 'VARCHAR(100) NOT NULL', 'active' : 'BOOLEAN NOT NULL CHECK (active IN (0, 1))', 'proto': 'VARCHAR(10) NOT NULL CHECK (proto IN ("tcp", "udp", "both", "any"))', @@ -81,7 +82,7 @@ async def get_rule_list(): """Get the list of existent firegex rules""" return { "policy": firewall.policy, - "rules": db.query("SELECT active, name, proto, src, dst, port_src_from, port_dst_from, port_src_to, port_dst_to, action, mode FROM rules ORDER BY rule_id;"), + "rules": db.query("SELECT active, name, proto, src, dst, port_src_from, port_dst_from, port_src_to, port_dst_to, action, mode, `table` FROM rules ORDER BY rule_id;"), "enabled": firewall.enabled } @@ -99,6 +100,9 @@ async def disable_firewall(): def parse_and_check_rule(rule:RuleModel): + if rule.table == Table.MANGLE and rule.mode == Mode.FORWARD: + raise HTTPException(status_code=400, detail="Mangle table does not support forward mode") + is_src_ip = is_dst_ip = True try: @@ -137,14 +141,14 @@ async def add_new_service(form: RuleFormAdd): src, dst, port_src_from, port_dst_from, port_src_to, port_dst_to, - action, mode - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?)""", + action, mode, `table` + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?, ?)""", rid, ele.active, ele.name, ele.proto, ele.src, ele.dst, ele.port_src_from, ele.port_dst_from, ele.port_src_to, ele.port_dst_to, - ele.action, ele.mode + ele.action, ele.mode, ele.table ) for rid, ele in enumerate(rules)] ) firewall.policy = form.policy.value diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py index de4ef54..fa3fe4f 100644 --- a/backend/utils/__init__.py +++ b/backend/utils/__init__.py @@ -12,8 +12,8 @@ socketio:SocketManager = None ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) ROUTERS_DIR = os.path.join(ROOT_DIR,"routers") -ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER" -DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG" +ON_DOCKER = "DOCKER" in sys.argv +DEBUG = "DEBUG" in sys.argv FIREGEX_PORT = int(os.getenv("PORT","4444")) JWT_ALGORITHM: str = "HS256" API_VERSION = "2.2.0" @@ -44,10 +44,11 @@ class SysctlManager: for name in ctl_table.keys(): self.old_table[name] = read_sysctl(name) - def write_table(self, table): + def write_table(self, table) -> bool: for name, value in table.items(): - write_sysctl(name, value) - + if read_sysctl(name) != value: + write_sysctl(name, value) + def set(self): self.write_table(self.new_table) @@ -124,7 +125,6 @@ class NFTableManager(Singleton): def cmd(self, *cmds): code, out, err = self.raw_cmd(*cmds) - if code == 0: return out else: raise Exception(err) diff --git a/backend/utils/loader.py b/backend/utils/loader.py index 3db07d0..3b72b53 100644 --- a/backend/utils/loader.py +++ b/backend/utils/loader.py @@ -1,13 +1,11 @@ import os, httpx from typing import Callable -from fastapi import APIRouter, WebSocket -import asyncio +from fastapi import APIRouter from starlette.responses import StreamingResponse from fastapi.responses import FileResponse from utils import DEBUG, ON_DOCKER, ROUTERS_DIR, list_files, run_func from utils.models import ResetRequest -from fastapi.middleware.cors import CORSMiddleware REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/" REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html") @@ -26,15 +24,6 @@ async def react_deploy(path): return FileResponse(file_request) def frontend_deploy(app): - if DEBUG: - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - @app.get("/{full_path:path}", include_in_schema=False) async def catch_all(full_path:str): if DEBUG: diff --git a/frontend/src/components/Firewall/ModeSelector.tsx b/frontend/src/components/Firewall/ModeSelector.tsx index eae8132..9824a71 100644 --- a/frontend/src/components/Firewall/ModeSelector.tsx +++ b/frontend/src/components/Firewall/ModeSelector.tsx @@ -1,25 +1,27 @@ import { SegmentedControl, SegmentedControlProps } from "@mantine/core"; -import { RuleMode } from "./utils"; +import { RuleMode, Table } from "./utils"; +import { table } from "console"; -export const ModeSelector = (props:Omit) => ( - & { table: Table }) => { + const isFilterTable = props.table == Table.FILTER + return -) \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/src/components/Firewall/utils.ts b/frontend/src/components/Firewall/utils.ts index 488cbcc..fa2419b 100644 --- a/frontend/src/components/Firewall/utils.ts +++ b/frontend/src/components/Firewall/utils.ts @@ -21,6 +21,11 @@ export enum RuleMode { FORWARD = "forward" } +export enum Table { + MANGLE = "mangle", + FILTER = "filter", +} + export type Rule = { active: boolean name:string, @@ -33,6 +38,7 @@ export type Rule = { port_dst_to: number, action: ActionType, mode: RuleMode, + table: Table } export type RuleInfo = { diff --git a/frontend/src/components/InterfaceInput.tsx b/frontend/src/components/InterfaceInput.tsx index 2e2b1b2..0c621d6 100644 --- a/frontend/src/components/InterfaceInput.tsx +++ b/frontend/src/components/InterfaceInput.tsx @@ -1,5 +1,5 @@ import { Combobox, TextInput, useCombobox } from "@mantine/core"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { ipInterfacesQuery } from "../js/utils"; interface ItemProps{ @@ -38,7 +38,7 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, }, }); - const data = [...customIpInterfaces, ...interfaces] + const data = [...customIpInterfaces.filter(v => !interfaces.map(v => v.value).includes(v.value)), ...interfaces] const [selectedValue, setSelectedValue] = useState(null); const [search, setSearch] = useState(''); @@ -52,12 +52,17 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, return a.value.localeCompare(b.value) }); - const options = filteredOptions.map((item) => ( + const options = filteredOptions.sort( (a, b) => a.value == selectedValue? -1 : b.value == selectedValue? 1: a.value.localeCompare(b.value) ).map((item) => ( - ( {item.netint} ) -{">"} {item.value} + ( {item.value == selectedValue ? "SELECTED" : item.netint} ) -{">"} {item.value} )); + useEffect(() => { + setSelectedValue(data.find((item) => item.value === defaultValue)?.value??null) + setSearch(defaultValue??'') + }, []) + return <> } - value={value??(defaultValue?undefined:search)} + value={value??search} placeholder="10.1.1.1" rightSectionPointerEvents="none" onChange={(event) => { @@ -107,7 +111,7 @@ export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, - + {options} {(exactOptionMatch==null) && search.trim().length > 0 && ( + Use this: {search} diff --git a/frontend/src/js/utils.tsx b/frontend/src/js/utils.tsx index 431502d..60af1ed 100644 --- a/frontend/src/js/utils.tsx +++ b/frontend/src/js/utils.tsx @@ -17,7 +17,7 @@ export const regex_ipv4 = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\. export const regex_ipv4_no_cidr = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" export const regex_port = "^([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?$" export const regex_range_port = "^(([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?)?)?$" -export const DEV_IP_BACKEND = "127.0.0.1:4444" +export const DEV_IP_BACKEND = "198.19.249.69:4444" export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity diff --git a/frontend/src/pages/Firewall/index.tsx b/frontend/src/pages/Firewall/index.tsx index c8ed7fc..07594ca 100644 --- a/frontend/src/pages/Firewall/index.tsx +++ b/frontend/src/pages/Firewall/index.tsx @@ -1,8 +1,8 @@ -import { ActionIcon, Badge, Box, Button, Divider, LoadingOverlay, Space, Switch, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core" +import { ActionIcon, Badge, Box, Divider, FloatingIndicator, LoadingOverlay, Space, Switch, Table, Tabs, TextInput, Title, Tooltip, useMantineTheme } from "@mantine/core" import { useEffect, useState } from "react"; import { BsPlusLg, BsTrashFill } from "react-icons/bs" import { rem } from '@mantine/core'; -import { ActionType, Protocol, Rule, RuleMode, firewall, firewallRulesQuery } from "../../components/Firewall/utils"; +import { ActionType, Protocol, Rule, RuleMode, Table as NFTables, firewall, firewallRulesQuery } from "../../components/Firewall/utils"; import { errorNotify, getErrorMessage, isMediumScreen, makeid, okNotify } from "../../js/utils"; import { useListState, useMediaQuery } from '@mantine/hooks'; import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'; @@ -62,6 +62,8 @@ export const Firewall = () => { const {rule_id, ...rest} = v return rest })) || rules.data?.policy != currentPolicy + const [selectedTab, setSelectedTab] = useState(NFTables.FILTER) + const enableFirewall = () => { if (valuesChanged){ @@ -122,7 +124,8 @@ export const Firewall = () => { port_src_to: 65535, port_dst_to: 8080, action: ActionType.ACCEPT, - mode: RuleMode.IN + mode: RuleMode.IN, + table: selectedTab }) } @@ -145,7 +148,7 @@ export const Firewall = () => { const items = state.map((item, index) => ( - + item.table == selectedTab && {(provided, snapshot) => { const customInt = [ { value: "0.0.0.0/0", netint: "ANY IPv4", label: "0.0.0.0/0" }, @@ -313,6 +316,7 @@ export const Firewall = () => { value={item.mode} onChange={(value)=>{item.mode = value as RuleMode;updateMe()}} style={{width:"100%"}} + table={item.table} />, !isMedium)} {condDiv( { }} - )); + )).filter(v => v); return <> @@ -389,6 +393,18 @@ export const Firewall = () => { + + setSelectedTab(v==NFTables.MANGLE?NFTables.MANGLE:NFTables.FILTER)} style={{ display:"flex", justifyContent:"center", alignItems:"center"}}> + Filtering Table: + + + Filter + + + Mangle + + + {items.length > 0? handlers.reorder({ from: source.index, to: destination?.index || 0 })