~/projects / net-health-monitor

-rwxr-xr-x · #tools · started 2026-04-13 · MIT

net-health-monitor/

A self-hosted dashboard that pings hosts, scans a few TCP ports, runs DNS lookups and asks an LLM to explain what it found. It began as a public demo. I pulled it off the internet and rebuilt it as a local tool, because anything that scans on request is easy to abuse.

  • Python
  • FastAPI
  • Pydantic
  • React
  • Vite
  • Docker Compose
  • nginx
  • pytest
  • GitHub Actions
Network Health Monitor dashboard after checking google.com: ping shows 15.7 ms latency with no packet loss, the port scan shows 80 and 443 open and 22 closed, and the AI diagnostic panel summarizes the results and suggests confirming only the intended web services are listening.
Ping, scan and AI summary for a public host. Summaries come from Groq here.

$ cat features

  • Ping with latency and packet loss.
  • TCP connect scan of up to 20 chosen ports per host.
  • DNS lookups: A, AAAA, MX and NS for names, PTR for IPs.
  • Diagnostic summaries from any OpenAI-compatible API. Groq is the default, and a local Ollama works too if nothing should leave the machine.

$ cat history

The first version ran on a public web server in my homelab, with a model on my own GPU writing the summaries. It worked as a demo, but it had three problems. A public form that scans any target is a free tool for whoever finds it. My ISP puts me behind CGNAT, so everything went through a tunnel. And one GPU meant one request at a time, so a second visitor just waited.

So I dropped public hosting and the GPU setup. The web server VM is gone, and the app now talks to any OpenAI-compatible API. It's open source, and the intended way to run it is on your own machine or network.

$ cat security-model

RiskWhat stops it
Scanning internal or cloud-metadata addressesTargets are checked by resolved IP, not by name. Private, loopback, link-local, CGNAT and reserved ranges are blocked by default if any resolved address matches
DNS rebindingThe name is resolved again at scan time and the already-checked IP is the one that gets scanned
Scan data leaking to a third-party LLMEvery IP and hostname is swapped for a placeholder like IP_1 before the prompt is sent, then swapped back in the reply
Prompt stuffing through the APIDNS analysis takes only a target and re-runs the lookup on the server, so no client-supplied text reaches the prompt
Malformed or oversized inputPydantic validates every request body, and bodies over 1 KB are rejected
Command injection through pingPing runs as an argument list with no shell
One client hogging itPer-client rate limits: 10/min for scans, 5/min for AI analysis. Behind a proxy, only that proxy's forwarded address is trusted

There is no login, and the README says so plainly: anyone who can reach it can scan with it. That's why it stays on localhost, a LAN or a VPN.

$ cat deploy/homelab

My own copy runs on doof, my Proxmox server:

  • An unprivileged LXC container running Docker Compose: the API plus an nginx container for the frontend.
  • Nginx Proxy Manager in front with a TLS certificate. The Proxmox firewall lets only the proxy reach the app's port.
  • Reachable from my LAN and over VPN only. No tunnel route, nothing public.
  • Private targets are allowed on this instance, since scanning my own servers is the point.

One gotcha: ping failed inside the container at first. Unprivileged ICMP sockets depend on ping_group_range, and its default range didn't fit the container's mapped IDs. Narrowing it to the container's range fixed it.

$ cat engineering

  • 102 pytest tests covering target blocking, rebinding, redaction, rate limits and the LLM client.
  • One GitHub Actions workflow with three parallel jobs: the API (ruff, pip-audit, pytest), the frontend (lint, npm audit, build) and a Docker build.
  • Configured entirely through .env, with safe defaults when a setting is missing.
  • Security reports go through GitHub private vulnerability reporting.

$ cat known-limits

  • Hosts and results live in memory and are gone after a restart.
  • Ping output is parsed for Linux and macOS only.
  • No user accounts, so access control is whatever the network gives you.
lab up — · 8 notes ·