Skip to content

[security] Restrict tokenless dev mode to loopback by default #406

Description

@danyalahmed1995

Summary

When CMCP_DEV_MODE=1 is enabled and CMCP_BEARER_TOKEN is unset, cMCP intentionally starts without bearer authentication. However, the default listen_addr is 0.0.0.0:8443, so the tokenless development gateway listens on every network interface unless the user explicitly overrides it.

The official quickstart currently follows this exact path and shows Uvicorn listening on 0.0.0.0:8443.

This is a configuration footgun rather than an authentication bypass in production: production startup correctly requires a bearer token. The unsafe combination is specifically tokenless dev mode + a non-loopback bind.

Current behavior

  • src/cmcp_runtime/config.py defaults listen_addr to 0.0.0.0:8443.
  • src/cmcp_runtime/startup.py permits a missing bearer token when config.dev_mode is true.
  • src/cmcp_runtime/mcp/server.py omits the bearer-auth middleware when no token is configured.
  • src/cmcp_runtime/cli.py binds Uvicorn directly to the configured host and port.
  • docs/quickstart.md omits listen_addr, starts with CMCP_DEV_MODE=1, and shows the runtime listening on 0.0.0.0:8443.

As a result, another client that can reach the host may access unauthenticated routes such as /mcp, /tools/list, /audit/export, and session endpoints. Tool calls still go through cMCP policy evaluation, but the network trust boundary is wider than a local-development user is likely to expect.

Reproduction

  1. Follow docs/quickstart.md and use its sample cmcp-config.yaml without a listen_addr.

  2. Ensure CMCP_BEARER_TOKEN is unset.

  3. Start the runtime:

    CMCP_DEV_MODE=1 cmcp start --config cmcp-config.yaml
  4. Observe:

    cMCP Runtime starting: TEE: software-only, listen: 0.0.0.0:8443
    INFO: Uvicorn running on http://0.0.0.0:8443
    
  5. Subject to routing and host-firewall rules, connect from another machine or container using the host's reachable IP without an Authorization header.

Suggested behavior

When no bearer token is configured, restrict the runtime to loopback addresses by default and fail closed for wildcard, private-network, or public-network binds.

Possible behavior:

  • Allow tokenless dev mode on loopback addresses such as 127.0.0.1 and ::1.
  • Reject tokenless dev mode on 0.0.0.0, ::, LAN addresses, and other non-loopback addresses.
  • Continue allowing non-loopback binds when CMCP_BEARER_TOKEN is configured.
  • If remote unauthenticated development is a required use case, gate it behind an explicit opt-in such as CMCP_ALLOW_INSECURE_REMOTE_DEV=1 and emit a prominent warning.
  • Update the quickstart to bind to loopback.

Changing the default to 127.0.0.1:8443 would improve the common case, but startup validation would still be useful to prevent an explicit non-loopback address from silently recreating the same condition.

Acceptance criteria

  • Tokenless dev mode starts successfully on IPv4 and IPv6 loopback addresses.
  • Tokenless dev mode rejects wildcard and non-loopback bind addresses.
  • A configured bearer token permits an explicitly configured non-loopback bind.
  • Tests cover at least 127.0.0.1, ::1, 0.0.0.0, ::, and one private-network address.
  • Quickstart and configuration documentation describe the network-safety behavior.

Related

This is a residual configuration-hardening gap after #138 (AUTH-001). That issue correctly added bearer authentication and production fail-closed startup; this issue concerns the remaining dev-mode bind default.

Metadata

Metadata

Assignees

No one assigned

    Labels

    fellowshipAgenTrust Fellowship-friendly workgood first issueGood for newcomersneeds-review:HIGHContributor check flagged HIGH risk

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions