Repository navigation
🤖 fix: date the serving CA from the injected clock and backdate it - #223
Conversation
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 review |
🛡️ 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. |
…efs #221) Change-Id: I69ad7649e8cae216fff651ddd91ff6aff9f5833b Signed-off-by: Thomas Kosiewski <tk@coder.com>
1d3b44c to
5bf9622
Compare
|
@codex review |
|
@codex security review |
🛡️ 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. |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Refs #221. Stacked on #222: merge #222 first, then this PR retargets to
main.Summary
servingcert.Generate(namespace, now)dated the serving CA from the wall clock, not fromnow, and did not backdate it.Parse(..., now)therefore rejected a fresh CA as "not valid until" whenever the caller'snowwas even one second behind the clock that issued the CA. This PR passesNotBefore: now - clockSkewAllowance(1 h, the same margin the serving certificate already uses) tocertutil.NewSelfSignedCACert.Background
The merge queue rejected #222 (run 38277477505) on this flake in
TestGenerateProducesVerifiableServingCert:Root cause:
internal/aggregated/servingcert/servingcert.go:116calledcertutil.NewSelfSignedCACertwithoutNotBefore. The vendored helper then uses its owntime.Now()(vendor/k8s.io/client-go/util/cert/cert.go:61,68). x509 encodes the time to whole seconds.servingcert.go:215rejects the CA whennow.Before(CACert.NotBefore).now := time.Now()beforeGenerate. When a second boundary falls between that call and certutil'stime.Now(), the truncated CANotBeforelands afternow, andParsefails.This predates #222.
vendor/k8s.iodid not change in #222, and the original test also fails onmain(go1.26.8, old deps).Production impact (same cause): the serving certificate is backdated by 1 h for clock skew, but the CA was not. A replica, or a kube-apiserver, whose clock is behind the issuing pod's saw a CA from the future. A replica that adopts the Secret then got a
CorruptSecretError("delete it so coder-k8s generates a new CA"). I did not reproduce the kube-apiserver case on a cluster.Implementation
Generatesetscertutil.Config.NotBefore = now.Add(-clockSkewAllowance). The CANotAfteris unchanged (certutil: wall clock + 10 years), and the serving certificate is still clamped to it.TestGenerateDatesCAFromInjectedClockgenerates withnow30 min in the past. It asserts that the CANotBeforeisnow - 1h(to the second), that the chain verifies atnow, and thatParse(..., now)accepts the bundle.Validation
Repro counts for the original test,
go test -count=N -run '^TestGenerateProducesVerifiableServingCert$' ./internal/aggregated/servingcert/:-race, 500-race, 2000main55e7f34The new test fails 100/100 without the fix (
CA NotBefore = <wall clock>, want <now - 1h>) and passes 100/100 with it.Risks
Low. Only newly generated CAs change: they are now valid from 1 h before issue. Existing Secrets are parsed as before.
Generated with
mux• Model:anthropic:claude-opus-5-5• Thinking:high