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
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