Skip to content

Security model for administrators

The AI client is untrusted. The gateway decides every call from the user’s token and the approved policies, and nothing in the client configuration or in the filter of the /mcp address widens access: the filter only narrows the tool list.

Token Issued by Accepted by Audience
User token of an AI client Identity provider Gateway mcp-gateway. The client id (azp) must be in MCP_ALLOWED_CLIENTS
Administration token Identity provider, exchanged by the portal Gateway admin API mcp-gateway-admin, with the client mcp-portal-bff
Administration token Identity provider, exchanged by the portal Credential broker mcp-credential-broker
Internal token for a server Gateway MCP server The identifier of that server (mcp-<system>)

The token of the identity provider never reaches a target system. The gateway replaces it with a credential from the broker, and builds the headers towards the server from scratch.

The gateway signs a new token for every call to a server: ES256, valid for 30 seconds, with the purpose use (call or policy-data). A server refuses a token with another audience or purpose. Servers fetch the public keys from https://<platform>/.well-known/mcp-jwks.json.

The administration pages check the group mcp-platform-admin in the portal, and the gateway and the broker check it again on the exchanged token. The admin API of the gateway listens on the internal port 8443 only and also needs the client certificate of the portal. The broker accepts calls only on port 8443 with a client certificate from the internal CA (the gateway, the portal and the offboarding job).

There is no mutual TLS between the gateway and a server, no client certificate and no internal CA on that leg. The trust is the one you approved for the connection (see Add and approve an MCP server):

  • Trusted CA: the certificate chains to the trust store of the gateway (plus MCP_SERVER_TRUSTED_CAS) and names the host.
  • Pinned certificate: exactly the certificate whose SHA-256 fingerprint you approved, with no chain and no host check. Any other certificate is refused as Certificate changed until you approve it.
  • Plain HTTP: no encryption. Only for a server in the same Docker network as the gateway.

The gateway refuses addresses that resolve to loopback, link-local, unspecified, multicast and cloud metadata addresses before every connection. The manifest of a server is stored only when it comes from the address you gave. A server cannot claim a system and approve itself: the connection, the policies and every tool definition wait for you, and a tool definition is pinned by hash. A different definition hides the tool until you approve it.

  • User credentials (tokens, OAuth grants) are encrypted with an OpenBao Transit key and tied to the user and the system. No endpoint returns a secret to a browser.
  • OAuth client secrets are write only in the portal. They are stored in OpenBao KV at secret/mcp/system-clients/<system>.
  • Service account keys are written by an operator to secret/mcp/service-accounts/<system>. The broker has read access only (see Add and manage systems).
  • The one exception to “never shown”: the seed of the Authenticator appears once, at registration, to its owner after a fresh MFA.
  • Secret values, tokens and confirmation codes are not written to logs, to the audit or to error messages. See Audit.

Anyone who can read the OpenBao volumes and its unseal key can read the secrets. Treat the host as the trust boundary, and see Backup for what to protect.

These operations need a login with a second factor within the last 5 minutes (auth_time and the evidence acr or amr). If yours is older, the portal sends you to the identity provider and brings you back to the form:

  • add, change (except disabling) and remove a server, and approve its connection;
  • approve policies, and approve a risk lowering;
  • save the scope mapping;
  • save or remove a system;
  • replace or remove an override, and lift an emergency switch;
  • register or remove your Authenticator.

Switching a server or tool off, adding an override and Refresh need none. The gateway and the broker check auth_time and the MFA evidence again on the exchanged token, so the identity provider must copy those claims into the exchanged tokens (see Configuration). A step-up never offers to enroll an OTP: a user without one is refused, so a password alone never reaches the level of an administrator.

The platform fails closed. It never calls a server without a credential or without the checks.

Failure Result
Broker, OpenBao, PostgreSQL, Redis or the identity provider keys unreachable 503 upstream_unavailable, no secret is handed out
Service account key missing, malformed or expired The call is refused
Policy data missing or stale The call is refused
Gateway cannot read the emergency switches Every call is refused, the catalog answers 503
Certificate of a pinned server changed Calls are refused until you approve it
Unknown tool, or a tool the user may not use The same answer for both, not_found_or_denied

Descriptions, schemas and hints come from servers and systems, so they are untrusted text. The portal writes hidden characters (direction marks, zero-width characters, control characters) as [U+XXXX] markers, shows hints as data and never as instructions, and refuses a definition whose hash does not match what it shows.

The published test keys of the contracts (test-*, dev-*) are refused unless the development profile and an explicit acknowledgement are both set. Never set them in production. The gateway logs event=devstack_profile at WARN when the profile is on.

Do not open a public issue. Use the private vulnerability reporting of the repository of the component (the Security tab, Report a vulnerability) and include the version or image tag and the steps to reproduce. Do not attach real secrets or tokens.