Skip to content

Commit

Permalink
refactoring: firewall model changed using ufw model (due to docker iu…
Browse files Browse the repository at this point in the history
…ssues)
  • Loading branch information
domysh committed Sep 29, 2023
1 parent 2657428 commit b11fa66
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 159 deletions.
43 changes: 38 additions & 5 deletions backend/modules/firewall/firewall.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -25,13 +25,31 @@ async def reload(self):
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)
Expand Down Expand Up @@ -79,5 +97,20 @@ def allow_established(self):
@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")


52 changes: 38 additions & 14 deletions backend/modules/firewall/models.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,21 +15,10 @@ def __init__(self, proto: str, src:str, dst:str, port_src_from:str, port_dst_fro
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",
Expand All @@ -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
203 changes: 143 additions & 60 deletions backend/modules/firewall/nftables.py

Large diffs are not rendered by default.

28 changes: 6 additions & 22 deletions backend/modules/nfregex/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import base64

class Service:
def __init__(self, id: str, status: str, port: int, name: str, proto: str, ip_int: str):
self.id = id
def __init__(self, service_id: str, status: str, port: int, name: str, proto: str, ip_int: str, **other):
self.id = service_id
self.status = status
self.port = port
self.name = name
Expand All @@ -11,36 +11,20 @@ def __init__(self, id: str, status: str, port: int, name: str, proto: str, ip_in

@classmethod
def from_dict(cls, var: dict):
return cls(
id=var["service_id"],
status=var["status"],
port=var["port"],
name=var["name"],
proto=var["proto"],
ip_int=var["ip_int"]
)
return cls(**var)


class Regex:
def __init__(self, id: int, regex: bytes, mode: str, service_id: str, is_blacklist: bool, blocked_packets: int, is_case_sensitive: bool, active: bool):
def __init__(self, regex_id: int, regex: bytes, mode: str, service_id: str, is_blacklist: bool, blocked_packets: int, is_case_sensitive: bool, active: bool, **other):
self.regex = regex
self.mode = mode
self.service_id = service_id
self.is_blacklist = is_blacklist
self.blocked_packets = blocked_packets
self.id = id
self.id = regex_id
self.is_case_sensitive = is_case_sensitive
self.active = active

@classmethod
def from_dict(cls, var: dict):
return cls(
id=var["regex_id"],
regex=base64.b64decode(var["regex"]),
mode=var["mode"],
service_id=var["service_id"],
is_blacklist=var["is_blacklist"],
blocked_packets=var["blocked_packets"],
is_case_sensitive=var["is_case_sensitive"],
active=var["active"]
)
return cls(**var)
13 changes: 2 additions & 11 deletions backend/modules/porthijack/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Service:
def __init__(self, service_id: str, active: bool, public_port: int, proxy_port: int, name: str, proto: str, ip_src: str, ip_dst:str):
def __init__(self, service_id: str, active: bool, public_port: int, proxy_port: int, name: str, proto: str, ip_src: str, ip_dst:str, **other):
self.service_id = service_id
self.active = active
self.public_port = public_port
Expand All @@ -11,13 +11,4 @@ def __init__(self, service_id: str, active: bool, public_port: int, proxy_port:

@classmethod
def from_dict(cls, var: dict):
return cls(
service_id=var["service_id"],
active=var["active"],
public_port=var["public_port"],
proxy_port=var["proxy_port"],
name=var["name"],
proto=var["proto"],
ip_src=var["ip_src"],
ip_dst=var["ip_dst"]
)
return cls(**var)
49 changes: 4 additions & 45 deletions backend/routers/firewall.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
import sqlite3
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from utils.sqlite import SQLite
from utils import ip_parse, ip_family, socketio_emit, PortType
from utils import ip_parse, ip_family, socketio_emit
from utils.models import ResetRequest, StatusMessageModel
from modules.firewall.nftables import FiregexTables
from modules.firewall.firewall import FirewallManager
from modules.firewall.models import Protocol, Mode, Action


class RuleModel(BaseModel):
active: bool
name: str
proto: Protocol
src: str
dst: str
port_src_from: PortType
port_dst_from: PortType
port_src_to: PortType
port_dst_to: PortType
action: Action
mode:Mode

class RuleFormAdd(BaseModel):
rules: list[RuleModel]
policy: Action

class RuleInfo(BaseModel):
rules: list[RuleModel]
policy: Action
enabled: bool

class RenameForm(BaseModel):
name:str

class FirewallSettings(BaseModel):
keep_rules: bool
allow_loopback: bool
allow_established: bool
allow_icmp: bool
from modules.firewall.models import *

db = SQLite('db/firewall-rules.db', {
'rules': {
Expand Down Expand Up @@ -101,20 +68,12 @@ async def apply_changes():
@app.get("/settings", response_model=FirewallSettings)
async def get_settings():
"""Get the firewall settings"""
return {
"keep_rules": firewall.keep_rules,
"allow_loopback": firewall.allow_loopback,
"allow_established": firewall.allow_established,
"allow_icmp": firewall.allow_icmp
}
return firewall.settings

@app.post("/settings/set", response_model=StatusMessageModel)
async def set_settings(form: FirewallSettings):
"""Set the firewall settings"""
firewall.keep_rules = form.keep_rules
firewall.allow_loopback = form.allow_loopback
firewall.allow_established = form.allow_established
firewall.allow_icmp = form.allow_icmp
firewall.settings = form
return await apply_changes()

@app.get('/rules', response_model=RuleInfo)
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/Firewall/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export type FirewallSettings = {
keep_rules: boolean,
allow_loopback: boolean,
allow_established: boolean,
allow_icmp: boolean
allow_icmp: boolean,
multicast_dns: boolean,
allow_upnp: boolean
}


Expand Down
6 changes: 5 additions & 1 deletion frontend/src/pages/Firewall/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { FirewallSettings, firewall } from '../../components/Firewall/utils';

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(()=>{
firewall.settings().then( res => {
Expand Down Expand Up @@ -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})}/>
<Space h="md" />
<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">
<Button loading={submitLoading} onClick={submitRequest}>Save Setting</Button>
Expand Down

0 comments on commit b11fa66

Please sign in to comment.