IP interface and Protocol + fix iptables

This commit is contained in:
DomySh
2022-07-12 15:47:31 +02:00
parent c4ab4c628e
commit 3034c66423
13 changed files with 243 additions and 145 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View 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.
*/

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -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,6 +84,13 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
<Space h="md" />
<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' },
@@ -92,13 +98,7 @@ function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void })
]}
{...form.getInputProps('proto')}
/>
<Space h="xl" />
<Switch
label="Auto-Start Service"
{...form.getInputProps('autostart', { type: 'checkbox' })}
/>
</div>
<Group position="right" mt="md">
<Button loading={submitLoading} type="submit">Add Service</Button>

View File

@@ -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' />

View File

@@ -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,
}

View File

@@ -18,7 +18,9 @@ function ServiceDetails() {
n_regex:0,
name:"",
ipv6:false,
status:"🤔"
status:"🤔",
ip_int: "",
proto: "tcp",
})
const [regexesList, setRegexesList] = useState<RegexFilter[]>([])