feat: verify the aggregated API server certificate through the APIService caBundle - #141
Merged
Merged
Conversation
Refs #137 The aggregated API server now keeps APIService v1alpha1.aggregation.coder.com trusting the CA from its serving-certificate Secret, so kube-apiserver verifies the aggregated API server instead of skipping TLS verification. - New internal/aggregated/apiservicetrust controller, started with the managed serving certificate (never in --app=controller or outside a Pod). It watches only that APIService (metadata.name field selector) and is a servingcert.Manager listener; it does not poll. - It sets spec.caBundle to the Secret's CA and insecureSkipTLSVerify to false in one merge patch (field manager coder-k8s-apiservice-cabundle), only when something differs. The CA is read from the live Secret and validated with servingcert.Parse, so replicas cannot flap during a CA rotation and unvalidated bytes are never written. - Failures never affect serving or readiness: they are logged at most every 5 minutes (naming the missing permission and the docs) and retried with backoff capped at 60s. - The annotation coder.com/manage-ca-bundle=false makes it leave the APIService alone (cert-manager or GitOps ownership). - config/rbac/apiservice-cabundle-role.yaml: get/list/watch/patch on that one APIService via resourceNames; excluded from dist/install.yaml, which stays byte-identical. - deploy/apiserver-apiservice.yaml no longer sets insecureSkipTLSVerify (the API rejects it once a caBundle is set). - CI e2e: wait for the caBundle to match the Secret CA, then Available; as the last step, revoke the sync's permission, set a wrong CA, require Available=False with an x509 message, restore, require recovery. - Docs: how kube-apiserver trusts the server, RBAC, upgrade order, rollback, opt-out, and restarting every replica after a CA rotation. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Refs #137 --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
…t out before rolling back Refs #137 Without list/watch the informer never syncs, so the missing-permission warning never appeared (seen in the Kind dogfood). Log it from the watch error handler, rate-limited like the patch warning. The documented rollback now sets the opt-out annotation first; otherwise the running server re-applies the caBundle immediately. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
…condition Refs #137 The Kind dogfood showed kube-apiserver's APIService availability check does not verify the serving certificate: with a wrong caBundle the APIService stayed Available=True/Passed while proxied requests failed with 503 and kube-apiserver logged x509 errors. The CI checks now use a request proxied by kube-apiserver (and its x509 log line), and the docs describe the 503 window instead of Available=False. --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Refs #137 --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Refs #137 --- _Generated with `xum` • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
Member
Author
|
@codex security review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 25, 2026
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.
Refs #137. Step 2 of 3.
The aggregated API server now puts its serving CA into the APIService
caBundleand turnsinsecureSkipTLSVerifyoff, so kube-apiserver verifies the server's certificate. The certificate itself comes from #140.What changed
apiservicetrustcontroller in the aggregated API server process. It runs only when the managed serving certificate is in use, so never with--app=controllerand never outside a Pod.v1alpha1.aggregation.coder.com, using ametadata.namefield selector, and reacts to CA changes. It does not poll.{caBundle, insecureSkipTLSVerify:false}, only when something differs, with FieldManagercoder-k8s-apiservice-cabundle.coder.com/manage-ca-bundle=falseon the APIService, for clusters where cert-manager or GitOps ownscaBundle.config/rbac/apiservice-cabundle-role.yamlgrantsget/list/watch/patchon that one APIService, limited byresourceNames. It is left out ofdist/install.yaml(the controller-only bundle), which is byte-identical.deploy/apiserver-apiservice.yamlno longer setsinsecureSkipTLSVerify: true.caBundleequals the Secret CA, andkubectl get --raw /apis/aggregation.coder.com/v1alpha1succeeds.Finding:
Availabledoes not show that verification worksWith a wrong
caBundle, the APIService stayedAvailable=True("all checks passed"), while proxied requests failed with 503 and kube-apiserver logged an x509 error. So neither check relies onAvailable.Risks
caBundle; the opt-out annotation covers that.Validation
make verify-vendor,build,lint,test,test-integration,docs-check, markdownlint, cspell, actionlint, and the installer freshness check all pass.resourceNames, no field selector, ignoring the opt-out, writing unvalidated bytes, and a missing FieldManager.resourceNameslist/watch works with the field selector, and that drift is repaired.ci.yaml(the x509 line appeared in 4 of 4 runs). A 2-replica CA rotation produced exactly one patch and no flapping.optout-ci.mp4
Next
Step 3 adds an x509 troubleshooting entry and more rotation docs; #137 will be closed after it lands. Follow-ups: a namespaced Role for the serving-CA Secret, and bring-your-own Secret support.
Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:medium