refactoring: firewall model changed using ufw model (due to docker iussues)

This commit is contained in:
Domingo Dirutigliano
2023-09-29 16:10:28 +02:00
parent 2657428d6e
commit b11fa66909
8 changed files with 241 additions and 161 deletions

View File

@@ -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")

View File

@@ -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"
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

View File

@@ -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):