Firewall refactor
This commit is contained in:
@@ -154,7 +154,7 @@ if __name__ == '__main__':
|
|||||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"app:app",
|
"app:app",
|
||||||
host=None,
|
host="0.0.0.0",
|
||||||
port=FIREGEX_PORT,
|
port=FIREGEX_PORT,
|
||||||
reload=DEBUG,
|
reload=DEBUG,
|
||||||
access_log=True,
|
access_log=True,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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 Rule
|
||||||
from utils.sqlite import SQLite
|
from utils.sqlite import SQLite
|
||||||
|
from modules.firewall.models import Action
|
||||||
|
|
||||||
nft = FiregexTables()
|
nft = FiregexTables()
|
||||||
|
|
||||||
@@ -25,14 +26,15 @@ class FirewallManager:
|
|||||||
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,
|
allow_loopback=self.allow_loopback,
|
||||||
allow_established=self.allow_established
|
allow_established=self.allow_established,
|
||||||
|
allow_icmp=self.allow_icmp
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
nft.reset()
|
nft.reset()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def policy(self):
|
def policy(self):
|
||||||
return self.db.get("POLICY", "accept")
|
return self.db.get("POLICY", Action.ACCEPT)
|
||||||
|
|
||||||
@policy.setter
|
@policy.setter
|
||||||
def policy(self, value):
|
def policy(self, value):
|
||||||
@@ -62,6 +64,14 @@ class FirewallManager:
|
|||||||
def allow_loopback(self, value):
|
def allow_loopback(self, value):
|
||||||
self.db.set("allow_loopback", "1" if value else "0")
|
self.db.set("allow_loopback", "1" if value else "0")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allow_icmp(self):
|
||||||
|
return self.db.get("allow_icmp", "1") == "1"
|
||||||
|
|
||||||
|
@allow_icmp.setter
|
||||||
|
def allow_icmp(self, value):
|
||||||
|
self.db.set("allow_icmp", "1" if value else "0")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_established(self):
|
def allow_established(self):
|
||||||
return self.db.get("allow_established", "1") == "1"
|
return self.db.get("allow_established", "1") == "1"
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
class Rule:
|
class Rule:
|
||||||
def __init__(self, proto: str, ip_src:str, ip_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):
|
||||||
self.proto = proto
|
self.proto = proto
|
||||||
self.ip_src = ip_src
|
self.src = src
|
||||||
self.ip_dst = ip_dst
|
self.dst = dst
|
||||||
self.port_src_from = port_src_from
|
self.port_src_from = port_src_from
|
||||||
self.port_dst_from = port_dst_from
|
self.port_dst_from = port_dst_from
|
||||||
self.port_src_to = port_src_to
|
self.port_src_to = port_src_to
|
||||||
self.port_dst_to = port_dst_to
|
self.port_dst_to = port_dst_to
|
||||||
self.action = action
|
self.action = action
|
||||||
self.input_mode = mode in ["I"]
|
self.input_mode = mode == "in"
|
||||||
self.output_mode = mode in ["O"]
|
self.output_mode = mode == "out"
|
||||||
|
self.forward_mode = mode == "forward"
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, var: dict):
|
def from_dict(cls, var: dict):
|
||||||
return cls(
|
return cls(
|
||||||
proto=var["proto"],
|
proto=var["proto"],
|
||||||
ip_src=var["ip_src"],
|
src=var["src"],
|
||||||
ip_dst=var["ip_dst"],
|
dst=var["dst"],
|
||||||
port_dst_from=var["port_dst_from"],
|
port_dst_from=var["port_dst_from"],
|
||||||
port_dst_to=var["port_dst_to"],
|
port_dst_to=var["port_dst_to"],
|
||||||
port_src_from=var["port_src_from"],
|
port_src_from=var["port_src_from"],
|
||||||
@@ -25,3 +28,20 @@ class Rule:
|
|||||||
action=var["action"],
|
action=var["action"],
|
||||||
mode=var["mode"]
|
mode=var["mode"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Protocol(str, Enum):
|
||||||
|
TCP = "tcp",
|
||||||
|
UDP = "udp",
|
||||||
|
BOTH = "both",
|
||||||
|
ANY = "any"
|
||||||
|
|
||||||
|
|
||||||
|
class Mode(str, Enum):
|
||||||
|
IN = "in",
|
||||||
|
OUT = "out",
|
||||||
|
FORWARD = "forward"
|
||||||
|
|
||||||
|
class Action(str, Enum):
|
||||||
|
ACCEPT = "accept",
|
||||||
|
DROP = "drop",
|
||||||
|
REJECT = "reject"
|
||||||
@@ -1,34 +1,13 @@
|
|||||||
from modules.firewall.models import Rule
|
from modules.firewall.models import Rule, Protocol, Mode, Action
|
||||||
from utils import nftables_int_to_json, ip_parse, ip_family, NFTableManager
|
from utils import nftables_int_to_json, ip_family, NFTableManager, is_ip_parse
|
||||||
|
import copy
|
||||||
|
|
||||||
class FiregexHijackRule():
|
|
||||||
def __init__(self, proto:str, ip_src:str, ip_dst:str, port_src_from:int, port_dst_from:int, port_src_to:int, port_dst_to:int, action:str, target:str, id:int):
|
|
||||||
self.id = id
|
|
||||||
self.target = target
|
|
||||||
self.proto = proto
|
|
||||||
self.ip_src = ip_src
|
|
||||||
self.ip_dst = ip_dst
|
|
||||||
self.port_src_from = min(port_src_from, port_src_to)
|
|
||||||
self.port_dst_from = min(port_dst_from, port_dst_to)
|
|
||||||
self.port_src_to = max(port_src_from, port_src_to)
|
|
||||||
self.port_dst_to = max(port_dst_from, port_dst_to)
|
|
||||||
self.action = action
|
|
||||||
|
|
||||||
def __eq__(self, o: object) -> bool:
|
|
||||||
if isinstance(o, FiregexHijackRule) or isinstance(o, Rule):
|
|
||||||
return self.action == o.action and self.proto == o.proto and\
|
|
||||||
ip_parse(self.ip_src) == ip_parse(o.ip_src) and ip_parse(self.ip_dst) == ip_parse(o.ip_dst) and\
|
|
||||||
int(self.port_src_from) == int(o.port_src_from) and int(self.port_dst_from) == int(o.port_dst_from) and\
|
|
||||||
int(self.port_src_to) == int(o.port_src_to) and int(self.port_dst_to) == int(o.port_dst_to)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class FiregexTables(NFTableManager):
|
class FiregexTables(NFTableManager):
|
||||||
rules_chain_in = "firewall_rules_in"
|
rules_chain_in = "firewall_rules_in"
|
||||||
rules_chain_out = "firewall_rules_out"
|
rules_chain_out = "firewall_rules_out"
|
||||||
|
rules_chain_fwd = "firewall_rules_fwd"
|
||||||
|
|
||||||
def init_comands(self, policy:str="accept", policy_out:str="accept", allow_loopback=False, allow_established=False):
|
def init_comands(self, policy:str=Action.ACCEPT, allow_loopback=False, allow_established=False, allow_icmp=False):
|
||||||
return [
|
return [
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{
|
||||||
"family":"inet",
|
"family":"inet",
|
||||||
@@ -36,7 +15,16 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.rules_chain_in,
|
"name":self.rules_chain_in,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"prerouting",
|
"hook":"prerouting",
|
||||||
"prio":-150,
|
"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
|
"policy":policy
|
||||||
}}},
|
}}},
|
||||||
{"add":{"chain":{
|
{"add":{"chain":{
|
||||||
@@ -45,24 +33,41 @@ class FiregexTables(NFTableManager):
|
|||||||
"name":self.rules_chain_out,
|
"name":self.rules_chain_out,
|
||||||
"type":"filter",
|
"type":"filter",
|
||||||
"hook":"postrouting",
|
"hook":"postrouting",
|
||||||
"prio":-150,
|
"prio":0,
|
||||||
"policy":policy_out
|
"policy":Action.ACCEPT
|
||||||
}}},
|
}}},
|
||||||
] + ([
|
] + ([
|
||||||
{ "add":{ "rule": {
|
{ "add":{ "rule": {
|
||||||
"family": "inet", "table": self.table_name, "chain": self.rules_chain_out,
|
"family": "inet", "table": self.table_name, "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": "inet", "table": self.table_name, "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 allow_loopback else []) + ([
|
||||||
{ "add":{ "rule": {
|
{ "add":{ "rule": {
|
||||||
"family": "inet", "table": self.table_name, "chain": self.rules_chain_in,
|
"family": "inet", "table": self.table_name, "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"]} }, { "accept": None }]
|
||||||
}}}
|
}}}
|
||||||
] if allow_established else [])
|
] if allow_established else []) + ([
|
||||||
|
{ "add":{ "rule": {
|
||||||
|
"family": "inet", "table": self.table_name, "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,
|
||||||
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "icmp"} }, { "accept": None }]
|
||||||
|
}}},
|
||||||
|
{ "add":{ "rule": {
|
||||||
|
"family": "inet", "table": self.table_name, "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,
|
||||||
|
"expr": [{ "match": { "op": "==", "left": { "meta": { "key": "l4proto" } }, "right": "ipv6-icmp"} }, { "accept": None }]
|
||||||
|
}}}
|
||||||
|
] if allow_icmp else [])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(self.init_comands(),[
|
super().__init__(self.init_comands(),[
|
||||||
@@ -70,39 +75,57 @@ class FiregexTables(NFTableManager):
|
|||||||
{"delete":{"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}}},
|
{"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}}},
|
{"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}}},
|
||||||
])
|
])
|
||||||
|
|
||||||
def set(self, srvs:list[Rule], policy:str="accept", allow_loopback=False, allow_established=False):
|
def set(self, srvs:list[Rule], policy:str="accept", allow_loopback=False, allow_established=False, allow_icmp=False):
|
||||||
srvs = list(srvs)
|
srvs = list(srvs)
|
||||||
self.reset()
|
self.reset()
|
||||||
if policy == "reject":
|
if policy == Action.REJECT:
|
||||||
policy = "drop"
|
policy = Action.DROP
|
||||||
srvs.append(Rule(
|
srvs.append(Rule(
|
||||||
proto="any",
|
proto=Protocol.ANY,
|
||||||
ip_src="any",
|
src="",
|
||||||
ip_dst="any",
|
dst="",
|
||||||
port_src_from=1,
|
port_src_from=1,
|
||||||
port_dst_from=1,
|
port_dst_from=1,
|
||||||
port_src_to=65535,
|
port_src_to=65535,
|
||||||
port_dst_to=65535,
|
port_dst_to=65535,
|
||||||
action="reject",
|
action=Action.REJECT,
|
||||||
mode="I"
|
mode=Mode.IN
|
||||||
))
|
))
|
||||||
rules = self.init_comands(policy, allow_loopback=allow_loopback, allow_established=allow_established) + self.get_rules(*srvs)
|
rules = self.init_comands(policy, allow_loopback=allow_loopback, allow_established=allow_established, allow_icmp=allow_icmp) + self.get_rules(*srvs)
|
||||||
self.cmd(*rules)
|
self.cmd(*rules)
|
||||||
|
|
||||||
def get_rules(self,*srvs:Rule):
|
def get_rules(self,*srvs:Rule):
|
||||||
rules = []
|
rules = []
|
||||||
for srv in srvs:
|
final_srvs:list[Rule] = []
|
||||||
|
for ele in srvs:
|
||||||
|
if ele.proto == Protocol.BOTH:
|
||||||
|
udp_rule = copy.deepcopy(ele)
|
||||||
|
udp_rule.proto = Protocol.UDP.value
|
||||||
|
ele.proto = Protocol.TCP.value
|
||||||
|
final_srvs.append(udp_rule)
|
||||||
|
final_srvs.append(ele)
|
||||||
|
|
||||||
|
for srv in final_srvs:
|
||||||
ip_filters = []
|
ip_filters = []
|
||||||
if srv.ip_src.lower() != "any" and srv.ip_dst.lower() != "any":
|
|
||||||
ip_filters = [
|
if srv.src != "":
|
||||||
{'match': {'left': {'payload': {'protocol': ip_family(srv.ip_src), 'field': 'saddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_src)}},
|
if is_ip_parse(srv.src):
|
||||||
{'match': {'left': {'payload': {'protocol': ip_family(srv.ip_dst), 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_dst)}},
|
ip_filters.append({'match': {'left': {'payload': {'protocol': ip_family(srv.src), 'field': 'saddr'}}, 'op': '==', 'right': nftables_int_to_json(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)}})
|
||||||
|
else:
|
||||||
|
ip_filters.append({"match": { "op": "==", "left": { "meta": { "key": "oifname" } }, "right": srv.dst} })
|
||||||
|
|
||||||
port_filters = []
|
port_filters = []
|
||||||
if srv.proto != "any":
|
if not srv.proto in [Protocol.ANY, Protocol.BOTH]:
|
||||||
if srv.port_src_from != 1 or srv.port_src_to != 65535: #Any Port
|
if srv.port_src_from != 1 or srv.port_src_to != 65535: #Any Port
|
||||||
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'sport'}}, 'op': '>=', 'right': int(srv.port_src_from)}})
|
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'sport'}}, 'op': '>=', 'right': int(srv.port_src_from)}})
|
||||||
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'sport'}}, 'op': '<=', 'right': int(srv.port_src_to)}})
|
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'sport'}}, 'op': '<=', 'right': int(srv.port_src_to)}})
|
||||||
@@ -110,13 +133,13 @@ class FiregexTables(NFTableManager):
|
|||||||
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'dport'}}, 'op': '>=', 'right': int(srv.port_dst_from)}})
|
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'dport'}}, 'op': '>=', 'right': int(srv.port_dst_from)}})
|
||||||
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'dport'}}, 'op': '<=', 'right': int(srv.port_dst_to)}})
|
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'dport'}}, 'op': '<=', 'right': int(srv.port_dst_to)}})
|
||||||
if len(port_filters) == 0:
|
if len(port_filters) == 0:
|
||||||
port_filters.append({'match': {'left': {'payload': {'protocol': str(srv.proto), 'field': 'sport'}}, 'op': '!=', 'right': 0}}) #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": {
|
rules.append({ "add":{ "rule": {
|
||||||
"family": "inet",
|
"family": "inet",
|
||||||
"table": self.table_name,
|
"table": self.table_name,
|
||||||
"chain": self.rules_chain_out if srv.output_mode else self.rules_chain_in,
|
"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
|
"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
|
||||||
}}})
|
}}})
|
||||||
|
|||||||
@@ -6,27 +6,29 @@ from utils import ip_parse, ip_family, socketio_emit, PortType
|
|||||||
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
|
||||||
|
|
||||||
|
|
||||||
class RuleModel(BaseModel):
|
class RuleModel(BaseModel):
|
||||||
active: bool
|
active: bool
|
||||||
name: str
|
name: str
|
||||||
proto: str
|
proto: Protocol
|
||||||
ip_src: str
|
src: str
|
||||||
ip_dst: str
|
dst: str
|
||||||
port_src_from: PortType
|
port_src_from: PortType
|
||||||
port_dst_from: PortType
|
port_dst_from: PortType
|
||||||
port_src_to: PortType
|
port_src_to: PortType
|
||||||
port_dst_to: PortType
|
port_dst_to: PortType
|
||||||
action: str
|
action: Action
|
||||||
mode:str
|
mode:Mode
|
||||||
|
|
||||||
class RuleFormAdd(BaseModel):
|
class RuleFormAdd(BaseModel):
|
||||||
rules: list[RuleModel]
|
rules: list[RuleModel]
|
||||||
policy: str
|
policy: Action
|
||||||
|
|
||||||
class RuleInfo(BaseModel):
|
class RuleInfo(BaseModel):
|
||||||
rules: list[RuleModel]
|
rules: list[RuleModel]
|
||||||
policy: str
|
policy: Action
|
||||||
enabled: bool
|
enabled: bool
|
||||||
|
|
||||||
class RenameForm(BaseModel):
|
class RenameForm(BaseModel):
|
||||||
@@ -36,29 +38,30 @@ class FirewallSettings(BaseModel):
|
|||||||
keep_rules: bool
|
keep_rules: bool
|
||||||
allow_loopback: bool
|
allow_loopback: bool
|
||||||
allow_established: bool
|
allow_established: bool
|
||||||
|
allow_icmp: bool
|
||||||
app = APIRouter()
|
|
||||||
|
|
||||||
db = SQLite('db/firewall-rules.db', {
|
db = SQLite('db/firewall-rules.db', {
|
||||||
'rules': {
|
'rules': {
|
||||||
'rule_id': 'INT PRIMARY KEY CHECK (rule_id >= 0)',
|
'rule_id': 'INT PRIMARY KEY CHECK (rule_id >= 0)',
|
||||||
'mode': 'VARCHAR(1) NOT NULL CHECK (mode IN ("O", "I"))', # O = out, I = in, B = both
|
'mode': 'VARCHAR(10) NOT NULL CHECK (mode IN ("in", "out", "forward"))',
|
||||||
'name': 'VARCHAR(100) NOT NULL',
|
'name': 'VARCHAR(100) NOT NULL',
|
||||||
'active' : 'BOOLEAN NOT NULL CHECK (active IN (0, 1))',
|
'active' : 'BOOLEAN NOT NULL CHECK (active IN (0, 1))',
|
||||||
'proto': 'VARCHAR(3) NOT NULL CHECK (proto IN ("tcp", "udp", "any"))',
|
'proto': 'VARCHAR(10) NOT NULL CHECK (proto IN ("tcp", "udp", "both", "any"))',
|
||||||
'ip_src': 'VARCHAR(100) NOT NULL',
|
'src': 'VARCHAR(100) NOT NULL',
|
||||||
'port_src_from': 'INT CHECK(port_src_from > 0 and port_src_from < 65536)',
|
'port_src_from': 'INT CHECK(port_src_from > 0 and port_src_from < 65536)',
|
||||||
'port_src_to': 'INT CHECK(port_src_to > 0 and port_src_to < 65536 and port_src_from <= port_src_to)',
|
'port_src_to': 'INT CHECK(port_src_to > 0 and port_src_to < 65536 and port_src_from <= port_src_to)',
|
||||||
'ip_dst': 'VARCHAR(100) NOT NULL',
|
'dst': 'VARCHAR(100) NOT NULL',
|
||||||
'port_dst_from': 'INT CHECK(port_dst_from > 0 and port_dst_from < 65536)',
|
'port_dst_from': 'INT CHECK(port_dst_from > 0 and port_dst_from < 65536)',
|
||||||
'port_dst_to': 'INT CHECK(port_dst_to > 0 and port_dst_to < 65536 and port_dst_from <= port_dst_to)',
|
'port_dst_to': 'INT CHECK(port_dst_to > 0 and port_dst_to < 65536 and port_dst_from <= port_dst_to)',
|
||||||
'action': 'VARCHAR(10) NOT NULL CHECK (action IN ("accept", "drop", "reject"))',
|
'action': 'VARCHAR(10) NOT NULL CHECK (action IN ("accept", "drop", "reject"))',
|
||||||
},
|
},
|
||||||
'QUERY':[
|
'QUERY':[
|
||||||
"CREATE UNIQUE INDEX IF NOT EXISTS unique_rules ON rules (proto, ip_src, ip_dst, port_src_from, port_src_to, port_dst_from, port_dst_to, mode);"
|
"CREATE UNIQUE INDEX IF NOT EXISTS unique_rules ON rules (proto, src, dst, port_src_from, port_src_to, port_dst_from, port_dst_to, mode);"
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app = APIRouter()
|
||||||
|
|
||||||
firewall = FirewallManager(db)
|
firewall = FirewallManager(db)
|
||||||
|
|
||||||
async def reset(params: ResetRequest):
|
async def reset(params: ResetRequest):
|
||||||
@@ -101,7 +104,8 @@ async def get_settings():
|
|||||||
return {
|
return {
|
||||||
"keep_rules": firewall.keep_rules,
|
"keep_rules": firewall.keep_rules,
|
||||||
"allow_loopback": firewall.allow_loopback,
|
"allow_loopback": firewall.allow_loopback,
|
||||||
"allow_established": firewall.allow_established
|
"allow_established": firewall.allow_established,
|
||||||
|
"allow_icmp": firewall.allow_icmp
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/settings/set", response_model=StatusMessageModel)
|
@app.post("/settings/set", response_model=StatusMessageModel)
|
||||||
@@ -110,14 +114,15 @@ async def set_settings(form: FirewallSettings):
|
|||||||
firewall.keep_rules = form.keep_rules
|
firewall.keep_rules = form.keep_rules
|
||||||
firewall.allow_loopback = form.allow_loopback
|
firewall.allow_loopback = form.allow_loopback
|
||||||
firewall.allow_established = form.allow_established
|
firewall.allow_established = form.allow_established
|
||||||
return {'status': 'ok'}
|
firewall.allow_icmp = form.allow_icmp
|
||||||
|
return await apply_changes()
|
||||||
|
|
||||||
@app.get('/rules', response_model=RuleInfo)
|
@app.get('/rules', response_model=RuleInfo)
|
||||||
async def get_rule_list():
|
async def get_rule_list():
|
||||||
"""Get the list of existent firegex rules"""
|
"""Get the list of existent firegex rules"""
|
||||||
return {
|
return {
|
||||||
"policy": firewall.policy,
|
"policy": firewall.policy,
|
||||||
"rules": db.query("SELECT active, name, proto, ip_src, ip_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 FROM rules ORDER BY rule_id;"),
|
||||||
"enabled": firewall.enabled
|
"enabled": firewall.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,31 +140,34 @@ async def disable_firewall():
|
|||||||
|
|
||||||
def parse_and_check_rule(rule:RuleModel):
|
def parse_and_check_rule(rule:RuleModel):
|
||||||
|
|
||||||
if rule.ip_src.lower().strip() == "any" or rule.ip_dst.lower().split() == "any":
|
is_src_ip = is_dst_ip = True
|
||||||
rule.ip_dst = rule.ip_src = "any"
|
|
||||||
else:
|
try:
|
||||||
try:
|
rule.src = ip_parse(rule.src)
|
||||||
rule.ip_src = ip_parse(rule.ip_src)
|
except ValueError:
|
||||||
rule.ip_dst = ip_parse(rule.ip_dst)
|
is_src_ip = False
|
||||||
except ValueError:
|
|
||||||
raise HTTPException(status_code=400, detail="Invalid address")
|
try:
|
||||||
if ip_family(rule.ip_dst) != ip_family(rule.ip_src):
|
rule.dst = ip_parse(rule.dst)
|
||||||
raise HTTPException(status_code=400, detail="Destination and source addresses must be of the same family")
|
except ValueError:
|
||||||
|
is_dst_ip = False
|
||||||
|
|
||||||
|
if not is_src_ip and "/" in rule.src: # Slash is not allowed in ip interfaces names
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid source address")
|
||||||
|
if not is_dst_ip and "/" in rule.dst:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid destination address")
|
||||||
|
|
||||||
|
if is_src_ip and is_dst_ip and ip_family(rule.dst) != ip_family(rule.src):
|
||||||
|
raise HTTPException(status_code=400, detail="Destination and source addresses must be of the same family")
|
||||||
|
|
||||||
rule.port_dst_from, rule.port_dst_to = min(rule.port_dst_from, rule.port_dst_to), max(rule.port_dst_from, rule.port_dst_to)
|
rule.port_dst_from, rule.port_dst_to = min(rule.port_dst_from, rule.port_dst_to), max(rule.port_dst_from, rule.port_dst_to)
|
||||||
rule.port_src_from, rule.port_src_to = min(rule.port_src_from, rule.port_src_to), max(rule.port_src_from, rule.port_src_to)
|
rule.port_src_from, rule.port_src_to = min(rule.port_src_from, rule.port_src_to), max(rule.port_src_from, rule.port_src_to)
|
||||||
|
|
||||||
if rule.proto not in ["tcp", "udp", "any"]:
|
|
||||||
raise HTTPException(status_code=400, detail="Invalid protocol")
|
|
||||||
if rule.action not in ["accept", "drop", "reject"]:
|
|
||||||
raise HTTPException(status_code=400, detail="Invalid action")
|
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
@app.post('/rules/set', response_model=StatusMessageModel)
|
@app.post('/rules/set', response_model=StatusMessageModel)
|
||||||
async def add_new_service(form: RuleFormAdd):
|
async def add_new_service(form: RuleFormAdd):
|
||||||
"""Add a new service"""
|
"""Add a new service"""
|
||||||
if form.policy not in ["accept", "drop", "reject"]:
|
|
||||||
raise HTTPException(status_code=400, detail="Invalid policy")
|
|
||||||
rules = [parse_and_check_rule(ele) for ele in form.rules]
|
rules = [parse_and_check_rule(ele) for ele in form.rules]
|
||||||
try:
|
try:
|
||||||
db.queries(["DELETE FROM rules"]+
|
db.queries(["DELETE FROM rules"]+
|
||||||
@@ -167,20 +175,20 @@ async def add_new_service(form: RuleFormAdd):
|
|||||||
INSERT INTO rules (
|
INSERT INTO rules (
|
||||||
rule_id, active, name,
|
rule_id, active, name,
|
||||||
proto,
|
proto,
|
||||||
ip_src, ip_dst,
|
src, dst,
|
||||||
port_src_from, port_dst_from,
|
port_src_from, port_dst_from,
|
||||||
port_src_to, port_dst_to,
|
port_src_to, port_dst_to,
|
||||||
action, mode
|
action, mode
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?)""",
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,?, ?)""",
|
||||||
rid, ele.active, ele.name,
|
rid, ele.active, ele.name,
|
||||||
ele.proto,
|
ele.proto,
|
||||||
ele.ip_src, ele.ip_dst,
|
ele.src, ele.dst,
|
||||||
ele.port_src_from, ele.port_dst_from,
|
ele.port_src_from, ele.port_dst_from,
|
||||||
ele.port_src_to, ele.port_dst_to,
|
ele.port_src_to, ele.port_dst_to,
|
||||||
ele.action, ele.mode
|
ele.action, ele.mode
|
||||||
) for rid, ele in enumerate(rules)]
|
) for rid, ele in enumerate(rules)]
|
||||||
)
|
)
|
||||||
firewall.policy = form.policy
|
firewall.policy = form.policy.value
|
||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
raise HTTPException(status_code=400, detail="Error saving the rules: maybe there are duplicated rules")
|
raise HTTPException(status_code=400, detail="Error saving the rules: maybe there are duplicated rules")
|
||||||
return await apply_changes()
|
return await apply_changes()
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ def list_files(mypath):
|
|||||||
def ip_parse(ip:str):
|
def ip_parse(ip:str):
|
||||||
return str(ip_interface(ip).network)
|
return str(ip_interface(ip).network)
|
||||||
|
|
||||||
|
def is_ip_parse(ip:str):
|
||||||
|
try:
|
||||||
|
ip_parse(ip)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def addr_parse(ip:str):
|
def addr_parse(ip:str):
|
||||||
return str(ip_address(ip))
|
return str(ip_address(ip))
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ export const ModeSelector = (props:Omit<SegmentedControlProps, "data">) => (
|
|||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
value: RuleMode.IN,
|
value: RuleMode.IN,
|
||||||
label: 'Inbound',
|
label: 'IN',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: RuleMode.FORWARD,
|
||||||
|
label: 'FWD',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: RuleMode.OUT,
|
value: RuleMode.OUT,
|
||||||
label: 'Outbound',
|
label: 'OUT',
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
size={props.size?props.size:"xs"}
|
size={props.size?props.size:"xs"}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ export const ProtocolSelector = (props:Omit<SegmentedControlProps, "data">) => (
|
|||||||
value: Protocol.UDP,
|
value: Protocol.UDP,
|
||||||
label: 'UDP',
|
label: 'UDP',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: Protocol.BOTH,
|
||||||
|
label: 'BOTH',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: Protocol.ANY,
|
value: Protocol.ANY,
|
||||||
label: 'ANY',
|
label: 'ANY',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { getapi, postapi } from "../../js/utils"
|
|||||||
export enum Protocol {
|
export enum Protocol {
|
||||||
TCP = "tcp",
|
TCP = "tcp",
|
||||||
UDP = "udp",
|
UDP = "udp",
|
||||||
|
BOTH = "both",
|
||||||
ANY = "any"
|
ANY = "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,16 +16,17 @@ export enum ActionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum RuleMode {
|
export enum RuleMode {
|
||||||
OUT = "O",
|
OUT = "out",
|
||||||
IN = "I",
|
IN = "in",
|
||||||
|
FORWARD = "forward"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Rule = {
|
export type Rule = {
|
||||||
active: boolean
|
active: boolean
|
||||||
name:string,
|
name:string,
|
||||||
proto: Protocol,
|
proto: Protocol,
|
||||||
ip_src: string,
|
src: string,
|
||||||
ip_dst: string,
|
dst: string,
|
||||||
port_src_from: number,
|
port_src_from: number,
|
||||||
port_dst_from: number,
|
port_dst_from: number,
|
||||||
port_src_to: number,
|
port_src_to: number,
|
||||||
@@ -48,6 +50,7 @@ export type FirewallSettings = {
|
|||||||
keep_rules: boolean,
|
keep_rules: boolean,
|
||||||
allow_loopback: boolean,
|
allow_loopback: boolean,
|
||||||
allow_established: boolean,
|
allow_established: boolean,
|
||||||
|
allow_icmp: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -74,16 +77,6 @@ export const firewall = {
|
|||||||
disable: async() => {
|
disable: async() => {
|
||||||
return await getapi("firewall/disable") as ServerResponse;
|
return await getapi("firewall/disable") as ServerResponse;
|
||||||
},
|
},
|
||||||
rulenable: async (rule_id:number) => {
|
|
||||||
return await getapi(`firewall/rule/${rule_id}/enable`) as ServerResponse;
|
|
||||||
},
|
|
||||||
ruledisable: async (rule_id:number) => {
|
|
||||||
return await getapi(`firewall/rule/${rule_id}/disable`) as ServerResponse;
|
|
||||||
},
|
|
||||||
rulerename: async (rule_id:number, name: string) => {
|
|
||||||
const { status } = await postapi(`firewall/rule/${rule_id}/rename`,{ name }) as ServerResponse;
|
|
||||||
return status === "ok"?undefined:status
|
|
||||||
},
|
|
||||||
ruleset: async (data:RuleAddForm) => {
|
ruleset: async (data:RuleAddForm) => {
|
||||||
return await postapi("firewall/rules/set", data) as ServerResponseListed;
|
return await postapi("firewall/rules/set", data) as ServerResponseListed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,19 +14,26 @@ interface ItemProps extends AutocompleteItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface InterfaceInputProps extends Omit<SelectProps, "data">{
|
interface InterfaceInputProps extends Omit<SelectProps, "data">{
|
||||||
initialCustomInterfaces?:AutocompleteItem[]
|
initialCustomInterfaces?:AutocompleteItem[],
|
||||||
|
includeInterfaceNames?:boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InterfaceInput = (props:InterfaceInputProps) => {
|
export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => {
|
||||||
|
|
||||||
const { initialCustomInterfaces, ...propeties } = props
|
|
||||||
|
|
||||||
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
|
const [customIpInterfaces, setCustomIpInterfaces] = useState<AutocompleteItem[]>(initialCustomInterfaces??[]);
|
||||||
const interfacesQuery = ipInterfacesQuery()
|
const interfacesQuery = ipInterfacesQuery()
|
||||||
|
|
||||||
const interfaces = (!interfacesQuery.isLoading?
|
const getInterfaces = () => {
|
||||||
(interfacesQuery.data!.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[]):
|
if (interfacesQuery.isLoading || !interfacesQuery.data) return []
|
||||||
[])
|
if(includeInterfaceNames){
|
||||||
|
const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[]
|
||||||
|
interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item}))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[])
|
||||||
|
}
|
||||||
|
|
||||||
|
const interfaces = getInterfaces()
|
||||||
|
|
||||||
return <Select
|
return <Select
|
||||||
placeholder="10.1.1.1"
|
placeholder="10.1.1.1"
|
||||||
@@ -43,6 +50,6 @@ export const InterfaceInput = (props:InterfaceInputProps) => {
|
|||||||
return item;
|
return item;
|
||||||
}}
|
}}
|
||||||
style={props.style?{width:"100%", ...props.style}:{width:"100%"}}
|
style={props.style?{width:"100%", ...props.style}:{width:"100%"}}
|
||||||
{...propeties}
|
{...props}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -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})
|
const [settings, setSettings] = useState<FirewallSettings>({keep_rules:false, allow_established:true, allow_loopback:true, allow_icmp:true})
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
firewall.settings().then( res => {
|
firewall.settings().then( res => {
|
||||||
@@ -39,6 +39,7 @@ export function SettingsModal({ opened, onClose }:{ opened:boolean, onClose:()=>
|
|||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
<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})}/>
|
||||||
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -106,8 +106,8 @@ export const Firewall = () => {
|
|||||||
active: true,
|
active: true,
|
||||||
name: "Rule name",
|
name: "Rule name",
|
||||||
proto: Protocol.TCP,
|
proto: Protocol.TCP,
|
||||||
ip_src: "any",
|
src: "",
|
||||||
ip_dst: "any",
|
dst: "",
|
||||||
port_src_from: 1,
|
port_src_from: 1,
|
||||||
port_dst_from: 8080,
|
port_dst_from: 8080,
|
||||||
port_src_to: 65535,
|
port_src_to: 65535,
|
||||||
@@ -140,18 +140,15 @@ export const Firewall = () => {
|
|||||||
{(provided, snapshot) => {
|
{(provided, snapshot) => {
|
||||||
const customInt = [
|
const customInt = [
|
||||||
{ value: "0.0.0.0/0", netint: "ANY IPv4", label: "0.0.0.0/0" },
|
{ value: "0.0.0.0/0", netint: "ANY IPv4", label: "0.0.0.0/0" },
|
||||||
{ value: "::/0", netint: "ANY IPv6", label: "::/0" }
|
{ value: "::/0", netint: "ANY IPv6", label: "::/0" },
|
||||||
|
{ value: "", netint: "ANY", label: "ANY" }
|
||||||
]
|
]
|
||||||
const src_custom_int = customInt.map(v => v.value).includes(item.ip_src) || item.ip_dst == "any"?[]:[{ value: item.ip_src, netint: "SELECTED", label: item.ip_src }]
|
const src_custom_int = customInt.map(v => v.value).includes(item.src)?[]:[{ value: item.src, netint: "SELECTED", label: item.src }]
|
||||||
const dst_custom_int = customInt.map(v => v.value).includes(item.ip_dst) || item.ip_dst == "any"?[]:[{ value: item.ip_dst, netint: "SELECTED", label: item.ip_dst }]
|
const dst_custom_int = customInt.map(v => v.value).includes(item.dst)?[]:[{ value: item.dst, netint: "SELECTED", label: item.dst }]
|
||||||
const [srcPortEnabled, setSrcPortEnabled] = useState(item.port_src_from != 1 || item.port_src_to != 65535)
|
const [srcPortEnabled, setSrcPortEnabled] = useState(item.port_src_from != 1 || item.port_src_to != 65535)
|
||||||
const [dstPortEnabled, setDstPortEnabled] = useState(item.port_dst_from != 1 || item.port_dst_to != 65535)
|
const [dstPortEnabled, setDstPortEnabled] = useState(item.port_dst_from != 1 || item.port_dst_to != 65535)
|
||||||
const [srcPortValue, setSrcPortValue] = useState(item.port_src_from==item.port_src_to?`${item.port_src_from}`:`${item.port_src_from}-${item.port_src_to}`)
|
const [srcPortValue, setSrcPortValue] = useState(item.port_src_from==item.port_src_to?`${item.port_src_from}`:`${item.port_src_from}-${item.port_src_to}`)
|
||||||
const [dstPortValue, setDstPortValue] = useState(item.port_dst_from==item.port_dst_to?`${item.port_dst_from}`:`${item.port_dst_from}-${item.port_dst_to}`)
|
const [dstPortValue, setDstPortValue] = useState(item.port_dst_from==item.port_dst_to?`${item.port_dst_from}`:`${item.port_dst_from}-${item.port_dst_to}`)
|
||||||
const [ipFilteringEnabled, setIpFilteringEnabled] = useState(!(item.ip_dst == "any" || item.ip_src == "any"))
|
|
||||||
|
|
||||||
const [srcIp, setSrcIp] = useState(item.ip_src!="any"?item.ip_src:"")
|
|
||||||
const [dstIp, setDstIp] = useState(item.ip_dst!="any"?item.ip_dst:"")
|
|
||||||
|
|
||||||
const port_range_setter = (rule:Rule, v:string, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
|
const port_range_setter = (rule:Rule, v:string, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
|
||||||
const elements = v.split("-")
|
const elements = v.split("-")
|
||||||
@@ -173,39 +170,23 @@ export const Firewall = () => {
|
|||||||
const ip_setter = (rule:Rule, v:string|null, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
|
const ip_setter = (rule:Rule, v:string|null, {src=false, dst=false}:{src?:boolean, dst?:boolean}) => {
|
||||||
const values = v?v:""
|
const values = v?v:""
|
||||||
if (src){
|
if (src){
|
||||||
rule.ip_src = values
|
rule.src = values
|
||||||
setSrcIp(values)
|
|
||||||
}
|
}
|
||||||
if (dst){
|
if (dst){
|
||||||
rule.ip_dst = values
|
rule.dst = values
|
||||||
setDstIp(values)
|
|
||||||
}
|
}
|
||||||
updateMe()
|
updateMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
const set_filtering_ip = (value:boolean) => {
|
|
||||||
if (!value){
|
|
||||||
item.ip_src = "any"
|
|
||||||
item.ip_dst = "any"
|
|
||||||
}else{
|
|
||||||
item.ip_src = srcIp
|
|
||||||
item.ip_dst = dstIp
|
|
||||||
}
|
|
||||||
setIpFilteringEnabled(value)
|
|
||||||
updateMe()
|
|
||||||
}
|
|
||||||
|
|
||||||
const disable_style = { opacity:"0.4", cursor:"not-allowed" }
|
const disable_style = { opacity:"0.4", cursor:"not-allowed" }
|
||||||
const proto_any = item.proto == Protocol.ANY
|
const proto_any = item.proto == Protocol.ANY
|
||||||
const disabletab = {
|
const disabletab = {
|
||||||
ips:!ipFilteringEnabled,
|
|
||||||
port_box: proto_any,
|
port_box: proto_any,
|
||||||
src_port: !srcPortEnabled || proto_any,
|
src_port: !srcPortEnabled || proto_any,
|
||||||
dst_port: !dstPortEnabled || proto_any
|
dst_port: !dstPortEnabled || proto_any
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalStyle = {
|
const additionalStyle = {
|
||||||
ips:disabletab.ips?disable_style:{},
|
|
||||||
port_box: disabletab.port_box?disable_style:{},
|
port_box: disabletab.port_box?disable_style:{},
|
||||||
src_port: disabletab.src_port?disable_style:{},
|
src_port: disabletab.src_port?disable_style:{},
|
||||||
dst_port: disabletab.dst_port?disable_style:{}
|
dst_port: disabletab.dst_port?disable_style:{}
|
||||||
@@ -254,11 +235,9 @@ export const Firewall = () => {
|
|||||||
<div style={{width:"100%"}}>
|
<div style={{width:"100%"}}>
|
||||||
<InterfaceInput
|
<InterfaceInput
|
||||||
initialCustomInterfaces={[...src_custom_int, ...customInt]}
|
initialCustomInterfaces={[...src_custom_int, ...customInt]}
|
||||||
value={srcIp}
|
value={item.src}
|
||||||
onChange={v => ip_setter(item, v, {src:true})}
|
onChange={v => ip_setter(item, v, {src:true})}
|
||||||
disabled={disabletab.ips}
|
includeInterfaceNames
|
||||||
error={!disabletab.ips && !srcIp}
|
|
||||||
style={additionalStyle.ips}
|
|
||||||
/>
|
/>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<div className="center-flex" style={{width:"100%"}}>
|
<div className="center-flex" style={{width:"100%"}}>
|
||||||
@@ -289,11 +268,9 @@ export const Firewall = () => {
|
|||||||
<div style={{width:"100%"}}>
|
<div style={{width:"100%"}}>
|
||||||
<InterfaceInput
|
<InterfaceInput
|
||||||
initialCustomInterfaces={[...dst_custom_int, ...customInt]}
|
initialCustomInterfaces={[...dst_custom_int, ...customInt]}
|
||||||
defaultValue={dstIp}
|
defaultValue={item.dst}
|
||||||
onChange={v => ip_setter(item, v, {dst:true})}
|
onChange={v => ip_setter(item, v, {dst:true})}
|
||||||
disabled={disabletab.ips}
|
includeInterfaceNames
|
||||||
error={!disabletab.ips && !dstIp}
|
|
||||||
style={additionalStyle.ips}
|
|
||||||
/>
|
/>
|
||||||
<Space h="sm" />
|
<Space h="sm" />
|
||||||
<div className="center-flex" style={{width:"100%"}}>
|
<div className="center-flex" style={{width:"100%"}}>
|
||||||
@@ -338,14 +315,6 @@ export const Firewall = () => {
|
|||||||
onChange={(value)=>{item.proto = value as Protocol;updateMe()}}
|
onChange={(value)=>{item.proto = value as Protocol;updateMe()}}
|
||||||
style={{width:"100%"}}
|
style={{width:"100%"}}
|
||||||
/>
|
/>
|
||||||
<Space h="xs" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
size="xs" variant="light"
|
|
||||||
color={ipFilteringEnabled?"lime":"red"}
|
|
||||||
onClick={()=>set_filtering_ip(!ipFilteringEnabled)}
|
|
||||||
style={{width:"100%"}}
|
|
||||||
>{ipFilteringEnabled?"IP Filtering ON":"IP Filtering OFF"}</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Space h="md" />
|
<Space h="md" />
|
||||||
|
|||||||
Reference in New Issue
Block a user