Skip to content

Breakingcircuitsllc/BCs-netwatch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 

Repository files navigation

β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—    β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—    β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ•β•β•β•β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘         β–ˆβ–ˆβ•”β•    β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—     β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘ β–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘        β–ˆβ–ˆβ•”β•     β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•     β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘       β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•‘   β•šβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•  β•šβ•β•       β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•   β•šβ•β•    β•šβ•β•β•β•šβ•β•β• β•šβ•β•  β•šβ•β•   β•šβ•β•    β•šβ•β•β•β•β•β•β•šβ•β•  β•šβ•β•

Real-Time Local Network Threat Monitor

by Breaking Circuits β€” We do not guarantee the attacker's safety


Python Version License Tests Grafana pfSense Platform


What Is It

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.


Features

πŸ” 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

Architecture

  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 Detection Rules

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

OS Fingerprinting

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

Quick Start

1 β€” Install

git clone https://github.com/breakingcircuits/bcs-netwatch
cd bcs-netwatch
sudo ./install.sh

Python dependencies:

pip3 install scapy fastapi "uvicorn[standard]" geoip2 influxdb-client websockets

The installer never installs or spawns Grafana or InfluxDB. It detects what's already running and points at it.


2 β€” Existing Grafana + InfluxDB (Proxmox, remote VM, etc.)

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_password

The installer will:

  • Probe both services and report their status
  • Write .env pointing 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-provision

Provision 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_token

3 β€” GeoIP

Option A β€” MaxMind GeoLite2 (recommended, free account)

# Sign up at https://www.maxmind.com/en/geolite2/signup
python3 geoip/download.py --key YOUR_LICENSE_KEY

Option B β€” ip-api.com (no registration, 45 req/min)

python3 geoip/download.py --use-fallback

4 β€” Run

# Via systemd (recommended for production):
sudo systemctl start bcsnetwatch
sudo journalctl -fu bcsnetwatch

# Direct (testing / dev):
sudo python3 server.py

Open the HUD β†’ http://<your-ip>:8765


5 β€” pfSense / Remote Capture

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_rsa
sudo systemctl restart bcsnetwatch

Multiple remote sources (ProCurve SPAN box + pfSense at the same time):

NETWATCH_REMOTE_HOSTS=192.168.1.1:admin:em0,10.0.0.5:root:eth0

Add 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...

Installer Reference

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

Configuration Reference

# .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

REST API

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

Project Structure

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)

Testing

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.


Security Hardening

  • Systemd service runs with CAP_NET_RAW + CAP_NET_ADMIN only β€” not full root
  • PrivateTmp=yes and ProtectSystem=strict in the unit file
  • .env written chmod 600 by installer β€” token never world-readable
  • .env excluded 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 tcpdump only in authorized_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...
    

Compatibility

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 ⚠️ Needs rc.d instead of systemd
Windows ❌ Use a VM or WSL2 noob

Roadmap

  • 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

License

MIT Β© 2026 Breaking Circuits


We do not guarantee the attacker's safety.

breakingcircuits.com Β |Β  bde.it.com Β |Β  beforedisaster.org

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors