diff --git a/backend/modules/firewall/firewall.py b/backend/modules/firewall/firewall.py index 0dd11ae..9bfd1ee 100644 --- a/backend/modules/firewall/firewall.py +++ b/backend/modules/firewall/firewall.py @@ -38,17 +38,19 @@ class FirewallManager: allow_established=self.allow_established, allow_icmp=self.allow_icmp, multicast_dns=self.multicast_dns, - allow_upnp=self.allow_upnp + allow_upnp=self.allow_upnp, + drop_invalid=self.drop_invalid ) @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.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 + self.drop_invalid=value.drop_invalid @property def policy(self): @@ -114,3 +116,11 @@ class FirewallManager: def allow_upnp(self, value): self.db.set("allow_upnp", "1" if value else "0") + @property + def drop_invalid(self): + return self.db.get("drop_invalid", "1") == "1" + + @drop_invalid.setter + def drop_invalid(self, value): + self.db.set("drop_invalid", "1" if value else "0") + diff --git a/backend/modules/firewall/models.py b/backend/modules/firewall/models.py index 14ae181..54dd3b8 100644 --- a/backend/modules/firewall/models.py +++ b/backend/modules/firewall/models.py @@ -68,4 +68,5 @@ class FirewallSettings(BaseModel): allow_established: bool allow_icmp: bool multicast_dns: bool - allow_upnp: bool \ No newline at end of file + allow_upnp: bool + drop_invalid: bool \ No newline at end of file diff --git a/backend/modules/firewall/nftables.py b/backend/modules/firewall/nftables.py index c0bda6c..d61e236 100644 --- a/backend/modules/firewall/nftables.py +++ b/backend/modules/firewall/nftables.py @@ -8,8 +8,8 @@ class FiregexTables(NFTableManager): rules_chain_fwd = "firegex_firewall_rules_fwd" filter_table = "filter" - def init_comands(self, policy:str=Action.ACCEPT, opt: FirewallSettings = None): - return [ + 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"}}}, @@ -26,100 +26,116 @@ class FiregexTables(NFTableManager): {"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": "ip", "table": self.filter_table, "chain": self.rules_chain_out, - "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}] - }}}, - { "add":{ "rule": { - "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 opt.allow_loopback else []) + ([ - { "add":{ "rule": { - "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": "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": "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": "ip", "table": self.filter_table, "chain": self.rules_chain_fwd, - "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }] - }}}, - { "add":{ "rule": { - "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": "ip6", "table": self.filter_table, "chain": self.rules_chain_fwd, - "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} }, { "accept": None }] - }}} - ] 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 [] + ] + if opt is None: return rules + + if opt.allow_loopback: + rules.extend([ + { "add":{ "rule": { + "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": "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 opt.allow_established: + rules.extend([ + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["related", "established"]} },{ "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["related", "established"]} },{ "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["related", "established"]} },{ "accept": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["related", "established"]} },{ "accept": None }] + }}} + ]) + if opt.drop_invalid: + rules.extend([ + { "add":{ "rule": { + "family": "ip", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "==", "left": { "ct": { "key": "state" }},"right": "invalid"} },{ "drop": None }] + }}}, + { "add":{ "rule": { + "family": "ip6", "table": self.filter_table, "chain": self.rules_chain_in, + "expr": [{ "match": {"op": "==", "left": { "ct": { "key": "state" }},"right": "invalid"} },{ "drop": None }] + }}} + ]) + if opt.allow_icmp: + rules.extend([ + { "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": "ip", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} },{ "accept": None }] + }}}, + { "add":{ "rule": { + "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": "ip6", "table": self.filter_table, "chain": self.rules_chain_fwd, + "expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} },{ "accept": None }] + }}} + ]) + if opt.multicast_dns: + rules.extend([ + { "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.allow_upnp: + rules.extend([ + { "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 } + ] + }}}, + ]) + return rules def __init__(self): super().__init__(self.init_comands(),[ diff --git a/frontend/src/components/Firewall/utils.ts b/frontend/src/components/Firewall/utils.ts index fb3837e..5683426 100644 --- a/frontend/src/components/Firewall/utils.ts +++ b/frontend/src/components/Firewall/utils.ts @@ -52,7 +52,8 @@ export type FirewallSettings = { allow_established: boolean, allow_icmp: boolean, multicast_dns: boolean, - allow_upnp: boolean + allow_upnp: boolean, + drop_invalid: boolean } diff --git a/frontend/src/pages/Firewall/SettingsModal.tsx b/frontend/src/pages/Firewall/SettingsModal.tsx index cafd598..7c62c40 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, allow_upnp:true, multicast_dns:true}) + const [settings, setSettings] = useState({} as FirewallSettings) useEffect(()=>{ firewall.settings().then( res => { @@ -44,6 +44,8 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=> setSettings({...settings, multicast_dns:v.target.checked})}/> setSettings({...settings, allow_upnp:v.target.checked})}/> + + setSettings({...settings, drop_invalid:v.target.checked})}/>