294 lines
7.6 KiB
Markdown
294 lines
7.6 KiB
Markdown
# MorkNetVisualizer
|
|
|
|
A real-time network traffic visualization tool for home and small office networks.
|
|
It displays live traffic flows as animated arcs on a world map, showing where your
|
|
devices are connecting to and where inbound traffic originates from.
|
|
|
|
---
|
|
|
|
## Important disclaimer
|
|
|
|
This project was built entirely through AI-assisted development (Claude by Anthropic).
|
|
No human has audited, reviewed, or manually written the code. Use it at your own risk
|
|
in trusted network environments. Do not expose it to the public internet without
|
|
adding authentication in front of it (e.g. via a reverse proxy with access control).
|
|
|
|
---
|
|
|
|
## Download
|
|
|
|
[Download v1.0 tar.gz](https://github.com/danielmorkcode/MorkNetVisualizer/releases/download/v1.0.0/netmapFinal.tar.gz)
|
|
|
|
Or clone the repository and build from source.
|
|
|
|
---
|
|
|
|
## What it does
|
|
|
|
- Captures real network flows via NetFlow v9/IPFIX from your router
|
|
- Geolocates source and destination IPs using a local MaxMind database
|
|
- Draws animated arcs on a world map showing traffic direction and volume
|
|
- Shows active devices, traffic volume, protocol breakdown, and top talkers
|
|
- Overlays CrowdSec banned IPs as markers on the map (optional)
|
|
- Displays a day/night overlay based on current solar position
|
|
- Supports arc history replay, connection heatmap, device grouping, and more
|
|
|
|
---
|
|
|
|
## Requirements
|
|
|
|
### Network
|
|
|
|
- A router that supports NetFlow v9 or IPFIX export (tested with UniFi Dream Router)
|
|
- The router must be able to reach the host running this tool over UDP
|
|
|
|
### Host
|
|
|
|
- Docker and Docker Compose
|
|
- At least 512 MB RAM
|
|
- Network reachable from the router for NetFlow (UDP) and from your browser over HTTP
|
|
|
|
### Accounts and files
|
|
|
|
**MaxMind GeoLite2 (required)**
|
|
|
|
NetFlow gives you IP addresses. MaxMind translates those IPs into geographic coordinates
|
|
for map display. Without it the map will show no arcs.
|
|
|
|
1. Create a free account at https://www.maxmind.com/en/geolite2/signup
|
|
2. Download GeoLite2-City in the binary .mmdb format
|
|
3. Place it at `data/GeoLite2-City.mmdb` in the project directory
|
|
|
|
The database is updated weekly by MaxMind. To refresh it, replace the file and
|
|
restart the backend container.
|
|
|
|
**UniFi API key (required)**
|
|
|
|
Used to fetch device names and active client list.
|
|
|
|
1. Log into your UniFi controller
|
|
2. Go to Settings -> System -> Advanced -> API Keys
|
|
3. Create a new API key and copy it
|
|
|
|
**CrowdSec bouncer API key (optional)**
|
|
|
|
See the CrowdSec section below.
|
|
|
|
---
|
|
|
|
## Tech stack
|
|
|
|
**Backend**
|
|
|
|
- Python 3.12
|
|
- FastAPI with uvicorn (HTTP API and WebSocket server)
|
|
- Native NetFlow v5/v9 parser written in pure Python using the struct module
|
|
- geoip2 (MaxMind GeoLite2 database reader)
|
|
- httpx (async HTTP client for UniFi and external APIs)
|
|
|
|
**Frontend**
|
|
|
|
- React 18 with Vite
|
|
- d3-geo with Natural Earth projection for the SVG world map
|
|
- topojson-client for country geometry
|
|
- WebSocket for live data streaming
|
|
- All state persistence via localStorage (no external dependencies for UI state)
|
|
|
|
**Infrastructure**
|
|
|
|
- Docker Compose with two containers: backend (Python) and frontend (nginx serving built React)
|
|
- NetFlow collected on UDP (default port 2055)
|
|
- nginx proxies WebSocket and API calls internally so only one port is exposed
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
### 1. Clone or download the project
|
|
|
|
```
|
|
git clone <your-repo-url>
|
|
cd MorkNetVisualizer
|
|
```
|
|
|
|
### 2. Place the GeoIP database
|
|
|
|
```
|
|
mkdir -p data
|
|
cp /path/to/GeoLite2-City.mmdb data/
|
|
```
|
|
|
|
### 3. Configure the environment
|
|
|
|
```
|
|
cp backend/.env.example backend/.env
|
|
```
|
|
|
|
Edit `backend/.env` and fill in your values. See the configuration section below.
|
|
|
|
### 4. Configure NetFlow on your router
|
|
|
|
On a UniFi Dream Router:
|
|
|
|
1. Go to Settings -> CyberSecure -> Traffic Logging -> NetFlow (IPFIX)
|
|
2. Enable it
|
|
3. Set collector address to the IP of the host running this tool
|
|
4. Set collector port to 2055 (or whatever you set in NETFLOW_PORT)
|
|
5. Set version to 9
|
|
6. Set sampling to Off
|
|
|
|
### 5. Start the stack
|
|
|
|
```
|
|
docker compose up -d --build
|
|
```
|
|
|
|
The first build takes 2-3 minutes. After that, open a browser to:
|
|
|
|
```
|
|
http://<host-ip>:3500
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
All configuration lives in `backend/.env`. Copy `backend/.env.example` as a starting point.
|
|
|
|
```
|
|
# UniFi Controller
|
|
UNIFI_URL=https://192.168.1.1 # IP or hostname of your UniFi controller
|
|
UNIFI_API_KEY= # API key from UniFi settings
|
|
UNIFI_SITE=default # Site name, usually "default"
|
|
UNIFI_VERIFY_SSL=false # Set to false for self-signed certificates
|
|
|
|
# GeoIP
|
|
GEOIP_DB_PATH=/data/GeoLite2-City.mmdb
|
|
|
|
# NetFlow collector
|
|
NETFLOW_PORT=2055 # Must match what your router is configured to send to
|
|
|
|
# Home location (shown as the origin point on the map)
|
|
HOME_LAT=51.5074
|
|
HOME_LON=-0.1278
|
|
HOME_NAME=Home
|
|
|
|
# CrowdSec (optional, leave blank to disable)
|
|
CROWDSEC_URL=
|
|
CROWDSEC_API_KEY=
|
|
```
|
|
|
|
---
|
|
|
|
## CrowdSec integration (optional)
|
|
|
|
CrowdSec is an open source threat detection tool. When configured, MorkNetVisualizer will
|
|
display banned IPs as red X markers on the map, with new bans showing a brief
|
|
pulse animation. You can filter between locally detected bans and the community
|
|
blocklist.
|
|
|
|
### Setup
|
|
|
|
CrowdSec must be running on a host reachable from the MorkNetVisualizer container.
|
|
By default, CrowdSec only listens on localhost (127.0.0.1:8080). To allow
|
|
remote access from another host, edit `/etc/crowdsec/config.yaml` on the
|
|
CrowdSec host:
|
|
|
|
```yaml
|
|
api:
|
|
server:
|
|
listen_uri: 0.0.0.0:8080
|
|
```
|
|
|
|
Then restart CrowdSec:
|
|
|
|
```
|
|
sudo systemctl restart crowdsec
|
|
```
|
|
|
|
Create a bouncer API key:
|
|
|
|
```
|
|
sudo cscli bouncers add MorkNetVisualizer-reader
|
|
```
|
|
|
|
Copy the generated key. Then in `backend/.env`:
|
|
|
|
```
|
|
CROWDSEC_URL=http://<crowdsec-host-ip>:8080
|
|
CROWDSEC_API_KEY=<key from above>
|
|
```
|
|
|
|
Restart the backend:
|
|
|
|
```
|
|
docker compose restart backend
|
|
```
|
|
|
|
---
|
|
|
|
## Updating the GeoIP database
|
|
|
|
MaxMind releases updated databases weekly. To update:
|
|
|
|
1. Download the new GeoLite2-City.mmdb from your MaxMind account
|
|
2. Replace `data/GeoLite2-City.mmdb`
|
|
3. Restart the backend: `docker compose restart backend`
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**No arcs on the map**
|
|
|
|
- Check that NetFlow is enabled on your router and pointing at the correct host and port
|
|
- Verify the backend is receiving packets: `curl http://<host>:3500/api/status`
|
|
Look for `netflow_stats.packets` incrementing
|
|
- Confirm the GeoLite2 database is present: `docker compose exec backend ls /data`
|
|
- Check backend logs: `docker compose logs backend`
|
|
|
|
**Reconnecting shown in the UI**
|
|
|
|
- The WebSocket connection to the backend failed
|
|
- Check `docker compose logs backend` for errors
|
|
- Verify the backend container is running: `docker compose ps`
|
|
|
|
**CrowdSec shows no bans**
|
|
|
|
- Run `curl http://<crowdsec-host>:8080/v1/decisions` with the API key header
|
|
to verify the CrowdSec API is reachable
|
|
- Check that `listen_uri` in `/etc/crowdsec/config.yaml` is `0.0.0.0:8080`
|
|
and not `127.0.0.1:8080`
|
|
|
|
**ASN/Org shows Unknown**
|
|
|
|
- ip-api.com has a rate limit of 45 requests per minute on the free tier
|
|
- The backend caches results, so this only affects the first lookup per IP
|
|
- If you are clicking many different IPs quickly, wait a moment and try again
|
|
|
|
---
|
|
|
|
## Ports
|
|
|
|
| Port | Protocol | Purpose |
|
|
|------|----------|---------|
|
|
| 3500 | TCP | Web interface (nginx, proxies to backend) |
|
|
| 2055 | UDP | NetFlow/IPFIX collector |
|
|
|
|
---
|
|
|
|
## Data privacy
|
|
|
|
All data stays on your host. No traffic data, IP addresses, or device information
|
|
is sent anywhere. The only outbound requests are:
|
|
|
|
- MaxMind GeoLite2 downloads (manual, on your schedule)
|
|
- ASN lookups to ip-api.com and ipinfo.io (triggered by clicking an arc in the UI)
|
|
- World map GeoJSON loaded from a CDN on first page load (cdn.jsdelivr.net)
|
|
|
|
---
|
|
|
|
## License
|
|
|
|
MIT
|