From b11fa66909db99bdda5413a74c25221432c6f3b8 Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Fri, 29 Sep 2023 16:10:28 +0200 Subject: [PATCH] refactoring: firewall model changed using ufw model (due to docker iussues) --- backend/modules/firewall/firewall.py | 43 +++- backend/modules/firewall/models.py | 52 +++-- backend/modules/firewall/nftables.py | 207 ++++++++++++------ backend/modules/nfregex/models.py | 28 +-- backend/modules/porthijack/models.py | 13 +- backend/routers/firewall.py | 49 +---- frontend/src/components/Firewall/utils.ts | 4 +- frontend/src/pages/Firewall/SettingsModal.tsx | 6 +- 8 files changed, 241 insertions(+), 161 deletions(-) diff --git a/backend/modules/firewall/firewall.py b/backend/modules/firewall/firewall.py index 915afe6..0dd11ae 100644 --- a/backend/modules/firewall/firewall.py +++ b/backend/modules/firewall/firewall.py @@ -1,6 +1,6 @@ import asyncio from modules.firewall.nftables import FiregexTables -from modules.firewall.models import Rule +from modules.firewall.models import * from utils.sqlite import SQLite from modules.firewall.models import Action @@ -25,13 +25,31 @@ class FirewallManager: nft.set( map(Rule.from_dict, self.db.query('SELECT * FROM rules WHERE active = 1 ORDER BY rule_id;')), policy=self.policy, - allow_loopback=self.allow_loopback, - allow_established=self.allow_established, - allow_icmp=self.allow_icmp + opt=self.settings ) else: nft.reset() + @property + def settings(self): + return FirewallSettings( + keep_rules=self.keep_rules, + allow_loopback=self.allow_loopback, + allow_established=self.allow_established, + allow_icmp=self.allow_icmp, + multicast_dns=self.multicast_dns, + allow_upnp=self.allow_upnp + ) + + @settings.setter + def settings(self, value:FirewallSettings): + self.keep_rules=value.keep_rules, + self.allow_loopback=value.allow_loopback, + self.allow_established=value.allow_established, + self.allow_icmp=value.allow_icmp, + self.multicast_dns=value.multicast_dns, + self.allow_upnp=value.allow_upnp + @property def policy(self): return self.db.get("POLICY", Action.ACCEPT) @@ -79,5 +97,20 @@ class FirewallManager: @allow_established.setter def allow_established(self, value): self.db.set("allow_established", "1" if value else "0") + + @property + def multicast_dns(self): + return self.db.get("multicast_dns", "1") == "1" + + @multicast_dns.setter + def multicast_dns(self, value): + self.db.set("multicast_dns", "1" if value else "0") + + @property + def allow_upnp(self): + return self.db.get("allow_upnp", "1") == "1" + + @allow_upnp.setter + def allow_upnp(self, value): + self.db.set("allow_upnp", "1" if value else "0") - diff --git a/backend/modules/firewall/models.py b/backend/modules/firewall/models.py index b20a47c..14ae181 100644 --- a/backend/modules/firewall/models.py +++ b/backend/modules/firewall/models.py @@ -1,7 +1,9 @@ from enum import Enum +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): + 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): self.proto = proto self.src = src self.dst = dst @@ -13,21 +15,10 @@ class Rule: self.input_mode = mode == "in" self.output_mode = mode == "out" self.forward_mode = mode == "forward" - @classmethod def from_dict(cls, var: dict): - return cls( - proto=var["proto"], - src=var["src"], - dst=var["dst"], - port_dst_from=var["port_dst_from"], - port_dst_to=var["port_dst_to"], - port_src_from=var["port_src_from"], - port_src_to=var["port_src_to"], - action=var["action"], - mode=var["mode"] - ) + return cls(**var) class Protocol(str, Enum): TCP = "tcp", @@ -44,4 +35,37 @@ class Mode(str, Enum): class Action(str, Enum): ACCEPT = "accept", DROP = "drop", - REJECT = "reject" \ No newline at end of file + REJECT = "reject" + +class RuleModel(BaseModel): + active: bool + name: str + proto: Protocol + src: str + dst: str + port_src_from: PortType + port_dst_from: PortType + port_src_to: PortType + port_dst_to: PortType + action: Action + mode:Mode + +class RuleFormAdd(BaseModel): + rules: list[RuleModel] + policy: Action + +class RuleInfo(BaseModel): + rules: list[RuleModel] + policy: Action + enabled: bool + +class RenameForm(BaseModel): + name:str + +class FirewallSettings(BaseModel): + keep_rules: bool + allow_loopback: bool + allow_established: bool + allow_icmp: bool + multicast_dns: bool + allow_upnp: bool \ No newline at end of file diff --git a/backend/modules/firewall/nftables.py b/backend/modules/firewall/nftables.py index 32d45f6..c0bda6c 100644 --- a/backend/modules/firewall/nftables.py +++ b/backend/modules/firewall/nftables.py @@ -1,89 +1,166 @@ -from modules.firewall.models import Rule, Protocol, Mode, Action +from modules.firewall.models import * from utils import nftables_int_to_json, ip_family, NFTableManager, is_ip_parse import copy class FiregexTables(NFTableManager): - rules_chain_in = "firewall_rules_in" - rules_chain_out = "firewall_rules_out" - rules_chain_fwd = "firewall_rules_fwd" + rules_chain_in = "firegex_firewall_rules_in" + rules_chain_out = "firegex_firewall_rules_out" + rules_chain_fwd = "firegex_firewall_rules_fwd" + filter_table = "filter" - def init_comands(self, policy:str=Action.ACCEPT, allow_loopback=False, allow_established=False, allow_icmp=False): + def init_comands(self, policy:str=Action.ACCEPT, opt: FirewallSettings = None): return [ - {"add":{"chain":{ - "family":"inet", - "table":self.table_name, - "name":self.rules_chain_in, - "type":"filter", - "hook":"prerouting", - "prio":0, - "policy":policy - }}}, - {"add":{"chain":{ - "family":"inet", - "table":self.table_name, - "name":self.rules_chain_fwd, - "type":"filter", - "hook":"forward", - "prio":0, - "policy":policy - }}}, - {"add":{"chain":{ - "family":"inet", - "table":self.table_name, - "name":self.rules_chain_out, - "type":"filter", - "hook":"postrouting", - "prio":0, - "policy":Action.ACCEPT - }}}, - ] + ([ + {"add":{"table":{"name":self.filter_table,"family":"ip"}}}, + {"add":{"table":{"name":self.filter_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}}}, + {"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":policy}}}, + {"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.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":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_out, + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_out, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}] }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_in, + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_out, + "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}] }}} - ] if allow_loopback else []) + ([ + ] if opt.allow_loopback else []) + ([ { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_in, - "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established"]} }, { "accept": None }] + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_fwd, - "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established"]} }, { "accept": None }] - }}} - ] if allow_established else []) + ([ + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] + }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_in, + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_out, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_out, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }] + }}} + ] if opt.allow_established else []) + ([ + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }] }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_fwd, + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_fwd, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }] }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_in, + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} }, { "accept": None }] }}}, { "add":{ "rule": { - "family": "inet", "table": self.table_name, "chain": self.rules_chain_fwd, + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_fwd, "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} }, { "accept": None }] }}} - ] if allow_icmp else []) + ] if opt.allow_icmp else []) + ([ + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [ + { 'match': {'left': {'payload': {'protocol': "ip", 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json("224.0.0.251/32")} }, + { 'match': {'left': {'payload': {'protocol': "udp", 'field': 'dport'}}, 'op': '==', 'right': 5353} }, + { "accept": None } + ] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [ + { 'match': {'left': {'payload': {'protocol': "ip6", 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json("ff02::fb/128")} }, + { 'match': {'left': {'payload': {'protocol': "udp", 'field': 'dport'}}, 'op': '==', 'right': 5353} }, + { "accept": None } + ] + }}}, + ] if opt.multicast_dns else []) + ([ + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [ + { 'match': {'left': {'payload': {'protocol': "ip", 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json("239.255.255.250/32")} }, + { 'match': {'left': {'payload': {'protocol': "udp", 'field': 'dport'}}, 'op': '==', 'right': 1900} }, + { "accept": None } + ] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [ + { 'match': {'left': {'payload': {'protocol': "ip6", 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json("ff02::f/128")} }, + { 'match': {'left': {'payload': {'protocol': "udp", 'field': 'dport'}}, 'op': '==', 'right': 1900} }, + { "accept": None } + ] + }}}, + ] if opt.allow_upnp else [])) if opt else [] def __init__(self): super().__init__(self.init_comands(),[ - {"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_in}}}, - {"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_in}}}, - {"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_out}}}, - {"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_out}}}, - {"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_fwd}}}, - {"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_fwd}}}, + {"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}}}, + {"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}}}, ]) + + 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 + 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 + }}} - def set(self, srvs:list[Rule], policy:str="accept", allow_loopback=False, allow_established=False, allow_icmp=False): + def set(self, srvs:list[Rule], policy:str=Action.ACCEPT, opt:FirewallSettings = None): srvs = list(srvs) self.reset() if policy == Action.REJECT: @@ -99,7 +176,8 @@ class FiregexTables(NFTableManager): action=Action.REJECT, mode=Mode.IN )) - rules = self.init_comands(policy, allow_loopback=allow_loopback, allow_established=allow_established, allow_icmp=allow_icmp) + self.get_rules(*srvs) + + rules = self.init_comands(policy, opt) + list(self.insert_firegex_chains()) + self.get_rules(*srvs) self.cmd(*rules) def get_rules(self,*srvs:Rule): @@ -112,6 +190,8 @@ class FiregexTables(NFTableManager): ele.proto = Protocol.TCP.value final_srvs.append(udp_rule) final_srvs.append(ele) + + families = ["ip", "ip6"] for srv in final_srvs: ip_filters = [] @@ -119,12 +199,14 @@ class FiregexTables(NFTableManager): if srv.src != "": if is_ip_parse(srv.src): ip_filters.append({'match': {'left': {'payload': {'protocol': ip_family(srv.src), 'field': 'saddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.src)}}) + families = [ip_family(srv.src)] else: ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "iifname" } }, "right": srv.src} }) if srv.dst != "": if is_ip_parse(srv.dst): ip_filters.append({'match': {'left': {'payload': {'protocol': ip_family(srv.dst), 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.dst)}}) + families = [ip_family(srv.dst)] else: ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "oifname" } }, "right": srv.dst} }) @@ -140,13 +222,14 @@ class FiregexTables(NFTableManager): port_filters.append({'match': {'left': {'meta': {'key': 'l4proto'}}, 'op': '==', 'right': srv.proto}}) #filter the protocol if no port is specified end_rules = [{'accept': None} if srv.action == "accept" else {'reject': {}} if (srv.action == "reject" and not srv.output_mode) else {'drop': None}] - rules.append({ "add":{ "rule": { - "family": "inet", - "table": self.table_name, - "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 - #If srv.output_mode is True, then the rule is in the output chain, so the reject action is not allowed - }}}) + #If srv.output_mode is True, then the rule is in the output chain, so the reject action is not allowed + for fam in families: + rules.append({ "add":{ "rule": { + "family": fam, + "table": self.filter_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 + }}}) return rules def add(self, *srvs:Rule): diff --git a/backend/modules/nfregex/models.py b/backend/modules/nfregex/models.py index e020d87..df39f37 100644 --- a/backend/modules/nfregex/models.py +++ b/backend/modules/nfregex/models.py @@ -1,8 +1,8 @@ import base64 class Service: - def __init__(self, id: str, status: str, port: int, name: str, proto: str, ip_int: str): - self.id = id + def __init__(self, service_id: str, status: str, port: int, name: str, proto: str, ip_int: str, **other): + self.id = service_id self.status = status self.port = port self.name = name @@ -11,36 +11,20 @@ class Service: @classmethod def from_dict(cls, var: dict): - return cls( - id=var["service_id"], - status=var["status"], - port=var["port"], - name=var["name"], - proto=var["proto"], - ip_int=var["ip_int"] - ) + return cls(**var) class Regex: - def __init__(self, id: int, regex: bytes, mode: str, service_id: str, is_blacklist: bool, blocked_packets: int, is_case_sensitive: bool, active: bool): + def __init__(self, regex_id: int, regex: bytes, mode: str, service_id: str, is_blacklist: bool, blocked_packets: int, is_case_sensitive: bool, active: bool, **other): self.regex = regex self.mode = mode self.service_id = service_id self.is_blacklist = is_blacklist self.blocked_packets = blocked_packets - self.id = id + self.id = regex_id self.is_case_sensitive = is_case_sensitive self.active = active @classmethod def from_dict(cls, var: dict): - return cls( - id=var["regex_id"], - regex=base64.b64decode(var["regex"]), - mode=var["mode"], - service_id=var["service_id"], - is_blacklist=var["is_blacklist"], - blocked_packets=var["blocked_packets"], - is_case_sensitive=var["is_case_sensitive"], - active=var["active"] - ) \ No newline at end of file + return cls(**var) \ No newline at end of file diff --git a/backend/modules/porthijack/models.py b/backend/modules/porthijack/models.py index a89e6d6..7b1e352 100644 --- a/backend/modules/porthijack/models.py +++ b/backend/modules/porthijack/models.py @@ -1,5 +1,5 @@ class Service: - def __init__(self, service_id: str, active: bool, public_port: int, proxy_port: int, name: str, proto: str, ip_src: str, ip_dst:str): + def __init__(self, service_id: str, active: bool, public_port: int, proxy_port: int, name: str, proto: str, ip_src: str, ip_dst:str, **other): self.service_id = service_id self.active = active self.public_port = public_port @@ -11,13 +11,4 @@ class Service: @classmethod def from_dict(cls, var: dict): - return cls( - service_id=var["service_id"], - active=var["active"], - public_port=var["public_port"], - proxy_port=var["proxy_port"], - name=var["name"], - proto=var["proto"], - ip_src=var["ip_src"], - ip_dst=var["ip_dst"] - ) + return cls(**var) diff --git a/backend/routers/firewall.py b/backend/routers/firewall.py index 7fb63cb..42f200c 100644 --- a/backend/routers/firewall.py +++ b/backend/routers/firewall.py @@ -1,44 +1,11 @@ import sqlite3 from fastapi import APIRouter, HTTPException -from pydantic import BaseModel from utils.sqlite import SQLite -from utils import ip_parse, ip_family, socketio_emit, PortType +from utils import ip_parse, ip_family, socketio_emit from utils.models import ResetRequest, StatusMessageModel from modules.firewall.nftables import FiregexTables from modules.firewall.firewall import FirewallManager -from modules.firewall.models import Protocol, Mode, Action - - -class RuleModel(BaseModel): - active: bool - name: str - proto: Protocol - src: str - dst: str - port_src_from: PortType - port_dst_from: PortType - port_src_to: PortType - port_dst_to: PortType - action: Action - mode:Mode - -class RuleFormAdd(BaseModel): - rules: list[RuleModel] - policy: Action - -class RuleInfo(BaseModel): - rules: list[RuleModel] - policy: Action - enabled: bool - -class RenameForm(BaseModel): - name:str - -class FirewallSettings(BaseModel): - keep_rules: bool - allow_loopback: bool - allow_established: bool - allow_icmp: bool +from modules.firewall.models import * db = SQLite('db/firewall-rules.db', { 'rules': { @@ -101,20 +68,12 @@ async def apply_changes(): @app.get("/settings", response_model=FirewallSettings) async def get_settings(): """Get the firewall settings""" - return { - "keep_rules": firewall.keep_rules, - "allow_loopback": firewall.allow_loopback, - "allow_established": firewall.allow_established, - "allow_icmp": firewall.allow_icmp - } + return firewall.settings @app.post("/settings/set", response_model=StatusMessageModel) async def set_settings(form: FirewallSettings): """Set the firewall settings""" - firewall.keep_rules = form.keep_rules - firewall.allow_loopback = form.allow_loopback - firewall.allow_established = form.allow_established - firewall.allow_icmp = form.allow_icmp + firewall.settings = form return await apply_changes() @app.get('/rules', response_model=RuleInfo) diff --git a/frontend/src/components/Firewall/utils.ts b/frontend/src/components/Firewall/utils.ts index 0fdaf6a..fb3837e 100644 --- a/frontend/src/components/Firewall/utils.ts +++ b/frontend/src/components/Firewall/utils.ts @@ -50,7 +50,9 @@ export type FirewallSettings = { keep_rules: boolean, allow_loopback: boolean, allow_established: boolean, - allow_icmp: boolean + allow_icmp: boolean, + multicast_dns: boolean, + allow_upnp: boolean } diff --git a/frontend/src/pages/Firewall/SettingsModal.tsx b/frontend/src/pages/Firewall/SettingsModal.tsx index ff56d9b..cafd598 100644 --- a/frontend/src/pages/Firewall/SettingsModal.tsx +++ b/frontend/src/pages/Firewall/SettingsModal.tsx @@ -5,7 +5,7 @@ import { FirewallSettings, firewall } from '../../components/Firewall/utils'; export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>void }) { - const [settings, setSettings] = useState({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true}) + const [settings, setSettings] = useState({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true, allow_upnp:true, multicast_dns:true}) useEffect(()=>{ firewall.settings().then( res => { @@ -40,6 +40,10 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=> setSettings({...settings, allow_established:v.target.checked})}/> setSettings({...settings, allow_icmp:v.target.checked})}/> + + setSettings({...settings, multicast_dns:v.target.checked})}/> + + setSettings({...settings, allow_upnp:v.target.checked})}/>