$ AI agent governance, security tooling, and mechanical enforcement.

Your AI services are listening on 0.0.0.0

rigscore, security, ai, network, ollama, mcp

Most local AI tools split between two defaults: loopback-only (safe but breaks remote access) and all-interfaces (works everywhere, reachable from anywhere). The split matters less than this: most developers don’t choose. They accept whatever the tool shipped with, or they copy a Docker snippet from a tutorial that maps 11434:11434 with no bind prefix, and the result is the same either way. An LLM inference server, or an MCP SSE endpoint, ends up reachable on whatever interface the host happens to expose.

rigscore’s network-exposure check exists because that exposure pattern is common enough to warrant a dedicated detector. It’s also advisory rather than scored, and the reason for that is part of the story.

The problem: 0.0.0.0 vs 127.0.0.1

Binding to 0.0.0.0 means the service accepts connections from any network interface the host has. Binding to 127.0.0.1 means localhost only โ€” not reachable from the network at all. On a laptop with Wi-Fi, 0.0.0.0 means the coffee shop next to you. On a cloud VM without firewall rules, it means the internet. Inside a container orchestrator, it usually means the host’s bridge interface โ€” which depending on your network config may or may not be what you wanted.

None of this is new. What’s new is that AI services are now something worth reaching.

Which AI tools default where

Verified against rigscore’s AI_SERVICE_PORTS map in src/constants.js:

ToolDefault portDefault bindNotes
Ollama11434127.0.0.1OLLAMA_HOST=0.0.0.0 is commonly set for Docker or remote access and is the single most common finding.
LM Studio1234 (1235 alt)Server toggleOff by default; once enabled, bind address depends on the build.
Open WebUI8080Container bindAlmost always run in Docker; the compose file’s port mapping is the real bind.
MCP SSE servers3001 (heuristic 3000โ€“3999)VariesMost ship loopback; a non-loopback bind is rarely intentional.
LiteLLM4000VariesProxy deployments legitimately bind to all interfaces; flag for review.
LocalAI50010.0.0.0Drop-in OpenAI-compatible server; ships broad by default.
vLLM90900.0.0.0Inference server intended for deployment; expects a firewall.
FastChat80000.0.0.0Research-oriented; binds broadly.

The takeaway from the table is not “these defaults are wrong.” It’s that the defaults vary, they are under-documented, and nobody audits them twice.

Four detection surfaces

rigscore reads four sources and reconciles them:

  1. MCP client config URL parsing. .mcp.json, .vscode/mcp.json, and six client configs under $HOME are scanned for SSE or streamable-HTTP URLs that target non-loopback hosts. This is the one surface that raises CRITICAL โ€” a non-loopback MCP endpoint is almost never intentional.
  2. Docker compose port mapping. docker-compose.yml and related files are parsed for AI-service ports declared without a 127.0.0.1: prefix. "11434:11434" becomes a finding; "127.0.0.1:11434:11434" does not.
  3. Ollama config files. Systemd drop-ins and .env-style config that set OLLAMA_HOST=0.0.0.0.
  4. Live listener scan. ss on Linux or lsof on macOS, filtered to known AI ports plus the MCP SSE heuristic range 3000โ€“3999. This is the one surface that catches “whatever the running process is actually doing” regardless of what the config files claim.

The four-layer approach exists because any single surface misses. Config-only misses runtime overrides; runtime-only misses services that are down when you scan. The deploy-gating audit script was updated to verify these controls directly โ€” checking container configurations and network bindings rather than package installation status. Same logic applies here.

Attack scenarios

  • LAN exposure. Another device on the same network queries your local LLM. No auth, full inference access.
  • Public Wi-Fi. Coffee shop attacker scans the subnet and finds your Ollama instance on 11434.
  • Cloud VM. AI service on a VPS without firewall rules is internet-exposed. This one generates abuse reports fast.
  • Lateral movement. A compromised container reaches AI services on the host bridge that were assumed private.

The MCP SSE case is the sharpest. An SSE endpoint on 0.0.0.0 lets any process on any reachable interface impersonate a legitimate agent client โ€” that maps to OWASP Agentic Top 10 ASI07 (Insecure Inter-Agent Communication). If the agent is authorized to run tools, the attacker is too.

Sample output

1
npx github:Back-Road-Creative/rigscore --check network-exposure
โ“˜ network-exposure โ€” advisory
  CRITICAL MCP server "local-rag" SSE endpoint on non-loopback host
    Server "local-rag" in .mcp.json targets 192.168.1.20:3001.
    Non-loopback MCP endpoints are reachable from the network.
  WARNING Docker port 11434 (Ollama) exposed without loopback bind
    Container "ollama" in docker-compose.yml maps port 11434 without
    explicit 127.0.0.1 bind. It will listen on all interfaces.
    Fix: change "11434:11434" to "127.0.0.1:11434:11434".
  WARNING Live listener on 0.0.0.0:11434 (Ollama)
    Process is currently bound to all interfaces.

Per-tool fix instructions

  • Ollama. OLLAMA_HOST=127.0.0.1, or remove the override entirely.
  • Docker ports. Change "11434:11434" to "127.0.0.1:11434:11434". Same pattern for every AI port.
  • MCP SSE servers. Bind the server itself to 127.0.0.1 in its own config. Fix the URL in the client config to match.
  • General. If remote access is actually intentional, put a reverse proxy with auth in front. The problem isn’t the bind โ€” it’s the bind without a gate.

Why advisory, not scored

The check is weight 0. It does not affect your rigscore number. That is a deliberate design decision, not a TODO.

Local development legitimately binds 0.0.0.0 all the time. Docker bridge networking requires it. WSL2 VM networking often requires it. Codespaces and devcontainer setups default to it. Team members on a shared LAN who want to hit each other’s Ollama instance for ad-hoc testing configure it. The false-positive rate on “is this bind intentional?” is too high to score without penalizing the majority of legitimate workflows.

Scoring it would do one of two bad things: penalize normal setups (eroding trust in the overall score) or force every user to maintain an allowlist (governance overhead without clear upside). Advisory is the honest middle: the check runs, the findings surface, the fix instructions are there, and you decide whether the exposure is intentional. Documentation reflects the actual security posture. When intent matters more than the signal, the right move is to report clearly and refuse to grade.

The one exception is the MCP SSE CRITICAL row. That specific finding is rarely intentional โ€” it emits at CRITICAL level inside the advisory check so it’s visible, but it still doesn’t feed the score. If you want a single line that tells you whether your AI services are exposed, this is the check. If you want it to change your number, it won’t. That’s the point.

More on the rigscore docs.

Configuration details reflect a production environment at time of writing. Implementation specifics vary based on tooling versions, platform updates, and organizational requirements. Validate approaches against current documentation before deployment.