Repository navigation
Conversation
The auth server's TLS listener (fixed port 8443) could only be set up by hand-writing a RunConfig, so operator-managed workloads had no way to serve SPIFFE X.509 clients. This adds the operator side. - A tlsListener field on the embedded auth server config names the certificate and private key Secrets (both required). They are projected as one read-only directory at /etc/toolhive/authserver/tls, so kubelet-driven Secret rotation reaches the pod (subPath mounts are never updated after the first mount). BuildAuthServerRunConfig points the runtime config at the projected files. - A SPIFFE X.509 client association without a listener is rejected at reconcile time (the operator has no admission webhook) and again by RunConfig.Validate, so CLI and standalone vMCP users get a startup error instead of clients that fail to authenticate with no signal. A listener with a missing Secret reference, or one with an empty name or key, is rejected the same way. - MCPServer and MCPRemoteProxy reject a proxyPort of 8443 with a terminal condition instead of a pod crash loop. - MCPServer and MCPRemoteProxy now roll their pods when the listener, signing-key, HMAC, SPIFFE bundle or Redis CA reference changes. The mount paths are fixed, so changing only the reference left the RunConfig unchanged and old pods kept mounting the old Secret. VirtualMCPServer already rolls on any volume change through its volumes hash. User-facing change: Before: tlsListener does not exist. Changing a signing-key, HMAC, SPIFFE bundle or Redis CA Secret reference did not restart MCPServer or MCPRemoteProxy pods, and proxyPort 8443 was accepted. After: tlsListener configures the listener. An X.509 association without one, or a listener with a missing or empty Secret reference, fails validation with a status condition (operator) or a startup error (RunConfig). proxyPort 8443 fails with a terminal condition when a listener is set. Pods roll when the listener, signing-key, HMAC, SPIFFE bundle or Redis CA reference changes. Notes for reviewers: - The "tlsListener required for spiffe_x509" rule is Go, not CEL. The nested exists() over spiffeClientAuth[].methods pushed the schema over the apiserver's CEL cost budget in a real dry-run apply. It is enforced in ValidateTLSListener. - The listener is reachable on the pod only. Adding port 8443 to the Service and container ports is a separate change, and SPIFFE trust configuration is still rejected at startup, so the feature is not deployable yet. - The pod-roll fix covers more than the listener because the signing key, HMAC, bundle and Redis CA volumes had the same gap and share the comparison helper. Upstream and trusted-issuer CA bundle references still roll pods only when the bundle content changes (#6928). - The proxyPort 8443 clash is reported through the auth server reference's validated condition rather than a new one. The clash only exists because the referenced config enables tlsListener, and the message tells the user to change proxyPort. - The volume comparison is tested per volume type in TestAuthServerVolumesNeedUpdate. The controller tests check that each of MCPServer and MCPRemoteProxy (with and without a podTemplateSpec) rolls on one listener change and one non-listener change. Tested with unit tests only. A kind-cluster run of the pod-roll behaviour is still outstanding. Part of #6205. Co-authored-by: Mecatl <noreply@mecatl.dev>
jhrozek
requested review from
ChrisJBurns,
JAORMX,
amirejaz,
blkt,
jerm-dro,
rdimitrov,
reyortiz3 and
tgrunnagle
as code owners
November 2, 2026 16:58
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7024 +/- ##
==========================================
+ Coverage 80.45% 80.54% +0.09%
==========================================
Files 852 852
Lines 87035 87119 +84
==========================================
+ Hits 70021 70173 +152
+ Misses 17009 16941 -68
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Operator-managed workloads had no way to turn on the embedded auth server's TLS listener. That listener, on fixed port 8443, is what SPIFFE X.509 clients need to authenticate, and until now it could only be set up by hand-writing a RunConfig.
The same gap also meant that changing the signing key, HMAC, SPIFFE bundle or Redis CA Secret reference did not restart MCPServer or MCPRemoteProxy pods. The mount paths are fixed, so the RunConfig checksum stayed the same and old pods kept mounting the old Secret.
What changed:
tlsListenerto the embedded auth server config. It takes a certificate Secret ref and a private key Secret ref, both required. The two files are projected as one read-only directory, so kubelet Secret rotation reaches the pod.RunConfig.Validate, so CLI and standalone vMCP users get a startup error.proxyPort: 8443when the referenced config sets a listener. The error goes on the auth server reference's validated condition.Part of "Add SPIRE and operator deployment integration for SPIFFE client auth" (#6205).
Type of change
Test plan
task test)task test-e2e)task lint-fix)The pod-roll behavior is covered by unit tests only: per-volume-type comparison tests, plus controller tests that check MCPServer and MCPRemoteProxy (with and without a podTemplateSpec) roll on one listener change and one non-listener change. A kind-cluster run is still outstanding.
API Compatibility
v1beta1API. It adds one optional field (tlsListener) and changes no existing fields.Changes
cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.goTLSListenerConfigtype,tlsListenerfield,ValidateTLSListenercmd/thv-operator/pkg/controllerutil/authserver.gocmd/thv-operator/controllers/mcpserver_controller.go,mcpremoteproxy_controller.gocmd/thv-operator/controllers/*_runconfig.gocmd/thv-operator/controllers/virtualmcpserver_controller.gopkg/authserver/config.goRunConfig.Validaterejects X.509 association without a listenerdeploy/charts/operator-crds/anddeploy/charts/operator/tlsListenerdocs/operator/crd-api.md,docs/redis-storage.md,docs/arch/*Does this introduce a user-facing change?
Before:
tlsListenerdoes not exist. Changing a signing-key, HMAC, SPIFFE bundle or Redis CA Secret reference did not restart MCPServer or MCPRemoteProxy pods, andproxyPort: 8443was accepted.After:
tlsListenerconfigures the listener. An X.509 SPIFFE association without one, or a listener with a missing or empty Secret reference, fails validation with a status condition (operator) or a startup error (RunConfig).proxyPort: 8443fails with a terminal condition when a listener is set. Pods roll when the listener, signing-key, HMAC, SPIFFE bundle or Redis CA reference changes.Special notes for reviewers
ValidateTLSListener. A nestedexists()overspiffeClientAuth[].methodspushed the schema over the apiserver's CEL cost budget in a dry-run apply.insecureHTTPIssuerMessagewith the same logic.