Skip to content

Scope pods/attach Role rule to stdio transport only #6230

Description

@lucianstancu

Summary

The per-MCP Role created by the operator (-proxy-runner) unconditionally includes pods/attach (create, get) regardless of the transport type configured on the MCPServer. For streamable-http and sse transports, the proxy runner communicates over HTTP and never needs to attach to the MCP pod's stdio — so this permission is granted but never used.

Current behavior

In cmd/thv-operator/controllers/mcpserver_controller.go, defaultRBACRules is applied to every MCPServer regardless of transport:

{APIGroups: []string{""}, Resources: []string{"pods/attach"}, Verbs: []string{"create", "get"}},

This means every MCP running streamable-http or sse transport gets a Role with pods/attach that it has no functional need for.

Expected behavior

The pods/attach rule should only be included in the Role when spec.transport is stdio. For streamable-http and sse, the rule should be omitted entirely.

Why it matters

Security auditing tools (e.g. Polaris) flag pods/attach as an elevated permission equivalent to pod exec. Clusters with strict audit policies will surface this as a violation on every MCPServer deployment, even when the permission is architecturally unnecessary for the configured transport.

Suggested fix

Gate the rule on transport type in ensureRBACResources (or wherever defaultRBACRules is applied):

rules := defaultRBACRules
if mcpServer.Spec.Transport == transportStdio {
rules = append(rules, podAttachRule)
}

Environment

  • ToolHive operator version: v0.33.0
  • Transport in use: streamable-http
  • Audit tool flagging this: Polaris

Metadata

Metadata

Assignees

No one assigned

    Labels

    kubernetesItems related to Kubernetesneeds-triageIssue needs initial triage by a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions