feat: implement packet sniffer with database integration and API endpoints
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s

This commit is contained in:
2025-11-25 13:40:13 +01:00
parent be54e665c4
commit 689e8aa717
5 changed files with 141 additions and 8 deletions

Binary file not shown.

View File

@@ -3,6 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware
import os
import src.Models.netplan as netplan
import src.network_api as network_api
import src.routes_sniffer as sniffer_router
from asyncio import subprocess
from fastapi import HTTPException
@@ -60,3 +61,4 @@ def nft_ruleset():
# ---------------------
app.include_router(network_api.router, prefix="/network", tags=["network"])
app.include_router(sniffer_router.router, prefix="/sniff")

View File

@@ -0,0 +1,86 @@
import asyncio
from scapy.all import sniff, Ether, IP
import asyncpg
from typing import List
import threading
DB_DSN = "postgresql://mitm_user:mitm_password@localhost:5432/mitm_db"
sniffer_tasks = {}
sniffer_threads = {}
async def db_insert_packet(pkt_info: dict):
conn = await asyncpg.connect(DB_DSN)
try:
await conn.execute("""
INSERT INTO packets(
iface, direction,
src_mac, dst_mac, eth_type,
src_ip, dst_ip, protocol,
length, ebpf_verdict, ebpf_chain, raw
)
VALUES(
$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12
);
""",
pkt_info["iface"],
pkt_info["direction"],
pkt_info["src_mac"],
pkt_info["dst_mac"],
pkt_info["eth_type"],
pkt_info["src_ip"],
pkt_info["dst_ip"],
pkt_info["protocol"],
pkt_info["length"],
pkt_info["ebpf_verdict"],
pkt_info["ebpf_chain"],
pkt_info["raw"]
)
finally:
await conn.close()
def handle_packet(pkt, iface):
"""
Scapy callback → run in thread.
"""
pkt_info = {
"iface": iface,
"direction": "unknown", # You can set eth0=ingress, eth1=egress
"src_mac": pkt[Ether].src if Ether in pkt else None,
"dst_mac": pkt[Ether].dst if Ether in pkt else None,
"eth_type": pkt[Ether].type if Ether in pkt else None,
"src_ip": pkt[IP].src if IP in pkt else None,
"dst_ip": pkt[IP].dst if IP in pkt else None,
"protocol": pkt[IP].proto if IP in pkt else None,
"length": len(pkt),
"ebpf_verdict": None, # will integrate later
"ebpf_chain": None,
"raw": bytes(pkt)
}
# push to asyncio loop
loop = asyncio.get_event_loop()
loop.create_task(db_insert_packet(pkt_info))
def start_sniffer_thread(iface: str):
def sniff_blocking():
sniff(prn=lambda x: handle_packet(x, iface), iface=iface, store=False)
thread = threading.Thread(target=sniff_blocking, daemon=True)
thread.start()
return thread
async def start_sniffing(interfaces: List[str]):
for iface in interfaces:
if iface in sniffer_threads:
continue
sniffer_threads[iface] = start_sniffer_thread(iface)
async def stop_sniffing():
# Scapy cannot easily stop sniff(), so we simply kill threads
sniffer_threads.clear()
return True

View File

@@ -0,0 +1,29 @@
from fastapi import APIRouter
from typing import List
from network_sniffer import start_sniffing, stop_sniffing
router = APIRouter()
sniffer_running_interfaces: List[str] = []
@router.post("/sniffer/start")
async def start_sniffer(bridge: str, interfaces: List[str]):
"""
Start packet sniffing on bridge member interfaces.
"""
global sniffer_running_interfaces
sniffer_running_interfaces = interfaces
await start_sniffing(interfaces)
return {
"status": "ok",
"started_on": interfaces
}
@router.post("/sniffer/stop")
async def stop_sniffer_api():
await stop_sniffing()
return {"status": "stopped"}

View File

@@ -17,12 +17,12 @@ sudo sed -i "s/^#listen_addresses =.*/listen_addresses = '*'/" "$PG_CONF"
echo "[3] Updating pg_hba.conf for LAN + localhost access…"
PG_HBA="/etc/postgresql/$(ls /etc/postgresql)/main/pg_hba.conf"
# Ensure localhost entry exists
if ! grep -q "host *all *all *127.0.0.1/32" "$PG_HBA"; then
# Add localhost entry if missing
if ! grep -Eq "^[ ]*host[ ]+all[ ]+all[ ]+127.0.0.1/32" "$PG_HBA"; then
echo "host all all 127.0.0.1/32 md5" | sudo tee -a "$PG_HBA"
fi
# Ensure LAN entry exists
# Add LAN entry if missing
if ! grep -q "$LAN_SUBNET" "$PG_HBA"; then
echo "host all all $LAN_SUBNET md5" | sudo tee -a "$PG_HBA"
fi
@@ -30,14 +30,16 @@ fi
echo "[4] Restarting PostgreSQL…"
sudo systemctl restart postgresql
echo "[5] Creating database and user (if not existing)…"
echo "[5] Creating database + user (idempotent)…"
sudo -u postgres psql <<EOF
DO \$\$
BEGIN
-- Create user if missing
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '$DB_USER') THEN
CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';
END IF;
-- Create database if missing
IF NOT EXISTS (SELECT FROM pg_database WHERE datname = '$DB_NAME') THEN
CREATE DATABASE $DB_NAME OWNER $DB_USER;
END IF;
@@ -45,26 +47,40 @@ END
\$\$;
EOF
echo "[6] Creating tables (idempotent)…"
sudo -u postgres psql -d $DB_NAME <<EOF
echo "[6] Creating table (idempotent)…"
sudo -u postgres psql -d "$DB_NAME" <<EOF
CREATE TABLE IF NOT EXISTS packet_log (
id BIGSERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ DEFAULT NOW(),
direction VARCHAR(16), -- ingress / egress
-- ingress / egress
direction VARCHAR(16),
-- eBPF metadata
interface VARCHAR(32),
-- Ethernet layer
src_mac VARCHAR(32),
dst_mac VARCHAR(32),
eth_type VARCHAR(16),
-- IP layer
src_ip VARCHAR(64),
dst_ip VARCHAR(64),
ip_protocol VARCHAR(16),
-- Transport layer
src_port INTEGER,
dst_port INTEGER,
packet_len INTEGER,
-- nftables metadata
nft_hook VARCHAR(32),
nft_table VARCHAR(64),
nft_table_name VARCHAR(64), -- <— renamed (fix)
nft_chain VARCHAR(64),
nft_verdict VARCHAR(32),
-- raw packet
raw_packet BYTEA
);
EOF