Skip to content

no-exposed-unauthenticated-service

Publishing Ollama, Qdrant, or the Docker MCP gateway on all host interfaces without authentication lets anyone on the network use it. How to bind to loopback instead.

On this page

A ports entry such as 11434:11434 publishes the container on every network interface of the host. For most services that is a deliberate choice. For some AI services it opens a door with no lock, because the service has no authentication to fall back on. This rule reports a service that runs one of these images, publishes a port on all interfaces, and sets nothing in Compose that turns authentication on.

  • Ollama has no server authentication, and its official image sets OLLAMA_HOST=0.0.0.0:11434, so a published port is reachable from the whole network.
  • Qdrant listens on 0.0.0.0 by default, and its api_key is commented out. The rule treats QDRANT__SERVICE__API_KEY in environment as authentication, and it stays silent when the service has an env_file or mounts a file under /qdrant/config, because the key can come from there.
  • The Docker MCP gateway creates a bearer token by default. The rule reports it only when command contains --allow-unauthenticated.

The rule reads ports in the short and the long syntax. It reports an entry with no host address and an entry bound to 0.0.0.0 or [::]. A ${BIND:-0.0.0.0} address resolves to its default. A ${BIND} address with no default is unknown, so the rule does not report it. network_mode: host counts as published. expose does not, because it never opens a port on the host. The rule matches the image name and skips a service with build:, since its image: value is only a tag.

What the rule catches

compose.yaml — Ollama on every host interface
services:
  ollama:
    image: ollama/ollama:0.12.3
    ports:
      - "11434:11434"

Scanning this file reports:

⚠ WARN [docker-doctor/no-exposed-unauthenticated-service]
  Service 'ollama' publishes Ollama on all interfaces, and Ollama has no authentication here. Anyone who can reach this host can use it.

Why it matters

These services hand over real capability and real data. Anyone who reaches Ollama can run prompts on your hardware, pull models, and delete the ones you installed. Anyone who reaches Qdrant can read and overwrite every collection, which in an agent stack is its memory. Anyone who reaches the MCP gateway can call every tool behind it, including tools that act with your stored secrets and OAuth tokens. CVE-2026-76092 describes exactly that for a gateway in a container, and its workaround is to bind the port to loopback or a trusted network. Loopback is not the whole answer for the gateway. CVE-2025-64443 is a DNS rebinding flaw that lets a web page in your browser reach a gateway running in SSE or streaming mode, which the gateway fixed in v0.28.0. Keep the port off the network first, and keep the gateway current.

How to fix it

Bind the port to loopback (127.0.0.1:11434:11434), or drop ports and reach the service over an internal network, or put an authenticating proxy in front of it.

compose.yaml — loopback bind and an internal network
services:
  ollama:
    image: ollama/ollama:0.12.3
    ports:
      - 127.0.0.1:11434:11434
  qdrant:
    image: qdrant/qdrant:v1.15.0
    networks:
      - backend
  app:
    image: my-app:1.2.0
    networks:
      - backend

networks:
  backend:
    internal: true

Pick the fix that matches how the service is used:

  • Bind the port to loopback. 127.0.0.1:11434:11434 keeps the service reachable from the host and closes it to the network.
  • Drop ports and use an internal network. Services on the same Compose network reach each other by name, and internal: true stops the network from routing outside.
  • Turn authentication on. Set QDRANT__SERVICE__API_KEY for Qdrant, and leave --allow-unauthenticated off for the gateway.
  • Put an authenticating proxy in front. Ollama has no authentication of its own, so a reverse proxy that checks a key is the way to share it.

The list holds only products whose default bind and authentication were confirmed in the product's own source or documentation. A missing product does not mean it is safe, only that the rule has not verified it.

Docker publishes ports through its own firewall rules, which run before host firewalls such as ufw. A ufw deny rule does not close a published port, so do not treat the host firewall as the fix.

The rule reads the environment and command of the service. It does not read an env_file or a mounted Qdrant config file, so it treats authentication as unknown for those services and reports nothing. The rule does not yet cover Chroma, Weaviate or vLLM, because their default bind and authentication are unconfirmed. Gateway releases from 0.25.0 up to 0.43.0 served no authentication in container mode at all, whatever the flags said, so pin 0.43.1 or later.

A monitoring or lab host that is meant to serve its whole network can set this rule to off in docker-doctor.config.json.

Rule details

  • Rule key — docker-doctor/no-exposed-unauthenticated-service
  • Category — Compose
  • Default severity — warning
  • Applies to — Docker Compose files

Explain this rule from the CLI:

npx @docker-doctor/cli@latest rules explain docker-doctor/no-exposed-unauthenticated-service

Change its severity — or turn it off — in your config file:

// docker-doctor.config.ts
export default {
  rules: {
    "docker-doctor/no-exposed-unauthenticated-service": "off",
  },
};

Severity affects the health score: error findings cost more points than warning, and info costs the least.