IP interface and Protocol + fix iptables
This commit is contained in:
@@ -10,6 +10,7 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jose import JWTError, jwt
|
||||
from passlib.context import CryptContext
|
||||
from fastapi_socketio import SocketManager
|
||||
from ipaddress import ip_interface
|
||||
|
||||
ON_DOCKER = len(sys.argv) > 1 and sys.argv[1] == "DOCKER"
|
||||
DEBUG = len(sys.argv) > 1 and sys.argv[1] == "DEBUG"
|
||||
@@ -24,7 +25,7 @@ class Settings(BaseSettings):
|
||||
JWT_ALGORITHM: str = "HS256"
|
||||
REACT_BUILD_DIR: str = "../frontend/build/" if not ON_DOCKER else "frontend/"
|
||||
REACT_HTML_PATH: str = os.path.join(REACT_BUILD_DIR,"index.html")
|
||||
VERSION = "1.4.0"
|
||||
VERSION = "1.5.0"
|
||||
|
||||
|
||||
settings = Settings()
|
||||
@@ -163,6 +164,8 @@ class ServiceModel(BaseModel):
|
||||
port: int
|
||||
name: str
|
||||
ipv6: bool
|
||||
proto: str
|
||||
ip_int: str
|
||||
n_regex: int
|
||||
n_packets: int
|
||||
|
||||
@@ -176,6 +179,8 @@ async def get_service_list(auth: bool = Depends(is_loggined)):
|
||||
s.port port,
|
||||
s.name name,
|
||||
s.ipv6 ipv6,
|
||||
s.proto proto,
|
||||
s.ip_int ip_int,
|
||||
COUNT(r.regex_id) n_regex,
|
||||
COALESCE(SUM(r.blocked_packets),0) n_packets
|
||||
FROM services s LEFT JOIN regexes r
|
||||
@@ -192,6 +197,8 @@ async def get_service_by_id(service_id: str, auth: bool = Depends(is_loggined)):
|
||||
s.port port,
|
||||
s.name name,
|
||||
s.ipv6 ipv6,
|
||||
s.proto proto,
|
||||
s.ip_int ip_int,
|
||||
COUNT(r.regex_id) n_regex,
|
||||
COALESCE(SUM(r.blocked_packets),0) n_packets
|
||||
FROM services s LEFT JOIN regexes r WHERE s.service_id = ?
|
||||
@@ -332,7 +339,6 @@ async def add_new_regex(form: RegexAddForm, auth: bool = Depends(is_loggined)):
|
||||
class ServiceAddForm(BaseModel):
|
||||
name: str
|
||||
port: int
|
||||
ipv6: bool
|
||||
proto: str
|
||||
ip_int: str
|
||||
|
||||
@@ -343,21 +349,22 @@ class ServiceAddResponse(BaseModel):
|
||||
@app.post('/api/services/add', response_model=ServiceAddResponse)
|
||||
async def add_new_service(form: ServiceAddForm, auth: bool = Depends(is_loggined)):
|
||||
"""Add a new service"""
|
||||
if form.ipv6:
|
||||
if not checkIpv6(form.ip_int):
|
||||
return {"status":"Invalid IPv6 address"}
|
||||
else:
|
||||
if not checkIpv4(form.ip_int):
|
||||
return {"status":"Invalid IPv4 address"}
|
||||
ipv6 = None
|
||||
try:
|
||||
ip_int = ip_interface(form.ip_int)
|
||||
ipv6 = ip_int.version == 6
|
||||
form.ip_int = str(ip_int)
|
||||
except ValueError:
|
||||
return {"status":"Invalid address"}
|
||||
if form.proto not in ["tcp", "udp"]:
|
||||
return {"status":"Invalid protocol"}
|
||||
srv_id = None
|
||||
try:
|
||||
srv_id = gen_service_id(db)
|
||||
db.query("INSERT INTO services (service_id ,name, port, ipv6, status) VALUES (?, ?, ?, ?, ?)",
|
||||
srv_id, refactor_name(form.name), form.port, form.ipv6, STATUS.STOP)
|
||||
db.query("INSERT INTO services (service_id ,name, port, ipv6, status, proto, ip_int) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
srv_id, refactor_name(form.name), form.port, ipv6, STATUS.STOP, form.proto, form.ip_int)
|
||||
except sqlite3.IntegrityError:
|
||||
return {'status': 'Name or/and ports of the service has been already assigned'}
|
||||
return {'status': 'This type of service already exists'}
|
||||
await firewall.reload()
|
||||
await refresh_frontend()
|
||||
return {'status': 'ok', 'service_id': srv_id}
|
||||
|
||||
@@ -4,6 +4,7 @@ from pypacker.layer3 import ip, ip6
|
||||
from pypacker.layer4 import tcp, udp
|
||||
from subprocess import Popen, PIPE
|
||||
import os, traceback, pcre, re
|
||||
from ipaddress import ip_interface
|
||||
|
||||
QUEUE_BASE_NUM = 1000
|
||||
|
||||
@@ -17,10 +18,12 @@ class ProtoTypes:
|
||||
|
||||
class IPTables:
|
||||
|
||||
def __init__(self, ipv6=False):
|
||||
def __init__(self, ipv6=False, table="mangle"):
|
||||
self.ipv6 = ipv6
|
||||
self.table = table
|
||||
|
||||
def command(self, params):
|
||||
params = ["-t", self.table] + params
|
||||
if os.geteuid() != 0:
|
||||
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
|
||||
return Popen(["ip6tables"]+params if self.ipv6 else ["iptables"]+params, stdout=PIPE, stderr=PIPE).communicate()
|
||||
@@ -48,18 +51,12 @@ class IPTables:
|
||||
self.command(["-F", str(name)])
|
||||
|
||||
def add_chain_to_input(self, name):
|
||||
if not self.find_if_filter_exists("INPUT", str(name)):
|
||||
self.command(["-I", "INPUT", "-j", str(name)])
|
||||
if not self.find_if_filter_exists("DOCKER-USER", str(name)):
|
||||
self.command(["-I", "DOCKER-USER", "-j", str(name)])
|
||||
|
||||
if not self.find_if_filter_exists("PREROUTING", str(name)):
|
||||
self.command(["-I", "PREROUTING", "-j", str(name)])
|
||||
|
||||
def add_chain_to_output(self, name):
|
||||
if not self.find_if_filter_exists("OUTPUT", str(name)):
|
||||
self.command(["-I", "OUTPUT", "-j", str(name)])
|
||||
if not self.find_if_filter_exists("FORWARD", str(name)):
|
||||
self.command(["-I", "FORWARD", "-j", str(name)])
|
||||
if not self.find_if_filter_exists("DOCKER-USER", str(name)):
|
||||
self.command(["-I", "DOCKER-USER", "-j", str(name)])
|
||||
if not self.find_if_filter_exists("POSTROUTING", str(name)):
|
||||
self.command(["-I", "POSTROUTING", "-j", str(name)])
|
||||
|
||||
def find_if_filter_exists(self, type, target):
|
||||
for filter in self.list_filters(type):
|
||||
@@ -67,34 +64,39 @@ class IPTables:
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_s_to_c(self, proto, port, queue_range):
|
||||
def add_s_to_c(self, queue_range, proto = None, port = None, ip_int = None):
|
||||
init, end = queue_range
|
||||
if init > end: init, end = end, init
|
||||
self.command([
|
||||
"-A", FilterTypes.OUTPUT, "-p", str(proto),
|
||||
"--sport", str(port), "-j", "NFQUEUE",
|
||||
"--queue-num" if init == end else "--queue-balance",
|
||||
f"{init}" if init == end else f"{init}:{end}", "--queue-bypass"
|
||||
self.command(["-A", FilterTypes.OUTPUT,
|
||||
* (["-p", str(proto)] if proto else []),
|
||||
* (["-s", str(ip_int)] if ip_int else []),
|
||||
* (["--sport", str(port)] if port else []),
|
||||
"-j", "NFQUEUE",
|
||||
* (["--queue-num", f"{init}"] if init == end else ["--queue-balance", f"{init}:{end}"]),
|
||||
"--queue-bypass"
|
||||
])
|
||||
|
||||
def add_c_to_s(self, proto, port, queue_range):
|
||||
def add_c_to_s(self, queue_range, proto = None, port = None, ip_int = None):
|
||||
init, end = queue_range
|
||||
if init > end: init, end = end, init
|
||||
self.command([
|
||||
"-A", FilterTypes.INPUT, "-p", str(proto),
|
||||
"--dport", str(port), "-j", "NFQUEUE",
|
||||
"--queue-num" if init == end else "--queue-balance",
|
||||
f"{init}" if init == end else f"{init}:{end}", "--queue-bypass"
|
||||
self.command(["-A", FilterTypes.INPUT,
|
||||
* (["-p", str(proto)] if proto else []),
|
||||
* (["-d", str(ip_int)] if ip_int else []),
|
||||
* (["--dport", str(port)] if port else []),
|
||||
"-j", "NFQUEUE",
|
||||
* (["--queue-num", f"{init}"] if init == end else ["--queue-balance", f"{init}:{end}"]),
|
||||
"--queue-bypass"
|
||||
])
|
||||
|
||||
class FiregexFilter():
|
||||
def __init__(self, type, number, queue, proto, port, ipv6):
|
||||
def __init__(self, type, number, queue, proto, port, ipv6, ip_int):
|
||||
self.type = type
|
||||
self.id = int(number)
|
||||
self.queue = queue
|
||||
self.proto = proto
|
||||
self.port = int(port)
|
||||
self.iptable = IPTables(ipv6)
|
||||
self.ip_int = str(ip_int)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<FiregexFilter type={self.type} id={self.id} port={self.port} proto={self.proto} queue={self.queue}>"
|
||||
@@ -103,18 +105,17 @@ class FiregexFilter():
|
||||
self.iptable.delete_command(self.type, self.id)
|
||||
|
||||
class Interceptor:
|
||||
def __init__(self, iptables, c_to_s, s_to_c, proto, ipv6, port, n_threads):
|
||||
def __init__(self, iptables, ip_int, c_to_s, s_to_c, proto, ipv6, port, n_threads):
|
||||
self.proto = proto
|
||||
self.ipv6 = ipv6
|
||||
self.itor_c_to_s, codes = self._start_queue(c_to_s, n_threads)
|
||||
iptables.add_c_to_s(proto, port, codes)
|
||||
iptables.add_c_to_s(queue_range=codes, proto=proto, port=port, ip_int=ip_int)
|
||||
self.itor_s_to_c, codes = self._start_queue(s_to_c, n_threads)
|
||||
iptables.add_s_to_c(proto, port, codes)
|
||||
iptables.add_s_to_c(queue_range=codes, proto=proto, port=port, ip_int=ip_int)
|
||||
|
||||
def _start_queue(self,func,n_threads):
|
||||
def func_wrap(ll_data, ll_proto_id, data, ctx, *args):
|
||||
pkt_parsed = ip6.IP6(data) if self.ipv6 else ip.IP(data)
|
||||
|
||||
try:
|
||||
level4 = None
|
||||
if self.proto == ProtoTypes.TCP: level4 = pkt_parsed[tcp.TCP].body_bytes
|
||||
@@ -152,9 +153,9 @@ class Interceptor:
|
||||
|
||||
class FiregexFilterManager:
|
||||
|
||||
def __init__(self, ipv6):
|
||||
self.ipv6 = ipv6
|
||||
self.iptables = IPTables(ipv6)
|
||||
def __init__(self, srv):
|
||||
self.ipv6 = srv["ipv6"]
|
||||
self.iptables = IPTables(self.ipv6)
|
||||
self.iptables.create_chain(FilterTypes.INPUT)
|
||||
self.iptables.create_chain(FilterTypes.OUTPUT)
|
||||
self.iptables.add_chain_to_input(FilterTypes.INPUT)
|
||||
@@ -177,30 +178,32 @@ class FiregexFilterManager:
|
||||
queue=queue_num,
|
||||
proto=filter["prot"],
|
||||
port=int(port[0]),
|
||||
ipv6=self.ipv6
|
||||
ipv6=self.ipv6,
|
||||
ip_int=filter["source"] if filter_type == FilterTypes.OUTPUT else filter["destination"]
|
||||
))
|
||||
return res
|
||||
|
||||
def add(self, proto, port, func, n_threads = 1):
|
||||
def add(self, proto, port, ip_int, func):
|
||||
for ele in self.get():
|
||||
if int(port) == ele.port: return None
|
||||
if int(port) == ele.port and proto == ele.proto and ip_interface(ip_int) == ip_interface(ele.ip_int):
|
||||
return None
|
||||
|
||||
def c_to_s(pkt): return func(pkt, True)
|
||||
def s_to_c(pkt): return func(pkt, False)
|
||||
|
||||
itor = Interceptor( self.iptables,
|
||||
c_to_s, s_to_c,
|
||||
proto, self.ipv6, port,
|
||||
int(os.getenv("N_THREADS_NFQUEUE","1")))
|
||||
itor = Interceptor( iptables=self.iptables, ip_int=ip_int,
|
||||
c_to_s=c_to_s, s_to_c=s_to_c,
|
||||
proto=proto, ipv6=self.ipv6, port=port,
|
||||
n_threads=int(os.getenv("N_THREADS_NFQUEUE","1")))
|
||||
return itor
|
||||
|
||||
def delete_all(self):
|
||||
for filter_type in [FilterTypes.INPUT, FilterTypes.OUTPUT]:
|
||||
self.iptables.flush_chain(filter_type)
|
||||
|
||||
def delete_by_port(self, port):
|
||||
def delete_by_srv(self, srv):
|
||||
for filter in self.get():
|
||||
if filter.port == int(port):
|
||||
if filter.port == int(srv["port"]) and filter.proto == srv["proto"] and ip_interface(filter.ip_int) == ip_interface(srv["ip_int"]):
|
||||
filter.delete()
|
||||
|
||||
class Filter:
|
||||
@@ -224,10 +227,10 @@ class Filter:
|
||||
return True if self.compiled_regex.search(data) else False
|
||||
|
||||
class Proxy:
|
||||
def __init__(self, port, ipv6, filters=None):
|
||||
self.manager = FiregexFilterManager(ipv6)
|
||||
self.port = port
|
||||
self.filters = filters if filters else []
|
||||
def __init__(self, srv, filters=None):
|
||||
self.srv = srv
|
||||
self.manager = FiregexFilterManager(self.srv)
|
||||
self.filters: List[Filter] = filters if filters else []
|
||||
self.interceptor = None
|
||||
|
||||
def set_filters(self, filters):
|
||||
@@ -239,7 +242,7 @@ class Proxy:
|
||||
|
||||
def start(self):
|
||||
if not self.interceptor:
|
||||
self.manager.delete_by_port(self.port)
|
||||
self.manager.delete_by_srv(self.srv)
|
||||
def regex_filter(pkt, by_client):
|
||||
try:
|
||||
for filter in self.filters:
|
||||
@@ -251,11 +254,11 @@ class Proxy:
|
||||
except IndexError: pass
|
||||
return True
|
||||
|
||||
self.interceptor = self.manager.add(ProtoTypes.TCP, self.port, regex_filter)
|
||||
self.interceptor = self.manager.add(self.srv["proto"], self.srv["port"], self.srv["ip_int"], regex_filter)
|
||||
|
||||
|
||||
def stop(self):
|
||||
self.manager.delete_by_port(self.port)
|
||||
self.manager.delete_by_srv(self.srv)
|
||||
if self.interceptor:
|
||||
self.interceptor.stop()
|
||||
self.interceptor = None
|
||||
|
||||
135
backend/utils.py
135
backend/utils.py
@@ -1,69 +1,27 @@
|
||||
from hashlib import md5
|
||||
import traceback
|
||||
from typing import Dict
|
||||
from proxy import Filter, Proxy
|
||||
import os, sqlite3, socket, asyncio, re
|
||||
import secrets
|
||||
import secrets, json
|
||||
from base64 import b64decode
|
||||
|
||||
LOCALHOST_IP = socket.gethostbyname(os.getenv("LOCALHOST_IP","127.0.0.1"))
|
||||
|
||||
regex_ipv6 = r"^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$";
|
||||
regex_ipv4 = r"^([0-9]{1,3}\\.){3}[0-9]{1,3}(\\/([0-9]|[1-2][0-9]|3[0-2]))?$"
|
||||
|
||||
def checkIpv6(ip:str):
|
||||
return bool(re.match(regex_ipv6, ip))
|
||||
|
||||
def checkIpv4(ip:str):
|
||||
return bool(re.match(regex_ipv4, ip))
|
||||
|
||||
class SQLite():
|
||||
def __init__(self, db_name) -> None:
|
||||
self.conn = None
|
||||
self.cur = None
|
||||
self.db_name = db_name
|
||||
|
||||
def connect(self) -> None:
|
||||
try:
|
||||
self.conn = sqlite3.connect(self.db_name, check_same_thread = False)
|
||||
except Exception:
|
||||
with open(self.db_name, 'x'):
|
||||
pass
|
||||
self.conn = sqlite3.connect(self.db_name, check_same_thread = False)
|
||||
def dict_factory(cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
self.conn.row_factory = dict_factory
|
||||
|
||||
def disconnect(self) -> None:
|
||||
self.conn.close()
|
||||
|
||||
def create_schema(self, tables = {}) -> None:
|
||||
cur = self.conn.cursor()
|
||||
for t in tables:
|
||||
cur.execute('''CREATE TABLE IF NOT EXISTS main.{}({});'''.format(t, ''.join([(c + ' ' + tables[t][c] + ', ') for c in tables[t]])[:-2]))
|
||||
cur.close()
|
||||
|
||||
def query(self, query, *values):
|
||||
cur = self.conn.cursor()
|
||||
try:
|
||||
cur.execute(query, values)
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
cur.close()
|
||||
try: self.conn.commit()
|
||||
except Exception: pass
|
||||
|
||||
def init(self):
|
||||
self.connect()
|
||||
self.create_schema({
|
||||
self.schema = {
|
||||
'services': {
|
||||
'service_id': 'VARCHAR(100) PRIMARY KEY',
|
||||
'status': 'VARCHAR(100) NOT NULL',
|
||||
'port': 'INT NOT NULL CHECK(port > 0 and port < 65536)',
|
||||
'name': 'VARCHAR(100) NOT NULL UNIQUE',
|
||||
'ipv6': 'BOOLEAN NOT NULL CHECK (ipv6 IN (0, 1)) DEFAULT 0',
|
||||
'proto': 'VARCHAR(3) NOT NULL CHECK (proto IN ("tcp", "udp"))',
|
||||
'ip_int': 'VARCHAR(100) NOT NULL',
|
||||
},
|
||||
'regexes': {
|
||||
'regex': 'TEXT NOT NULL',
|
||||
@@ -80,9 +38,62 @@ class SQLite():
|
||||
'key': 'VARCHAR(100) PRIMARY KEY',
|
||||
'value': 'VARCHAR(100) NOT NULL',
|
||||
},
|
||||
})
|
||||
self.query("CREATE UNIQUE INDEX IF NOT EXISTS unique_services ON services (ipv6,port);")
|
||||
self.query("CREATE UNIQUE INDEX IF NOT EXISTS unique_regex_service ON regexes (regex,service_id,is_blacklist,mode,is_case_sensitive);")
|
||||
'QUERY':[
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS unique_services ON services (ipv6, port, ip_int, proto);",
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS unique_regex_service ON regexes (regex,service_id,is_blacklist,mode,is_case_sensitive);"
|
||||
]
|
||||
}
|
||||
self.DB_VER = md5(json.dumps(self.schema).encode()).hexdigest()
|
||||
|
||||
def connect(self) -> None:
|
||||
try:
|
||||
self.conn = sqlite3.connect(self.db_name, check_same_thread = False)
|
||||
except Exception:
|
||||
with open(self.db_name, 'x'):
|
||||
pass
|
||||
self.conn = sqlite3.connect(self.db_name, check_same_thread = False)
|
||||
def dict_factory(cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
self.conn.row_factory = dict_factory
|
||||
|
||||
def disconnect(self) -> None:
|
||||
if self.conn: self.conn.close()
|
||||
|
||||
def create_schema(self, tables = {}) -> None:
|
||||
cur = self.conn.cursor()
|
||||
for t in tables:
|
||||
if t == "QUERY": continue
|
||||
cur.execute('CREATE TABLE IF NOT EXISTS main.{}({});'.format(t, ''.join([(c + ' ' + tables[t][c] + ', ') for c in tables[t]])[:-2]))
|
||||
if "QUERY" in tables: [cur.execute(qry) for qry in tables["QUERY"]]
|
||||
cur.close()
|
||||
|
||||
def query(self, query, *values):
|
||||
cur = self.conn.cursor()
|
||||
try:
|
||||
cur.execute(query, values)
|
||||
return cur.fetchall()
|
||||
finally:
|
||||
cur.close()
|
||||
try: self.conn.commit()
|
||||
except Exception: pass
|
||||
|
||||
def delete(self):
|
||||
self.disconnect()
|
||||
os.remove(self.db_name)
|
||||
|
||||
def init(self):
|
||||
self.connect()
|
||||
try:
|
||||
current_ver = self.query("SELECT value FROM keys_values WHERE key = 'DB_VERSION'")[0]['value']
|
||||
if current_ver != self.DB_VER: raise Exception("DB_VERSION is not correct")
|
||||
except Exception:
|
||||
self.delete()
|
||||
self.connect()
|
||||
self.create_schema(self.schema)
|
||||
self.query("INSERT INTO keys_values (key, value) VALUES ('DB_VERSION', ?)", self.DB_VER)
|
||||
|
||||
class KeyValueStorage:
|
||||
def __init__(self, db):
|
||||
@@ -108,11 +119,10 @@ class STATUS:
|
||||
class ServiceNotFoundException(Exception): pass
|
||||
|
||||
class ServiceManager:
|
||||
def __init__(self, id, port, ipv6, db):
|
||||
self.id = id
|
||||
self.port = port
|
||||
def __init__(self, srv, db):
|
||||
self.srv = srv
|
||||
self.db = db
|
||||
self.proxy = Proxy(port, ipv6)
|
||||
self.proxy = Proxy(srv)
|
||||
self.status = STATUS.STOP
|
||||
self.filters = {}
|
||||
self._update_filters_from_db()
|
||||
@@ -122,10 +132,10 @@ class ServiceManager:
|
||||
def _update_filters_from_db(self):
|
||||
res = self.db.query("""
|
||||
SELECT
|
||||
regex, mode, regex_id `id`, is_blacklist,
|
||||
regex, mode, regex_id id, is_blacklist,
|
||||
blocked_packets n_packets, is_case_sensitive
|
||||
FROM regexes WHERE service_id = ? AND active=1;
|
||||
""", self.id)
|
||||
""", self.srv["service_id"])
|
||||
|
||||
#Filter check
|
||||
old_filters = set(self.filters.keys())
|
||||
@@ -151,7 +161,7 @@ class ServiceManager:
|
||||
self.proxy.set_filters(self.filters.values())
|
||||
|
||||
def __update_status_db(self, status):
|
||||
self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.id)
|
||||
self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.srv["service_id"])
|
||||
|
||||
async def next(self,to):
|
||||
async with self.lock:
|
||||
@@ -216,13 +226,14 @@ class ProxyManager:
|
||||
|
||||
async def reload(self):
|
||||
async with self.lock:
|
||||
for srv in self.db.query('SELECT service_id, port, status, ipv6 FROM services;'):
|
||||
srv_id, srv_port, req_status, srv_ipv6 = srv["service_id"], srv["port"], srv["status"], srv["ipv6"]
|
||||
if srv_port in self.proxy_table:
|
||||
for srv in self.db.query('SELECT * FROM services;'):
|
||||
|
||||
srv_id = srv["service_id"]
|
||||
if srv_id in self.proxy_table:
|
||||
continue
|
||||
|
||||
self.proxy_table[srv_id] = ServiceManager(srv_id, srv_port, srv_ipv6, self.db)
|
||||
await self.proxy_table[srv_id].next(req_status)
|
||||
self.proxy_table[srv_id] = ServiceManager(srv, self.db)
|
||||
await self.proxy_table[srv_id].next(srv["status"])
|
||||
|
||||
async def _stats_updater(self, callback):
|
||||
try:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.08225a85.css",
|
||||
"main.js": "/static/js/main.3f472f16.js",
|
||||
"main.js": "/static/js/main.0e7d88b5.js",
|
||||
"index.html": "/index.html",
|
||||
"main.08225a85.css.map": "/static/css/main.08225a85.css.map",
|
||||
"main.3f472f16.js.map": "/static/js/main.3f472f16.js.map"
|
||||
"main.0e7d88b5.js.map": "/static/js/main.0e7d88b5.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.08225a85.css",
|
||||
"static/js/main.3f472f16.js"
|
||||
"static/js/main.0e7d88b5.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.3f472f16.js"></script><link href="/static/css/main.08225a85.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#FFFFFFFF"/><meta name="description" content="Firegex by Pwnzer0tt1"/><title>Firegex</title><script defer="defer" src="/static/js/main.0e7d88b5.js"></script><link href="/static/css/main.08225a85.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
3
frontend/build/static/js/main.0e7d88b5.js
Normal file
3
frontend/build/static/js/main.0e7d88b5.js
Normal file
File diff suppressed because one or more lines are too long
70
frontend/build/static/js/main.0e7d88b5.js.LICENSE.txt
Normal file
70
frontend/build/static/js/main.0e7d88b5.js.LICENSE.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* The buffer module from node.js, for the browser.
|
||||
*
|
||||
* @author Feross Aboukhadijeh <https://feross.org>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||||
|
||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Router v6.3.0
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
1
frontend/build/static/js/main.0e7d88b5.js.map
Normal file
1
frontend/build/static/js/main.0e7d88b5.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -29,7 +29,7 @@
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"start": "BROWSER=none react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
@@ -41,8 +41,7 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
|
||||
const submitRequest = ({ name, port, autostart, proto, ip_int }:ServiceAddForm) =>{
|
||||
setSubmitLoading(true)
|
||||
const ipv6 = ip_int.match(regex_ipv4)?false:true
|
||||
addservice({name, port, ipv6, proto, ip_int }).then( res => {
|
||||
addservice({name, port, proto, ip_int }).then( res => {
|
||||
if (res.status === "ok" && res.service_id){
|
||||
setSubmitLoading(false)
|
||||
close();
|
||||
@@ -69,7 +68,7 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
|
||||
<TextInput
|
||||
label="Public IP Interface (ipv4/ipv6 + CIDR allowed)"
|
||||
placeholder="10.40.20.0/8"
|
||||
placeholder="10.1.1.0/24"
|
||||
{...form.getInputProps('ip_int')}
|
||||
/>
|
||||
|
||||
@@ -85,20 +84,21 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
|
||||
|
||||
<Space h="md" />
|
||||
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{ label: 'TCP', value: 'tcp' },
|
||||
{ label: 'UDP', value: 'udp' },
|
||||
]}
|
||||
{...form.getInputProps('proto')}
|
||||
/>
|
||||
|
||||
<Space h="xl" />
|
||||
|
||||
<Switch
|
||||
label="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
/>
|
||||
<div className='center-flex'>
|
||||
<Switch
|
||||
label="Auto-Start Service"
|
||||
{...form.getInputProps('autostart', { type: 'checkbox' })}
|
||||
/>
|
||||
<div className="flex-spacer"></div>
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{ label: 'TCP', value: 'tcp' },
|
||||
{ label: 'UDP', value: 'udp' },
|
||||
]}
|
||||
{...form.getInputProps('proto')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Group position="right" mt="md">
|
||||
<Button loading={submitLoading} type="submit">Add Service</Button>
|
||||
|
||||
@@ -98,7 +98,7 @@ function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void })
|
||||
<Space h="xs" />
|
||||
<Badge color="violet" radius="sm" size="md" variant="filled">Regex: {service.n_regex}</Badge>
|
||||
<Space h="xs" />
|
||||
<Badge color={service.ipv6?"pink":"cyan"} radius="sm" size="md" variant="filled">Protocol: {service.ipv6?"IPv6":"IPv4"}</Badge>
|
||||
<Badge color={service.ipv6?"pink":"cyan"} radius="sm" size="md" variant="filled">{service.ip_int} on {service.proto}</Badge>
|
||||
</div>
|
||||
<MediaQuery largerThan="md" styles={{ display: 'none' }}>
|
||||
<div className='flex-spacer' />
|
||||
|
||||
@@ -10,6 +10,8 @@ export type Service = {
|
||||
status:string,
|
||||
port:number,
|
||||
ipv6:boolean,
|
||||
proto: string,
|
||||
ip_int: string,
|
||||
n_packets:number,
|
||||
n_regex:number,
|
||||
}
|
||||
@@ -17,7 +19,6 @@ export type Service = {
|
||||
export type ServiceAddForm = {
|
||||
name:string,
|
||||
port:number,
|
||||
ipv6:boolean,
|
||||
proto:string,
|
||||
ip_int:string,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ function ServiceDetails() {
|
||||
n_regex:0,
|
||||
name:"",
|
||||
ipv6:false,
|
||||
status:"🤔"
|
||||
status:"🤔",
|
||||
ip_int: "",
|
||||
proto: "tcp",
|
||||
})
|
||||
|
||||
const [regexesList, setRegexesList] = useState<RegexFilter[]>([])
|
||||
|
||||
Reference in New Issue
Block a user