llm-host-guard
Site: https://leinad07912-ux.github.io/llm-host-guard/ (what it is, install, Telegram setup)
Is my Ollama / LM Studio / vLLM box open to the world? One command tells you, in plain English, and fixes it if you say yes.
30-second start
pipx install llm-host-guard # or: pip install llm-host-guard
llm-host-guard
No pipx? git clone https://github.com/leinad07912-ux/llm-host-guard && cd llm-host-guard && python3 llm_host_guard.py — no dependencies, Python 3.9+.
You get a verdict line, a ranked list with a fix under every item, and the next command to run:
llm-host-guard v0.2.0 — neo (Linux, 192.168.50.156)
🔴 2 thing(s) to fix NOW, 2 this week score 2/10
CRITICAL Ollama on *:11434 reachable from LAN with no auth (7 models listed)
fix: OLLAMA_HOST=127.0.0.1 … or `ufw allow from <LAN>/24 to any port 11434`
CRITICAL container web publishes 0.0.0.0:8080 — bypasses host firewall
HIGH sshd PasswordAuthentication yes
HIGH 3 pickle-format weight file(s) — execute code on load
OK ufw active, default deny incoming
score: 2/10
next: preview the fixes → llm-host-guard --fix --dry-run
apply them → sudo llm-host-guard --fix
Then: sudo llm-host-guard --fix walks each fix, shows the exact command, asks y/N, prints the undo. llm-host-guard --open gives the same report as a page in your browser. sudo llm-host-guard --watch 30 --telegram keeps watching and messages you only when something new opens up.
Why
Ollama, LM Studio, vLLM, llama.cpp and friends ship with no authentication, often bind to 0.0.0.0, load model files that can execute code on open, and run on laptops with no perimeter. Tens of thousands of open Ollama servers are already on Shodan. Tooling exists for agent safety (agent-firewall, mcp-sentinel) and generic host hardening (lynis) — nothing covers the LLM-host gap specifically.
What it checks
| Check | Finds |
|---|---|
ports |
LLM servers (Ollama, LM Studio, vLLM, llama.cpp, Open WebUI, text-gen-webui, Jan, koboldcpp, LocalAI, Xinference, SGLang, GPT4All, TabbyAPI) bound non-loopback; probes them from your LAN IP to prove they answer without auth |
firewall |
ufw / firewalld / nftables / pf / Windows Firewall missing, disabled, not default-deny, or allowing LLM ports from Anywhere |
docker |
Containers publishing on 0.0.0.0 — these bypass ufw |
exposure |
Public IP on an interface; cloudflared / ngrok / frp / bore tunnels running |
models |
Pickle weights (.pt .bin .ckpt) = RCE on load; malformed GGUF / safetensors headers (parser-CVE bait); world-writable model dirs |
versions |
Ollama / vLLM / llama.cpp / Open WebUI against a bundled CVE table |
config |
OLLAMA_ORIGINS=* (any website can drive your LLM from your browser), OLLAMA_HOST=0.0.0.0, vLLM / llama-server without --api-key, Open WebUI signup, sshd password auth |
agents |
Inventory of AI agents installed or running here (Claude Code, Cursor, Codex, Gemini CLI, Windsurf, Aider, Open Interpreter, OpenClaw…), what each can do, and its MCP servers. Reads config paths only. |
permissions |
Permission audit of agent/MCP configs: file tools rooted at your home or a whole drive, approval prompts switched off, wildcard shell rules, secrets saved in plain text (names only, never values), unpinned npx/uvx tools, remote http:// servers. |
agent_risk |
Idle agents (running but unused for --idle-days, default 3; 1 day for agents that control the screen/mouse; open model servers nobody uses) and prompt-injection exposure: an agent that reads web/email content and can run commands or change files. This shows where an injection would hurt most. It does not detect injections. |
runtime |
Behaviour of the running server: an LLM process that spawned a shell/downloader/unexpected child (the shape of a poisoned-model exploit) → CRITICAL; server or its children connected to a public IP (fine during a pull, otherwise not) → HIGH. /proc on Linux, ps+lsof on macOS, no root. --checks runtime --watch 0.1 for a 6-second loop. |
internet (opt-in --internet) |
What the outside already sees: your public IP's open ports + CVEs via Shodan InternetDB (free, keyless), and router UPnP port-forwards to LLM ports. The only check that makes outbound calls. |
Each finding carries the exact fix.
What the levels mean
| Level | Plain English |
|---|---|
| CRITICAL | Wide open right now. Anyone on your network (or the internet) can use or damage your LLM today. Fix first. |
| HIGH | A real hole, but an attacker needs one more thing — be on your WiFi, or get you to open a file. Fix this week. |
| MED | Exposed on purpose but wider than it needs to be. Tighten when convenient. |
| LOW | Looks exposed but something else already blocks it. Just be aware. |
| INFO | Not a problem, just something you should know exists. |
| OK | Checked, nothing wrong. |
Score starts at 10; each CRITICAL takes 3, HIGH 2, MED 1. Run with sudo so firewall rules that already protect a port get credited (a LAN-scoped Ollama drops from CRITICAL to MED, a DOCKER-USER-blocked container to LOW).
Usage
llm-host-guard # terminal report, exit 2=critical 1=high/med 0=clean
llm-host-guard --json # machine-readable
llm-host-guard --html report.html # self-contained page; --open writes it and opens your browser
llm-host-guard --watch 15 --webhook https://… # rerun every 15 min, alert on new findings
llm-host-guard --watch 30 --telegram # same, to Telegram (token/chat via env, see below)
llm-host-guard --watch 1 --once --telegram # single diff pass, for cron
llm-host-guard --internet # + Shodan InternetDB + router UPnP (outbound calls)
llm-host-guard --checks ports,docker --model-dir /data/models
sudo llm-host-guard # optional: reads ufw rules + effective sshd config
Run --watch 30 under systemd / launchd for continuous monitoring; --html in watch mode keeps the report fresh.
Buttons. When watch mode runs as root (sudo llm-host-guard --watch 30 --telegram), every fixable alert arrives with 🔒 Close it / 🔒 Close for 1h / ⏰ Remind me tomorrow / ✓ Leave it; after closing, ↩ Undo (reopen). Close runs exactly the recipe --fix would and replies with the result. Close for 1h runs the same recipe and undoes it an hour later (the reopen is done by the watch service, so it needs to stay running). Remind me tomorrow hides the finding for 24h, then it alerts once more. Alerts with no recipe get just Remind / Leave it. Only your chat id can press. Use a bot dedicated to llm-host-guard — Telegram allows one long-polling program per bot token.
Telegram: create a bot with @BotFather, DM it once, get your chat id from https://api.telegram.org/bot<TOKEN>/getUpdates, then export LLM_HOST_GUARD_TELEGRAM_BOT_TOKEN and LLM_HOST_GUARD_TELEGRAM_CHAT_ID (a systemd EnvironmentFile= keeps them off the command line). examples/llm-host-guard-watch.service is a ready user unit.
Many machines? (fleet)
llm-host-guard --report-to https://fleet.yourcompany.example --enrol-key lhg_… # or env LLM_HOST_GUARD_FLEET_URL / _KEY
Every run also posts its JSON to a llm-host-guard-fleet collector: one table for all hosts, history, alerts when any host goes red or silent. Best-effort — if the collector is down the local report and exit code are unchanged. Refuses plain http:// to non-local hosts.
--fix (v2)
llm-host-guard --fix --dry-run # preview every recipe, no root, runs nothing
sudo llm-host-guard --fix # prints each command, asks y/N per finding
sudo llm-host-guard --fix --yes # unattended
Recipes exist for: open LLM port → scoped ufw rule; ufw "Anywhere" rule on an LLM port → re-scoped; OLLAMA_HOST=0.0.0.0 under systemd → loopback drop-in; container on 0.0.0.0 → DOCKER-USER drop; sshd password auth → sshd_config.d/00-… drop-in (only if you already have an SSH key installed — it will not lock you out).
Every recipe only adds a rule or a drop-in file, never edits an existing file, and prints its undo command. Findings without a safe automatic recipe say why.
What it does not do
- Edit config files in place (
--fixonly adds drop-ins/rules) - Replace a firewall or IDS — it tells you when you're missing one
- Detect prompt injection or scan MCP servers — see agent-firewall / mcp-sentinel
- Kill anything:
runtimedetects and tells you the pid; agent-firewall's eBPF guard is the kill layer
Platforms
Linux and macOS first-class. Windows best-effort (netstat -ano, netsh advfirewall; use py -m llm_host_guard if llm-host-guard isn't on PATH). Python ≥ 3.9. No dependencies.
Running from a clone without installing: python3 llm_host_guard.py or python3 -m llm_host_guard — identical.
Contributing
python3 -m unittest — tests use fixtures, no live system needed.
New server signatures → data/signatures.json. New CVEs → data/cves.json (include vendor advisory link in PR).
MIT.
Metadata
Release files for llm-host-guard 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| llm_host_guard-0.5.0.tar.gz | 67.6 kB | Without provenance | Details for llm_host_guard-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| llm_host_guard-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for llm_host_guard-0.5.0-py3-none-any.whl |
Total release size: 126.8 kB
Release files / llm_host_guard-0.5.0.tar.gz
| Download URL | llm_host_guard-0.5.0.tar.gz |
|---|---|
| Size | 67.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
debf77b4b06c5d190ea68059b2cbd899c93f655333d97e626e673366f3bf66e1
|
|
BLAKE2b-256 checksum How to use checksums |
896c088342c7146b7b4a8b831be43c9658cc92869c7913d31250aba08daa94a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / llm_host_guard-0.5.0-py3-none-any.whl
| Download URL | llm_host_guard-0.5.0-py3-none-any.whl |
|---|---|
| Size | 59.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bf6e18e78657b6900b98914d61d56506103e9837c95ff6cbdb3fe0c7ff4ffda9
|
|
BLAKE2b-256 checksum How to use checksums |
c37d85f6159fa2318eebe3636fb7904f09357ad6380f2be6caae8f2aa19b8543
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for llm-host-guard 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.