refactoring: firewall model changed using ufw model (due to docker iussues)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from modules.firewall.nftables import FiregexTables
|
from modules.firewall.nftables import FiregexTables
|
||||||
from modules.firewall.models import Rule
|
from modules.firewall.models import *
|
||||||
from utils.sqlite import SQLite
|
from utils.sqlite import SQLite
|
||||||
from modules.firewall.models import Action
|
from modules.firewall.models import Action
|
||||||
|
|
||||||
@@ -25,13 +25,31 @@ class FirewallManager:
|
|||||||
nft.set(
|
nft.set(
|
||||||
map(Rule.from_dict, self.db.query('SELECT * FROM rules WHERE active = 1 ORDER BY rule_id;')),
|
map(Rule.from_dict, self.db.query('SELECT * FROM rules WHERE active = 1 ORDER BY rule_id;')),
|
||||||
policy=self.policy,
|
policy=self.policy,
|
||||||
allow_loopback=self.allow_loopback,
|
opt=self.settings
|
||||||
allow_established=self.allow_established,
|
|
||||||
allow_icmp=self.allow_icmp
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
nft.reset()
|
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
|
@property
|
||||||
def policy(self):
|
def policy(self):
|
||||||
return self.db.get("POLICY", Action.ACCEPT)
|
return self.db.get("POLICY", Action.ACCEPT)
|
||||||
@@ -79,5 +97,20 @@ class FirewallManager:
|
|||||||
@allow_established.setter
|
@allow_established.setter
|
||||||
def allow_established(self, value):
|
def allow_established(self, value):
|
||||||
self.db.set("allow_established", "1" if value else "0")
|
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")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from utils import PortType
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
class Rule:
|
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.proto = proto
|
||||||
self.src = src
|
self.src = src
|
||||||
self.dst = dst
|
self.dst = dst
|
||||||
@@ -13,21 +15,10 @@ class Rule:
|
|||||||
self.input_mode = mode == "in"
|
self.input_mode = mode == "in"
|
||||||
self.output_mode = mode == "out"
|
self.output_mode = mode == "out"
|
||||||
self.forward_mode = mode == "forward"
|
self.forward_mode = mode == "forward"
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, var: dict):
|
def from_dict(cls, var: dict):
|
||||||
return cls(
|
return cls(**var)
|
||||||
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"]
|
|
||||||
)
|
|
||||||
|
|
||||||
class Protocol(str, Enum):
|
class Protocol(str, Enum):
|
||||||
TCP = "tcp",
|
TCP = "tcp",
|
||||||
@@ -44,4 +35,37 @@ class Mode(str, Enum):
|
|||||||
class Action(str, Enum):
|
class Action(str, Enum):
|
||||||
ACCEPT = "accept",
|
ACCEPT = "accept",
|
||||||
DROP = "drop",
|
DROP = "drop",
|
||||||
REJECT = "reject"
|
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
|
||||||
@@ -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
|
from utils import nftables_int_to_json, ip_family, NFTableManager, is_ip_parse
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
class FiregexTables(NFTableManager):
|
class FiregexTables(NFTableManager):
|
||||||
rules_chain_in = "firewall_rules_in"
|
rules_chain_in = "firegex_firewall_rules_in"
|
||||||
rules_chain_out = "firewall_rules_out"
|
rules_chain_out = "firegex_firewall_rules_out"
|
||||||
rules_chain_fwd = "firewall_rules_fwd"
|
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 [
|
return [
|
||||||
{"add":{"chain":{
|
{"add":{"table":{"name":self.filter_table,"family":"ip"}}},
|
||||||
"family":"inet",
|
{"add":{"table":{"name":self.filter_table,"family":"ip6"}}},
|
||||||
"table":self.table_name,
|
|
||||||
"name":self.rules_chain_in,
|
{"add":{"chain":{"family":"ip","table":self.filter_table, "name":"INPUT","type":"filter","hook":"input","prio":0,"policy":policy}}},
|
||||||
"type":"filter",
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"INPUT","type":"filter","hook":"input","prio":0,"policy":policy}}},
|
||||||
"hook":"prerouting",
|
{"add":{"chain":{"family":"ip","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":policy}}},
|
||||||
"prio":0,
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":policy}}},
|
||||||
"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":"inet",
|
{"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_in}}},
|
||||||
"table":self.table_name,
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_in}}},
|
||||||
"name":self.rules_chain_fwd,
|
{"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_out}}},
|
||||||
"type":"filter",
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_out}}},
|
||||||
"hook":"forward",
|
{"add":{"chain":{"family":"ip","table":self.filter_table,"name":self.rules_chain_fwd}}},
|
||||||
"prio":0,
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":self.rules_chain_fwd}}},
|
||||||
"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":{ "rule": {
|
{ "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}]
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}]
|
||||||
}}},
|
}}},
|
||||||
{ "add":{ "rule": {
|
{ "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}]
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "iif" }}, "right": "lo"}},{"accept": None}]
|
||||||
}}}
|
}}}
|
||||||
] if allow_loopback else []) + ([
|
] if opt.allow_loopback else []) + ([
|
||||||
{ "add":{ "rule": {
|
{ "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": "in", "left": { "ct": { "key": "state" }},"right": ["established"]} }, { "accept": None }]
|
"expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }]
|
||||||
}}},
|
}}},
|
||||||
{ "add":{ "rule": {
|
{ "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": "in", "left": { "ct": { "key": "state" }},"right": ["established"]} }, { "accept": None }]
|
"expr": [{ "match": {"op": "in", "left": { "ct": { "key": "state" }},"right": ["established", "related"]} }, { "accept": None }]
|
||||||
}}}
|
}}},
|
||||||
] if allow_established else []) + ([
|
|
||||||
{ "add":{ "rule": {
|
{ "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 }]
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }]
|
||||||
}}},
|
}}},
|
||||||
{ "add":{ "rule": {
|
{ "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 }]
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }]
|
||||||
}}},
|
}}},
|
||||||
{ "add":{ "rule": {
|
{ "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 }]
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} }, { "accept": None }]
|
||||||
}}},
|
}}},
|
||||||
{ "add":{ "rule": {
|
{ "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 }]
|
"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):
|
def __init__(self):
|
||||||
super().__init__(self.init_comands(),[
|
super().__init__(self.init_comands(),[
|
||||||
{"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_in}}},
|
{"add":{"chain":{"family":"ip","table":self.filter_table, "name":"INPUT","type":"filter","hook":"input","prio":0,"policy":Action.ACCEPT}}},
|
||||||
{"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_in}}},
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"INPUT","type":"filter","hook":"input","prio":0,"policy":Action.ACCEPT}}},
|
||||||
{"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_out}}},
|
{"add":{"chain":{"family":"ip","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":Action.ACCEPT}}},
|
||||||
{"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_out}}},
|
{"add":{"chain":{"family":"ip6","table":self.filter_table,"name":"FORWARD","type":"filter","hook":"forward","prio":0,"policy":Action.ACCEPT}}},
|
||||||
{"flush":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_fwd}}},
|
{"flush":{"chain":{"table":self.filter_table,"family":"ip", "name":self.rules_chain_in}}},
|
||||||
{"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.rules_chain_fwd}}},
|
{"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)
|
srvs = list(srvs)
|
||||||
self.reset()
|
self.reset()
|
||||||
if policy == Action.REJECT:
|
if policy == Action.REJECT:
|
||||||
@@ -99,7 +176,8 @@ class FiregexTables(NFTableManager):
|
|||||||
action=Action.REJECT,
|
action=Action.REJECT,
|
||||||
mode=Mode.IN
|
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)
|
self.cmd(*rules)
|
||||||
|
|
||||||
def get_rules(self,*srvs:Rule):
|
def get_rules(self,*srvs:Rule):
|
||||||
@@ -112,6 +190,8 @@ class FiregexTables(NFTableManager):
|
|||||||
ele.proto = Protocol.TCP.value
|
ele.proto = Protocol.TCP.value
|
||||||
final_srvs.append(udp_rule)
|
final_srvs.append(udp_rule)
|
||||||
final_srvs.append(ele)
|
final_srvs.append(ele)
|
||||||
|
|
||||||
|
families = ["ip", "ip6"]
|
||||||
|
|
||||||
for srv in final_srvs:
|
for srv in final_srvs:
|
||||||
ip_filters = []
|
ip_filters = []
|
||||||
@@ -119,12 +199,14 @@ class FiregexTables(NFTableManager):
|
|||||||
if srv.src != "":
|
if srv.src != "":
|
||||||
if is_ip_parse(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)}})
|
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:
|
else:
|
||||||
ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "iifname" } }, "right": srv.src} })
|
ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "iifname" } }, "right": srv.src} })
|
||||||
|
|
||||||
if srv.dst != "":
|
if srv.dst != "":
|
||||||
if is_ip_parse(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)}})
|
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:
|
else:
|
||||||
ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "oifname" } }, "right": srv.dst} })
|
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
|
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}]
|
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": {
|
#If srv.output_mode is True, then the rule is in the output chain, so the reject action is not allowed
|
||||||
"family": "inet",
|
for fam in families:
|
||||||
"table": self.table_name,
|
rules.append({ "add":{ "rule": {
|
||||||
"chain": self.rules_chain_out if srv.output_mode else self.rules_chain_in if srv.input_mode else self.rules_chain_fwd,
|
"family": fam,
|
||||||
"expr": ip_filters + port_filters + end_rules
|
"table": self.filter_table,
|
||||||
#If srv.output_mode is True, then the rule is in the output chain, so the reject action is not allowed
|
"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
|
return rules
|
||||||
|
|
||||||
def add(self, *srvs:Rule):
|
def add(self, *srvs:Rule):
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
class Service:
|
class Service:
|
||||||
def __init__(self, id: str, status: str, port: int, name: str, proto: str, ip_int: str):
|
def __init__(self, service_id: str, status: str, port: int, name: str, proto: str, ip_int: str, **other):
|
||||||
self.id = id
|
self.id = service_id
|
||||||
self.status = status
|
self.status = status
|
||||||
self.port = port
|
self.port = port
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -11,36 +11,20 @@ class Service:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, var: dict):
|
def from_dict(cls, var: dict):
|
||||||
return cls(
|
return cls(**var)
|
||||||
id=var["service_id"],
|
|
||||||
status=var["status"],
|
|
||||||
port=var["port"],
|
|
||||||
name=var["name"],
|
|
||||||
proto=var["proto"],
|
|
||||||
ip_int=var["ip_int"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Regex:
|
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.regex = regex
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.service_id = service_id
|
self.service_id = service_id
|
||||||
self.is_blacklist = is_blacklist
|
self.is_blacklist = is_blacklist
|
||||||
self.blocked_packets = blocked_packets
|
self.blocked_packets = blocked_packets
|
||||||
self.id = id
|
self.id = regex_id
|
||||||
self.is_case_sensitive = is_case_sensitive
|
self.is_case_sensitive = is_case_sensitive
|
||||||
self.active = active
|
self.active = active
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, var: dict):
|
def from_dict(cls, var: dict):
|
||||||
return cls(
|
return cls(**var)
|
||||||
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"]
|
|
||||||
)
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
class Service:
|
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.service_id = service_id
|
||||||
self.active = active
|
self.active = active
|
||||||
self.public_port = public_port
|
self.public_port = public_port
|
||||||
@@ -11,13 +11,4 @@ class Service:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, var: dict):
|
def from_dict(cls, var: dict):
|
||||||
return cls(
|
return cls(**var)
|
||||||
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"]
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,44 +1,11 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel
|
|
||||||
from utils.sqlite import SQLite
|
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 utils.models import ResetRequest, StatusMessageModel
|
||||||
from modules.firewall.nftables import FiregexTables
|
from modules.firewall.nftables import FiregexTables
|
||||||
from modules.firewall.firewall import FirewallManager
|
from modules.firewall.firewall import FirewallManager
|
||||||
from modules.firewall.models import Protocol, Mode, Action
|
from modules.firewall.models import *
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
db = SQLite('db/firewall-rules.db', {
|
db = SQLite('db/firewall-rules.db', {
|
||||||
'rules': {
|
'rules': {
|
||||||
@@ -101,20 +68,12 @@ async def apply_changes():
|
|||||||
@app.get("/settings", response_model=FirewallSettings)
|
@app.get("/settings", response_model=FirewallSettings)
|
||||||
async def get_settings():
|
async def get_settings():
|
||||||
"""Get the firewall settings"""
|
"""Get the firewall settings"""
|
||||||
return {
|
return firewall.settings
|
||||||
"keep_rules": firewall.keep_rules,
|
|
||||||
"allow_loopback": firewall.allow_loopback,
|
|
||||||
"allow_established": firewall.allow_established,
|
|
||||||
"allow_icmp": firewall.allow_icmp
|
|
||||||
}
|
|
||||||
|
|
||||||
@app.post("/settings/set", response_model=StatusMessageModel)
|
@app.post("/settings/set", response_model=StatusMessageModel)
|
||||||
async def set_settings(form: FirewallSettings):
|
async def set_settings(form: FirewallSettings):
|
||||||
"""Set the firewall settings"""
|
"""Set the firewall settings"""
|
||||||
firewall.keep_rules = form.keep_rules
|
firewall.settings = form
|
||||||
firewall.allow_loopback = form.allow_loopback
|
|
||||||
firewall.allow_established = form.allow_established
|
|
||||||
firewall.allow_icmp = form.allow_icmp
|
|
||||||
return await apply_changes()
|
return await apply_changes()
|
||||||
|
|
||||||
@app.get('/rules', response_model=RuleInfo)
|
@app.get('/rules', response_model=RuleInfo)
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ export type FirewallSettings = {
|
|||||||
keep_rules: boolean,
|
keep_rules: boolean,
|
||||||
allow_loopback: boolean,
|
allow_loopback: boolean,
|
||||||
allow_established: boolean,
|
allow_established: boolean,
|
||||||
allow_icmp: boolean
|
allow_icmp: boolean,
|
||||||
|
multicast_dns: boolean,
|
||||||
|
allow_upnp: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FirewallSettings, firewall } from '../../components/Firewall/utils';
|
|||||||
|
|
||||||
export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>void }) {
|
export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>void }) {
|
||||||
|
|
||||||
const [settings, setSettings] = useState<FirewallSettings>({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true})
|
const [settings, setSettings] = useState<FirewallSettings>({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true, allow_upnp:true, multicast_dns:true})
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
firewall.settings().then( res => {
|
firewall.settings().then( res => {
|
||||||
@@ -40,6 +40,10 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>
|
|||||||
<Switch label="Allow established connection (essential to allow opening connection) (Dangerous to disable)" checked={settings.allow_established} onChange={v => setSettings({...settings, allow_established:v.target.checked})}/>
|
<Switch label="Allow established connection (essential to allow opening connection) (Dangerous to disable)" checked={settings.allow_established} onChange={v => setSettings({...settings, allow_established:v.target.checked})}/>
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
<Switch label="Allow icmp packets" checked={settings.allow_icmp} onChange={v => setSettings({...settings, allow_icmp:v.target.checked})}/>
|
<Switch label="Allow icmp packets" checked={settings.allow_icmp} onChange={v => setSettings({...settings, allow_icmp:v.target.checked})}/>
|
||||||
|
<Space h="md" />
|
||||||
|
<Switch label="Allow multicast DNS" checked={settings.multicast_dns} onChange={v => setSettings({...settings, multicast_dns:v.target.checked})}/>
|
||||||
|
<Space h="md" />
|
||||||
|
<Switch label="Allow UPnP protocol" checked={settings.allow_upnp} onChange={v => setSettings({...settings, allow_upnp:v.target.checked})}/>
|
||||||
|
|
||||||
<Group position="right" mt="md">
|
<Group position="right" mt="md">
|
||||||
<Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>
|
<Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user