MCP server has no authentication and is exposed publicly #30

Closed
opened 2026-08-23 05:42:03 +00:00 by nasandre · 3 comments
Owner

Critical

The MCP server (`mcp-server/server.py`) runs FastMCP with `host=0.0.0.0` (line 19) and no authentication of any kind. Caddy routes the public path `/mcp/*` to it (port 8874), so anyone on the internet can:

  • Read all reference tables from the DB (homeworlds, skills, equipment, factions, talents, psychic powers, weapon types) via resources
  • Call all tools without any credential

```python
app = FastMCP("wh40-rogue-trader-mcp", host="0.0.0.0", port=MCP_PORT)
...
app.run(transport="streamable-http") # no auth hook anywhere
```

Suggested fix

  • Add a shared-secret/token check (FastMCP supports auth hooks / middleware), forwarded by Caddy from the frontend's JWT, or
  • Remove the public Caddy route entirely if nothing external uses it, or
  • At minimum restrict to internal network + read-only DB role.
## Critical The MCP server (\`mcp-server/server.py\`) runs FastMCP with \`host=0.0.0.0\` (line 19) and **no authentication of any kind**. Caddy routes the public path \`/mcp/*\` to it (port 8874), so anyone on the internet can: - Read all reference tables from the DB (homeworlds, skills, equipment, factions, talents, psychic powers, weapon types) via resources - Call all tools without any credential \`\`\`python app = FastMCP("wh40-rogue-trader-mcp", host="0.0.0.0", port=MCP_PORT) ... app.run(transport="streamable-http") # no auth hook anywhere \`\`\` ## Suggested fix - Add a shared-secret/token check (FastMCP supports auth hooks / middleware), forwarded by Caddy from the frontend's JWT, or - Remove the public Caddy route entirely if nothing external uses it, or - At minimum restrict to internal network + read-only DB role.
Author
Owner

🔧 Work started — Plan:

  1. Add bearer-token auth to the FastMCP app (ASGI middleware wrapper reading MCP_AUTH_TOKEN env)
  2. Make the token mandatory in docker-compose.prod.yml (like JWT_SECRET)
  3. Verify with curl: no token → 401, valid token → resources/tools respond

Note: this will break any existing unauthenticated MCP clients — they'll need to send Authorization: Bearer <token> going forward.

🔧 **Work started** — Plan: 1. Add bearer-token auth to the FastMCP app (ASGI middleware wrapper reading `MCP_AUTH_TOKEN` env) 2. Make the token mandatory in `docker-compose.prod.yml` (like JWT_SECRET) 3. Verify with curl: no token → 401, valid token → resources/tools respond Note: this will break any existing unauthenticated MCP clients — they'll need to send `Authorization: Bearer <token>` going forward.
Author
Owner

🔧 Progress: ASGI bearer-token middleware added around FastMCP's streamable-http app; MCP_AUTH_TOKEN is now required by docker-compose (token generated into .env). uvicorn runs the wrapped app. Pending: build + live curl verification (401 without token / success with token).

🔧 Progress: ASGI bearer-token middleware added around FastMCP's streamable-http app; `MCP_AUTH_TOKEN` is now required by docker-compose (token generated into .env). uvicorn runs the wrapped app. Pending: build + live curl verification (401 without token / success with token).
Author
Owner

✅ Fixed & verified live (commit 05b5453):

  • ASGI bearer-token middleware wraps FastMCP's streamable-http app — every request requires Authorization: Bearer <MCP_AUTH_TOKEN>
  • Token mandatory in compose (MCP_AUTH_TOKEN:${MCP_AUTH_TOKEN:?}), generated into .env
  • Proxy prefix handling fixed: FastMCP now serves at /, middleware strips /mcp (Caddy forwards the prefix intact — this was also silently breaking the route)

Live verification:

  • POST /mcp/ without token → 401
  • with valid token → initialize + tools respond normally
  • Direct :8874 confirms same

Client config going forward: URL https://roguetrader.tabletopamsterdam.nl/mcp/ (trailing slash matters), header Authorization: Bearer <token from .env>.

Note for external MCP clients (Claude etc.): add the Authorization header to your MCP connector config.

✅ **Fixed & verified live** (commit `05b5453`): - ASGI bearer-token middleware wraps FastMCP's streamable-http app — every request requires `Authorization: Bearer <MCP_AUTH_TOKEN>` - Token mandatory in compose (`MCP_AUTH_TOKEN:${MCP_AUTH_TOKEN:?}`), generated into `.env` - Proxy prefix handling fixed: FastMCP now serves at `/`, middleware strips `/mcp` (Caddy forwards the prefix intact — this was also silently breaking the route) **Live verification:** - `POST /mcp/` without token → **401** - with valid token → initialize + tools respond normally - Direct :8874 confirms same **Client config going forward:** URL `https://roguetrader.tabletopamsterdam.nl/mcp/` (trailing slash matters), header `Authorization: Bearer <token from .env>`. Note for external MCP clients (Claude etc.): add the Authorization header to your MCP connector config.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nasandre/wh40-rogue-trader#30
No description provided.