Upgraded to pypacker

This commit is contained in:
nik012003
2022-07-10 23:30:53 +02:00
parent 6c890e3d95
commit beabbaed9d
5 changed files with 91 additions and 88 deletions

View File

@@ -1,8 +1,10 @@
#Building main conteiner #Building main conteiner
FROM python:slim-buster FROM python:slim-buster
RUN apt-get update && apt-get -y install build-essential libpcre3-dev python-dev git iptables libnetfilter-queue-dev RUN apt-get update && apt-get -y install build-essential libpcre3-dev git iptables libnetfilter-queue1
RUN git clone https://gitlab.com/guerrera.nicola/pypacker && cd pypacker && pip3 install .
WORKDIR /
RUN mkdir /execute RUN mkdir /execute
WORKDIR /execute WORKDIR /execute

View File

@@ -1,48 +1,12 @@
from threading import Thread
from typing import List from typing import List
from netfilterqueue import NetfilterQueue from pypacker import interceptor
from multiprocessing import Manager, Process from pypacker.layer3 import ip, ip6
from pypacker.layer4 import tcp, udp
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import os, traceback, pcre, re import os, traceback, pcre, re
QUEUE_BASE_NUM = 1000 QUEUE_BASE_NUM = 1000
def bind_queues(func, ipv6, len_list=1):
from scapy.all import IP, TCP, UDP, IPv6
if len_list <= 0: raise Exception("len must be >= 1")
queue_list = []
starts = QUEUE_BASE_NUM
end = starts
def func_wrap(pkt):
pkt_parsed = IPv6(pkt.get_payload()) if ipv6 else IP(pkt.get_payload())
try:
payload = None
if UDP in pkt_parsed: payload = pkt_parsed[UDP].payload
if TCP in pkt_parsed: payload = pkt_parsed[TCP].payload
if payload: func(pkt, pkt_parsed, bytes(payload))
else: pkt.accept()
except Exception:
traceback.print_exc()
pkt.accept()
while True:
if starts >= 65536:
raise Exception("Netfilter queue is full!")
try:
for _ in range(len_list):
queue_list.append(NetfilterQueue())
queue_list[-1].bind(end, func_wrap)
end+=1
end-=1
break
except OSError:
del queue_list[-1]
for ele in queue_list:
ele.unbind()
queue_list = []
starts = end = end+1
return queue_list, (starts, end)
class FilterTypes: class FilterTypes:
INPUT = "FIREGEX-INPUT" INPUT = "FIREGEX-INPUT"
OUTPUT = "FIREGEX-OUTPUT" OUTPUT = "FIREGEX-OUTPUT"
@@ -124,6 +88,53 @@ class FiregexFilter():
def delete(self): def delete(self):
self.iptable.delete_command(self.type, self.id) 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):
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)
self.itor_s_to_c, codes = self._start_queue(s_to_c, n_threads)
iptables.add_s_to_c(proto, port, codes)
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
elif self.proto == ProtoTypes.UDP: level4 = pkt_parsed[udp.UDP].body_bytes
if level4:
if func(level4):
return pkt_parsed.bin(), interceptor.NF_ACCEPT
elif self.proto == ProtoTypes.TCP:
pkt_parsed[tcp.TCP].flags &= 0x00
pkt_parsed[tcp.TCP].flags |= tcp.TH_FIN | tcp.TH_ACK
pkt_parsed[tcp.TCP].body_bytes = b""
return pkt_parsed.bin(), interceptor.NF_ACCEPT
else: return b"", interceptor.NF_DROP
else: return pkt_parsed.bin(), interceptor.NF_ACCEPT
except Exception:
traceback.print_exc()
return pkt_parsed.bin(), interceptor.NF_ACCEPT
ictor = interceptor.Interceptor()
starts = QUEUE_BASE_NUM
while True:
if starts >= 65536:
raise Exception("Netfilter queue is full!")
queue_ids = list(range(starts,starts+n_threads))
try:
ictor.start(func_wrap, queue_ids=queue_ids)
break
except interceptor.UnableToBindException as e:
starts = e.queue_id + 1
return ictor, (starts, starts+n_threads-1)
def stop(self):
self.itor_c_to_s.stop()
self.itor_s_to_c.stop()
class FiregexFilterManager: class FiregexFilterManager:
def __init__(self, ipv6): def __init__(self, ipv6):
@@ -170,14 +181,14 @@ class FiregexFilterManager:
for ele in self.get(): for ele in self.get():
if int(port) == ele.port: return None if int(port) == ele.port: return None
def c_to_s(pkt, data, payload): return func(pkt, data, payload, True) def c_to_s(pkt): return func(pkt, True)
def s_to_c(pkt, data, payload): return func(pkt, data, payload, False) def s_to_c(pkt): return func(pkt, False)
queues_c_to_s, codes = bind_queues(c_to_s, n_threads) itor = Interceptor( self.iptables,
self.iptables.add_c_to_s(proto, port, codes) c_to_s, s_to_c,
queues_s_to_c, codes = bind_queues(s_to_c, n_threads) proto, self.ipv6, port,
self.iptables.add_s_to_c(proto, port, codes) int(os.getenv("N_THREADS_NFQUEUE","1")))
return queues_c_to_s + queues_s_to_c return itor
def delete_all(self): def delete_all(self):
for filter_type in [FilterTypes.INPUT, FilterTypes.OUTPUT]: for filter_type in [FilterTypes.INPUT, FilterTypes.OUTPUT]:
@@ -212,8 +223,8 @@ class Proxy:
def __init__(self, port, ipv6, filters=None): def __init__(self, port, ipv6, filters=None):
self.manager = FiregexFilterManager(ipv6) self.manager = FiregexFilterManager(ipv6)
self.port = port self.port = port
self.filters = Manager().list(filters) if filters else Manager().list([]) self.filters = filters if filters else []
self.process = None self.interceptor = None
def set_filters(self, filters): def set_filters(self, filters):
elements_to_pop = len(self.filters) elements_to_pop = len(self.filters)
@@ -222,41 +233,29 @@ class Proxy:
for _ in range(elements_to_pop): for _ in range(elements_to_pop):
self.filters.pop(0) self.filters.pop(0)
def _starter(self):
self.manager.delete_by_port(self.port)
def regex_filter(pkt, data, packet, by_client):
try:
for i, filter in enumerate(self.filters):
if (by_client and filter.c_to_s) or (not by_client and filter.s_to_c):
match = filter.check(packet)
if (filter.is_blacklist and match) or (not filter.is_blacklist and not match):
filter.blocked+=1
try: self.filters[i] = filter
except Exception: pass
pkt.drop()
return
except IndexError: pass
pkt.accept()
queue_list = self.manager.add(ProtoTypes.TCP, self.port, regex_filter)
threads = []
for ele in queue_list:
threads.append(Thread(target=ele.run))
threads[-1].daemon = True
threads[-1].start()
for ele in threads: ele.join()
for ele in queue_list: ele.unbind()
def start(self): def start(self):
self.process = Process(target=self._starter) if not self.interceptor:
self.process.start() self.manager.delete_by_port(self.port)
def regex_filter(pkt, by_client):
try:
for filter in self.filters:
if (by_client and filter.c_to_s) or (not by_client and filter.s_to_c):
match = filter.check(pkt)
if (filter.is_blacklist and match) or (not filter.is_blacklist and not match):
filter.blocked+=1
return False
except IndexError: pass
return True
self.interceptor = self.manager.add(ProtoTypes.TCP, self.port, regex_filter)
def stop(self): def stop(self):
self.manager.delete_by_port(self.port) self.manager.delete_by_port(self.port)
if self.process: if self.interceptor:
self.process.kill() self.interceptor.stop()
self.process = None self.interceptor = None
def restart(self): def restart(self):
self.stop() self.stop()
self.start() self.start()

View File

@@ -3,7 +3,6 @@ httpx
uvicorn[standard] uvicorn[standard]
passlib[bcrypt] passlib[bcrypt]
python-jose[cryptography] python-jose[cryptography]
NetfilterQueue #pypacker
scapy
python-pcre python-pcre
fastapi-socketio fastapi-socketio

View File

@@ -21,6 +21,7 @@ def puts(text, *args, color=colors.white, is_bold=False, **kwargs):
def sep(): puts("-----------------------------------", is_bold=True) def sep(): puts("-----------------------------------", is_bold=True)
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--port', "-p", type=int, required=False, help='Port where open the web service of the firewall', default=4444) parser.add_argument('--port', "-p", type=int, required=False, help='Port where open the web service of the firewall', default=4444)
parser.add_argument('--thread-per-queue', "-t", type=int, required=False, help='Number of threads started for each queue', default=1)
parser.add_argument('--no-autostart', "-n", required=False, action="store_true", help='Auto-execute "docker-compose up -d --build"', default=False) parser.add_argument('--no-autostart', "-n", required=False, action="store_true", help='Auto-execute "docker-compose up -d --build"', default=False)
args = parser.parse_args() args = parser.parse_args()
@@ -44,6 +45,7 @@ services:
network_mode: "host" network_mode: "host"
environment: environment:
- PORT={args.port} - PORT={args.port}
- N_THREADS_NFQUEUE={args.thread_per_queue}
volumes: volumes:
- /execute/db - /execute/db
cap_add: cap_add:
@@ -65,6 +67,7 @@ services:
- {args.port}:{args.port} - {args.port}:{args.port}
environment: environment:
- PORT={args.port} - PORT={args.port}
- N_THREADS_NFQUEUE={args.thread_per_queue}
volumes: volumes:
- /execute/db - /execute/db
cap_add: cap_add:

View File

@@ -58,11 +58,11 @@ else: puts(f"Test Failed: Couldn't create service ✗", color=colors.red); exit(
#Delete the Service and exit #Delete the Service and exit
def exit_test(status_code=0): def exit_test(status_code=0):
if service_created: #if service_created:
if(firegex.delete(service)): # if(firegex.delete(service)):
puts(f"Sucessfully delete service with id {service}", color=colors.green) # puts(f"Sucessfully delete service with id {service} ✔", color=colors.green)
else: # else:
puts(f"Test Failed: Couldn't delete service ✗", color=colors.red); exit(1) # puts(f"Test Failed: Couldn't delete service ✗", color=colors.red); exit(1)
sep() sep()
server.terminate() server.terminate()
exit(status_code) exit(status_code)