feat: initial release - MorkNetVizualiser v1.0
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
# NetMap
|
||||
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## 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 netmap
|
||||
```
|
||||
|
||||
### 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, NetMap 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 NetMap 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 netmap-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
|
||||
Reference in New Issue
Block a user