Skip to content

fix: decouple operator from internal secrets providers #721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 16, 2025

Conversation

danbarr
Copy link
Collaborator

@danbarr danbarr commented Jun 12, 2025

Problem Solved

The original implementation was passing secrets as CLI arguments (--secret) to the thv command, which was non-functional because the "none" secrets provider does not allow it:

Error: failed to get secrets: secret not found: github-token/token (none provider doesn't store secrets)

Resolves #521

Solution Implemented

Replaced the CLI argument approach with secure Kubernetes-native secret injection:

Key Changes Made:

  1. Modified deploymentForMCPServer function in mcpserver_controller.go:

    • Removed CLI --secret arguments
    • Added generation of podTemplateSpec patches containing secret environment variables
    • Implemented merging of operator-generated patches with user-provided podTemplateSpecs
  2. Added generateSecretsPodTemplatePatch function:

    • Creates environment variables for the mcp container using Kubernetes Secret references
    • Uses secure ValueFrom.SecretKeyRef instead of plain text values
  3. Added mergePodTemplateSpecs function:

    • Safely merges operator-generated secret patches with user customizations
    • Preserves all user-defined pod template specifications
  4. Updated deploymentNeedsUpdate function:

    • Now checks for changes in pod template patches instead of CLI arguments
  5. Fixed test file mcpserver_pod_template_test.go:

    • Updated TestDeploymentForMCPServerWithSecrets to validate the new approach
    • Added comprehensive assertions for secret environment variable injection
  6. Updated example mcpserver_github.yaml:

    • Reverted the direct podTemplateSpec workaround back to the (corrected) secrets CRD implementation

Security Benefits

  • Kubernetes-native: Uses standard Kubernetes Secret references
  • Container-level injection: Secrets go directly into the MCP container where needed
  • Audit trail: All secret access is logged through Kubernetes RBAC

Functionality Benefits

  • Actually works: Secrets are now accessible to MCP servers in Kubernetes
  • Backward compatible: Existing podTemplateSpec usage continues to work
  • User-friendly: Simple secrets field in MCPServer CRD remains unchanged

Testing

  • ✅ All existing tests pass
  • ✅ New test validates secret injection via pod template patches
  • ✅ Code compiles successfully
  • ✅ Comprehensive test coverage for both secret injection and podTemplateSpec merging

Results

Token is securely handled in resources:

$ kubectl -n demo-namespace describe mcpserver github
Kind: MCPServer
<snip>
Spec:
  <snip>
  Secrets:
    Key:              token
    Name:             github-token
    Target Env Name:  GITHUB_PERSONAL_ACCESS_TOKEN


$ kubectl -n demo-namespace describe deployment github
<snip>
Pod Template:
  Containers:
   toolhive:
    Args:
    <snip>
      --k8s-pod-patch={"metadata":{"creationTimestamp":null},"spec":{"containers":[{"name":"mcp","env":[{"name":"GITHUB_PERSONAL_ACCESS_TOKEN","valueFrom":{"secretKeyRef":{"name":"github-token","key":"token"}}}],"resources":{}}]}}


$ kubectl -n demo-namespace describe statefulset github | grep "Environment" -A 2
    Environment:
      GITHUB_PERSONAL_ACCESS_TOKEN:  <set to the key 'token' in secret 'github-token'>  Optional: false
      MCP_TRANSPORT:                 stdio

Plaintext value is not found:

$ kubectl -n demo-namespace describe pod,statefulset,deployment,replicaset,service,mcpserver | grep "gho_" || echo "Not found"
Not found

Future considerations

At some point we might want a full kubernetes secrets provider in thv, but the end result (adding valueFrom.secretKeyRef env's) would likely be the same.

The new "none" secrets provider doesn't allow us to use the `--secrets`
CLI param. This alternate approach adds the secrets to the
`podTemplateSpec` using `env[].valueFrom.secretKeyRef` so they're not
exposed.
@danbarr danbarr requested a review from ChrisJBurns June 12, 2025 19:43
@danbarr danbarr changed the title fix: resolve error with operator's secrets implementation fix: decouple operator from internal secrets providers Jun 13, 2025
@danbarr danbarr merged commit e8188a6 into main Jun 16, 2025
17 checks passed
@danbarr danbarr deleted the fix/k8s-secrets-handling branch June 16, 2025 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Kubernetes operator] GitHub example not working
2 participants