# mNetVisualizer 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 > [!Important] > 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/mNetVisualizer/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 cd mNetVisualizer ``` ### 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://: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, mNetVisualizer 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 mNetVisualizer 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 mNetVisualizer-reader ``` Copy the generated key. Then in `backend/.env`: ``` CROWDSEC_URL=http://:8080 CROWDSEC_API_KEY= ``` 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://: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://: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