βββββββ βββββββββββββββ ββββ βββββββββββββββββββββββ βββ ββββββ βββββββββ ββββββββββ βββ
ββββββββββββββββββββββββ βββββ βββββββββββββββββββββββ βββββββββββββββββββββββββββββββ βββ
βββββββββββ ββββ ββββββ βββββββββ βββ βββ ββ βββββββββββ βββ βββ ββββββββ
βββββββββββ ββββ ββββββββββββββββ βββ ββββββββββββββββββ βββ βββ ββββββββ
ββββββββββββββββ βββ βββ ββββββββββββββ βββ βββββββββββββ βββ βββ βββββββββββ βββ
βββββββ βββββββ βββ βββ βββββββββββββ βββ ββββββββ βββ βββ βββ ββββββββββ βββ
by Breaking Circuits β We do not guarantee the attacker's safety
BC's NetWatch is a passive, zero-footprint network threat detection system built for real environments β not labs. It captures and analyzes raw packets off your NIC (or remotely over SSH from pfSense/OPNsense), classifies threats in real time, fingerprints operating systems, geolocates sources, and fires audio and visual alerts the moment something hits.
Built entirely on open source. No agents on endpoints. No cloud dependency. No commercial license. Designed to integrate with infrastructure you already have running β including existing Grafana and InfluxDB instances.
| π 9 Threat Detection Rules | Port scans, SYN floods, ARP spoofing, ICMP floods, DNS exfiltration, brute-force, C2 beaconing, jumbo packet anomalies, new host discovery |
| π Geolocation | MaxMind GeoLite2 or ip-api.com fallback β country, city, org, lat/lon plotted live on a world map |
| π₯οΈ Passive OS Fingerprinting | TTL + TCP window scoring β Linux / Windows / BSD / Cisco β no active probing, completely silent on the wire |
| π Audio + Visual Alerts | Web Audio API tones graded by severity, flash banners, animated geo map drops, live scrolling event feed |
| π‘ Remote SSH Capture | Stream pcap from pfSense, OPNsense, OpenWrt, or any SSH host β no plugin, no agent, uses built-in tcpdump |
| π Grafana Integration | Points at your existing instance β auto-provisions datasource, dashboard, timeseries, piechart, geomap, and annotations |
| π WebSocket Push | Sub-second threat delivery to all connected browser clients simultaneously |
| βοΈ Systemd Service | Hardened unit with CAP_NET_RAW only β never runs as full root in production |
| π§ͺ 74 Tests | Full pytest suite covering all detection rules, fingerprinting, geo, event structure, and lifecycle |
NIC / SSH Stream (pfSense Β· OPNsense Β· OpenWrt Β· any Linux box)
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ThreatDetector (engine/detector.py) β
β β
β Scapy AsyncSniffer βββΊ Detection Rules Engine β
β β β
β βββββββββββββββββββββββββββββΌβββββββββββββββββββ β
β β PORT_SCAN SYN_FLOOD β ARP_SPOOF β β
β β ICMP_FLOOD DNS_EXFIL β BRUTE_FORCE β β
β β BEACON LARGE_PKT β NEW_HOST β β
β βββββββββββββββββββββββββββββΌβββββββββββββββββββ β
β β β
β GeoIP Resolver ββββββββββββββ€ β
β OS Fingerprinter ββββββββββββ β
βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β ThreatEvent
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI Server (server.py :8765) β
β β
β WebSocket /ws βββΊ Browser Alert HUD β
β REST /api/* βββΊ Stats Β· Events Β· Hosts β
β /grafana/* βββΊ SimpleJSON Datasource β
β InfluxDB Writer (async, non-blocking) β
ββββββββββββ¬βββββββββββββββββββββββ¬ββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββ ββββββββββββββββββββββββββββ
β Browser HUD β β Your existing Grafana β
β Live event feed β β Timeseries β
β Geo world map β β Piechart by type β
β OS fingerprint β β Geomap panel β
β Audio alerts β β CRITICAL/HIGH annots β
β Detail sidebar β β Points at existing β
ββββββββββββββββββββ β InfluxDB β never spawns β
ββββββββββββββββββββββββββββ
| Threat | Severity | Logic |
|---|---|---|
SYN_FLOOD |
π΄ CRITICAL | >100 SYN packets/sec from single source |
ARP_SPOOF |
π΄ CRITICAL | ARP reply MAC change detected for a known IP |
PORT_SCAN |
π HIGH | >15 unique destination ports within 10s |
BRUTE_FORCE |
π HIGH | >20 connections to SSH/RDP/FTP/Telnet in 30s |
DNS_EXFIL |
π HIGH | Query name >80 chars OR >50 queries in 5s |
ICMP_FLOOD |
π HIGH | >50 ICMP packets/sec from single source |
BEACON |
π HIGH | Regular interval connections, variance <2.0 (C2 pattern) |
LARGE_PKT |
π‘ MEDIUM | Packet >9000 bytes (jumbo frame anomaly) |
NEW_HOST |
π΅ INFO | First-seen IP address on the network |
Completely passive β no probes sent, invisible on the wire. Scored on TTL proximity + TCP window exact match so same-TTL OSes (Cisco vs BSD) are correctly separated.
| OS | TTL | TCP Window |
|---|---|---|
| Linux 4.x+ | 64 | 14600 |
| Linux 2.x/3.x | 64 | 5840 |
| Linux (modern) | 64 | 65535 |
| Windows Vista/7/10/11 | 128 | 8192 |
| Windows XP/2003 | 128 | 65535 |
| BSD / macOS | 255 | 65535 |
| Cisco IOS | 255 | 4128 |
git clone https://github.com/breakingcircuits/bcs-netwatch
cd bcs-netwatch
sudo ./install.shPython dependencies:
pip3 install scapy fastapi "uvicorn[standard]" geoip2 influxdb-client websocketsThe installer never installs or spawns Grafana or InfluxDB. It detects what's already running and points at it.
If you already have Grafana and InfluxDB running elsewhere β the common case β pass their addresses directly to the installer:
sudo ./install.sh \
--influx-url http://192.168.1.50:8086 \
--influx-token your_existing_token \
--influx-org your_org \
--influx-bucket threats \
--grafana-url http://192.168.1.50:3000 \
--grafana-pass your_grafana_passwordThe installer will:
- Probe both services and report their status
- Write
.envpointing at your existing instances - Auto-provision the Grafana dashboard if both are reachable and a token is provided
- Skip provisioning cleanly if either is unreachable β with instructions to run it manually later
To skip Grafana provisioning entirely:
sudo ./install.sh \
--influx-url http://192.168.1.50:8086 \
--influx-token your_token \
--no-provisionProvision manually at any time:
python3 grafana/provision.py \
--grafana-url http://192.168.1.50:3000 \
--influx-url http://192.168.1.50:8086 \
--influx-token your_tokenOption A β MaxMind GeoLite2 (recommended, free account)
# Sign up at https://www.maxmind.com/en/geolite2/signup
python3 geoip/download.py --key YOUR_LICENSE_KEYOption B β ip-api.com (no registration, 45 req/min)
python3 geoip/download.py --use-fallback# Via systemd (recommended for production):
sudo systemctl start bcsnetwatch
sudo journalctl -fu bcsnetwatch
# Direct (testing / dev):
sudo python3 server.pyOpen the HUD β http://<your-ip>:8765
No plugin. No agent. Uses pfSense's built-in tcpdump streamed over SSH. Works with OPNsense, OpenWrt, and any Linux/BSD box with tcpdump installed.
# 1. Generate a dedicated SSH key β no passphrase, runs headless
ssh-keygen -t ed25519 -f /root/.ssh/bcsnetwatch_rsa -N ''
# 2. Push to pfSense
ssh-copy-id -i /root/.ssh/bcsnetwatch_rsa.pub admin@192.168.1.1
# OR paste bcsnetwatch_rsa.pub into:
# pfSense UI β System β User Manager β admin β Authorized Keys
# 3. Verify
ssh -i /root/.ssh/bcsnetwatch_rsa admin@192.168.1.1 'tcpdump --version'Edit .env:
NETWATCH_MODE=both # local NIC + remote stream
NETWATCH_REMOTE_HOSTS=192.168.1.1:admin:em0 # host:user:interface
NETWATCH_SSH_KEY=/root/.ssh/bcsnetwatch_rsasudo systemctl restart bcsnetwatchMultiple remote sources (ProCurve SPAN box + pfSense at the same time):
NETWATCH_REMOTE_HOSTS=192.168.1.1:admin:em0,10.0.0.5:root:eth0Add sources at runtime without restarting:
curl -X POST http://localhost:8765/api/remote/add \
-H 'Content-Type: application/json' \
-d '{"host":"10.0.0.5","user":"root","iface":"eth0"}'The SSH stream is self-healing β auto-reconnects after reboots or updates.
Lock down the SSH key on pfSense (restrict to tcpdump only):
command="tcpdump -i em0 -w - -U -s 0 'ip or arp' 2>/dev/null",no-pty,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA...
sudo ./install.sh [options]
Options:
--influx-url <url> InfluxDB URL (default: http://localhost:8086)
--influx-token <token> InfluxDB API token
--influx-org <org> InfluxDB org (default: netwatch)
--influx-bucket <bucket> InfluxDB bucket (default: threats)
--grafana-url <url> Grafana URL (default: http://localhost:3000)
--grafana-user <user> Grafana user (default: admin)
--grafana-pass <pass> Grafana password (default: admin)
--iface <iface> Force capture interface (skips auto-detect)
--no-provision Skip Grafana provisioning entirely# .env β written by install.sh, chmod 600
# Restart after changes: sudo systemctl restart bcsnetwatch
# ββ Capture mode ββββββββββββββββββββββββββββββββββββββββββββββββββ
NETWATCH_MODE=local # local | remote | both
NETWATCH_IFACE=eth0 # local capture interface
LOCAL_NET=192.168.1.0/24 # your LAN subnet
# ββ Remote / pfSense ββββββββββββββββββββββββββββββββββββββββββββββ
NETWATCH_REMOTE_HOSTS= # 192.168.1.1:admin:em0,10.0.0.1:root:eth0
NETWATCH_SSH_KEY=/root/.ssh/bcsnetwatch_rsa
NETWATCH_SSH_PORT=22
# ββ GeoIP βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
NETWATCH_GEOIP_DB=/opt/bcsnetwatch/geoip/GeoLite2-City.mmdb
# ββ InfluxDB (existing instance) ββββββββββββββββββββββββββββββββββ
INFLUX_URL=http://192.168.1.50:8086
INFLUX_TOKEN=your_token_here
INFLUX_ORG=netwatch
INFLUX_BKT=threats
# ββ Tuning ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
NETWATCH_HISTORY=1000 # in-memory event ring buffer size| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stats |
Totals, by-severity, by-type, by-country, uptime |
GET |
/api/events?limit=100&severity=HIGH |
Filtered event history |
GET |
/api/hosts |
All discovered host IPs |
GET |
/api/interfaces |
Available capture interfaces |
POST |
/api/iface/{iface} |
Switch capture interface live |
POST |
/api/remote/add |
Add remote SSH capture source at runtime |
GET |
/api/remote/status |
Remote capture thread health |
WS |
/ws |
Live threat WebSocket stream |
GET |
/grafana |
Grafana health check |
POST |
/grafana/query |
SimpleJSON query endpoint |
POST |
/grafana/annotations |
CRITICAL/HIGH event annotations |
bcs-netwatch/
βββ server.py # FastAPI server β main entry point (:8765)
βββ engine/
β βββ detector.py # Threat detection engine (Scapy + all rules)
βββ static/
β βββ index.html # Browser HUD β live feed, geo map, audio alerts
βββ geoip/
β βββ download.py # MaxMind / ip-api.com setup helper
β βββ GeoLite2-City.mmdb # download separately β not in repo
βββ grafana/
β βββ provision.py # Grafana provisioner (points at existing instance)
βββ tests/
β βββ test_detector.py # 74-test pytest suite
βββ install.sh # Systemd installer β never spawns Grafana/InfluxDB
βββ pytest.ini
βββ .env # Runtime config (chmod 600, excluded from releases)
pip3 install pytest pytest-asyncio
python3 -m pytest tests/ -v======================== 74 passed in 4.58s ========================
Test coverage includes all 9 detection rules with synthesized packets, OS fingerprinting for all 7 signatures, GeoIP resolver behavior, event structure and JSON serialization, detector lifecycle, and remote capture method signatures.
- Systemd service runs with
CAP_NET_RAW + CAP_NET_ADMINonly β not full root PrivateTmp=yesandProtectSystem=strictin the unit file.envwrittenchmod 600by installer β token never world-readable.envexcluded from all release archives- Firewall the HUD port to your LAN:
sudo ufw allow from 192.168.0.0/16 to any port 8765 sudo ufw deny 8765
- Restrict the pfSense SSH key to
tcpdumponly inauthorized_keys:command="tcpdump -i em0 -w - -U -s 0 'ip or arp' 2>/dev/null",no-pty,no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA...
| Platform | Status |
|---|---|
| Debian / Ubuntu (x86_64) | β Fully supported |
| Proxmox VM (Debian guest) | β Fully supported |
| pfSense (remote SSH source) | β Fully supported |
| OPNsense (remote SSH source) | β Fully supported |
| HP ProCurve 3500yl (SPAN β Linux box) | β Fully supported |
| TP-Link managed (mirror β Linux box) | β Fully supported |
| OpenWrt (remote SSH source) | β Supported |
| Raspberry Pi (Debian ARM64) | β Supported |
| Single NIC (workstation / server) | β Supported β sees all traffic through that NIC |
| FreeBSD direct install | |
| Windows | β Use a VM or WSL2 noob |
- Threat intel feed integration (AbuseIPDB, Emerging Threats blocklists)
- Email / Slack / webhook notification hooks
- VLAN-aware capture with 802.1Q tag parsing
- AI-assisted anomaly baseline (normal traffic profiling)
- PCAP export of flagged sessions
- Web UI remote host and interface manager
- Multi-sensor unified Grafana view
MIT Β© 2026 Breaking Circuits
We do not guarantee the attacker's safety.
breakingcircuits.com Β |Β bde.it.com Β |Β beforedisaster.org