Skip to content

Implement bearer token controller logic and environment variable management#3487

Open
amirejaz wants to merge 8 commits intomainfrom
bearer-token-support-kubernetes
Open

Implement bearer token controller logic and environment variable management#3487
amirejaz wants to merge 8 commits intomainfrom
bearer-token-support-kubernetes

Conversation

@amirejaz
Copy link
Contributor

Summary

Implements the controller logic and runtime integration for bearer token authentication in Kubernetes, building on the foundation laid in PR #3224. This completes the bearer token authentication flow by adding reconciliation, RunConfig generation, environment variable management, and secret watching capabilities.

Context

This PR builds on PR #3224 which added:

  • BearerTokenConfig CRD type in MCPExternalAuthConfig
  • ExternalAuthTypeBearerToken enum value
  • Webhook validation for bearer token configuration
  • Basic type definitions and API structure

This PR implements the operational logic to make bearer tokens work end-to-end.

Changes

Controller Implementation

  • MCPExternalAuthConfig Controller:

    • Added reconciliation logic for bearerToken type in mcpexternalauthconfig_controller.go
    • Implements secret watch to trigger reconciliation when referenced secrets change
    • Calculates config hash including secret content (SHA256, truncated to 16 hex chars) for change detection
    • Refactored secret reference finding into dedicated functions:
      • findMCPExternalAuthConfigsReferencingSecret() - finds configs referencing a secret
      • configReferencesSecret() - checks if a config references a specific secret
  • MCPRemoteProxy Controller:

    • Integrated bearer token configuration into RunConfig generation (mcpremoteproxy_runconfig.go)
    • Converts Kubernetes Secret references to CLI format ("secret-name,target=bearer_token")
    • Validates secret existence and key presence before deployment
    • Generates environment variables for bearer token secrets

Environment Variable Management

  • GenerateBearerTokenEnvVar(): Creates TOOLHIVE_SECRET_{secret-name} env vars from Secret references
  • EnsureRequiredEnvVars(): Auto-detects TOOLHIVE_SECRET_* env vars and sets TOOLHIVE_SECRETS_PROVIDER=environment
    • Checks all env vars (not just specific ones) for TOOLHIVE_SECRET_* prefix
    • Only sets provider when actual secrets are present (excludes provider env var itself)
    • Ensures secrets provider is correctly configured for CLI-format secret resolution

Secret Resolution Flow

  1. User creates MCPExternalAuthConfig with bearerToken type referencing a Kubernetes Secret
  2. Controller validates secret exists and contains required key
  3. Controller generates TOOLHIVE_SECRET_{secret-name} env var in pod spec
  4. EnsureRequiredEnvVars detects secret env vars and sets TOOLHIVE_SECRETS_PROVIDER=environment
  5. RunConfig contains CLI-format secret reference: "secret-name,target=bearer_token"
  6. At runtime, EnvironmentProvider resolves secret from TOOLHIVE_SECRET_* env var
  7. Bearer token is injected into Authorization header for remote MCP server requests

Testing

  • Comprehensive unit tests for bearer token RunConfig generation (mcpremoteproxy_runconfig_test.go)
  • Tests for environment variable generation and validation
  • Tests for EnsureRequiredEnvVars with 13 test cases covering:
    • Default env var setting
    • Secret detection with different naming patterns
    • Edge cases (empty lists, mixed vars, provider exclusion, etc.)
  • Tests for secret watch and reconciliation triggers (mcpexternalauthconfig_controller_test.go)
  • Integration tests for end-to-end bearer token flow

Examples & Documentation

  • Added mcpremoteproxy_with_bearer_token.yaml example demonstrating bearer token configuration
  • Updated example files to use generic names (removed PostHog-specific references)
  • Cleaned up examples to focus on essential configuration

Security

  • Only secret references supported (no plaintext values)
  • Secrets validated before deployment
  • Secret changes trigger automatic reconciliation via watch mechanism
  • Config hash includes secret content for change detection (ensures dependent resources reconcile when secrets change)

Technical Details

Secret Watch Implementation

  • Controller watches Secrets and triggers reconciliation of MCPExternalAuthConfig resources that reference them
  • Uses Watches(&corev1.Secret{}, secretHandler) in SetupWithManager
  • Ensures changes to secret values propagate to dependent resources

Config Hash with Secret Content

  • calculateConfigHash() now includes SHA256 hash (truncated to 16 hex chars) of referenced secret values
  • Enables change detection when secrets are updated
  • Triggers reconciliation of dependent MCPServer and MCPRemoteProxy resources

Related

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Large PR Detected

This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.

How to unblock this PR:

Add a section to your PR description with the following format:

## Large PR Justification

[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformation

Alternative:

Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.

See our Contributing Guidelines for more details.


This review will be automatically dismissed once you add the justification section.

@github-actions github-actions bot added the size/XL Extra large PR: 1000+ lines changed label Jan 28, 2026
@codecov
Copy link

codecov bot commented Jan 28, 2026

Codecov Report

❌ Patch coverage is 83.13253% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.68%. Comparing base (c3549f6) to head (7c2845d).

Files with missing lines Patch % Lines
...d/thv-operator/pkg/controllerutil/tokenexchange.go 79.59% 5 Missing and 5 partials ⚠️
...-operator/controllers/mcpremoteproxy_deployment.go 66.66% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3487      +/-   ##
==========================================
- Coverage   65.71%   65.68%   -0.04%     
==========================================
  Files         410      410              
  Lines       40624    40704      +80     
==========================================
+ Hits        26697    26736      +39     
- Misses      11846    11886      +40     
- Partials     2081     2082       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@amirejaz amirejaz marked this pull request as draft January 28, 2026 17:16
@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Jan 28, 2026
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/XL Extra large PR: 1000+ lines changed labels Jan 28, 2026
@github-actions github-actions bot dismissed their stale review January 28, 2026 23:58

PR size has been reduced below the XL threshold. Thank you for splitting this up!

@github-actions
Copy link
Contributor

✅ PR size has been reduced below the XL threshold. The size review has been dismissed and this PR can now proceed with normal review. Thank you for splitting this up!

// +kubebuilder:rbac:groups=toolhive.stacklok.dev,resources=mcpexternalauthconfigs/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=toolhive.stacklok.dev,resources=mcpexternalauthconfigs/finalizers,verbs=update
// +kubebuilder:rbac:groups=toolhive.stacklok.dev,resources=mcpservers,verbs=get;list;watch;update;patch
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only way of doing this? I was explicitly not adding this capability because a compromise of the operator will grant folks the ability to read secrets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Removed it, this PR doesn't actually need secret access since it only hashes the spec, not secret values. We can revisit secret permissions when the other PR adds secret-aware hashing.

@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Jan 29, 2026
@amirejaz amirejaz force-pushed the bearer-token-support-kubernetes branch from f146eb7 to a47eb6f Compare January 29, 2026 12:30
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Jan 29, 2026
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Jan 30, 2026
@amirejaz amirejaz requested a review from JAORMX February 5, 2026 15:14
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large PR: 600-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants