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
-
Follow docs/quickstart.md and use its sample cmcp-config.yaml without a listen_addr.
-
Ensure CMCP_BEARER_TOKEN is unset.
-
Start the runtime:
CMCP_DEV_MODE=1 cmcp start --config cmcp-config.yaml
-
Observe:
cMCP Runtime starting: TEE: software-only, listen: 0.0.0.0:8443
INFO: Uvicorn running on http://0.0.0.0:8443
-
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.
Summary
When
CMCP_DEV_MODE=1is enabled andCMCP_BEARER_TOKENis unset, cMCP intentionally starts without bearer authentication. However, the defaultlisten_addris0.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.pydefaultslisten_addrto0.0.0.0:8443.src/cmcp_runtime/startup.pypermits a missing bearer token whenconfig.dev_modeis true.src/cmcp_runtime/mcp/server.pyomits the bearer-auth middleware when no token is configured.src/cmcp_runtime/cli.pybinds Uvicorn directly to the configured host and port.docs/quickstart.mdomitslisten_addr, starts withCMCP_DEV_MODE=1, and shows the runtime listening on0.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
Follow
docs/quickstart.mdand use its samplecmcp-config.yamlwithout alisten_addr.Ensure
CMCP_BEARER_TOKENis unset.Start the runtime:
Observe:
Subject to routing and host-firewall rules, connect from another machine or container using the host's reachable IP without an
Authorizationheader.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:
127.0.0.1and::1.0.0.0.0,::, LAN addresses, and other non-loopback addresses.CMCP_BEARER_TOKENis configured.CMCP_ALLOW_INSECURE_REMOTE_DEV=1and emit a prominent warning.Changing the default to
127.0.0.1:8443would 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
127.0.0.1,::1,0.0.0.0,::, and one private-network address.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.