fix(csrapprover): validate CSR requester username matches CommonName for node certificates - #2734
Open
nishantbkl3345-ship-it wants to merge 4 commits into
Conversation
added 4 commits
August 3, 2026 00:26
…LeaderNodeLabelSelector is nil
…key mismatch in enhancement mode In gcPodsWhenRestart, KeyBuildInfo for pods fetched from the apiserver was missing the Version field. In enhancement mode, this produced keys like 'pods..core' instead of 'pods.v1.core', causing them to never match the cached keys from ListResourceKeysOfComponent. As a result, all cached pods appeared deleted, triggering the safety guard that skips GC entirely — making pod GC silently non-functional on restart. Added Version: 'v1' to the KeyBuildInfo and created gc_test.go with unit tests that prove the mismatch and verify the fix.
…bjWithObjs to prevent overflow In completeListObjWithObjs, resourceVersion was parsed using strconv.Atoi into a signed Go int, ignoring parsing errors. On 32-bit platforms (such as ARM32 linux/arm/v7 edge devices), signed int is 32-bit (max 2147483647). Any resourceVersion above 2^31-1 caused strconv.Atoi to return ErrRange and clamp to 2147483647. This produced corrupted resourceVersion strings on synthesized list responses, causing relist storms and 410 Gone errors on edge devices. Switched to strconv.ParseUint(rvStr, 10, 64) with uint64 listRv and added unit tests covering 64-bit uint64 resourceVersions and edge cases.
…for node certificates
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens several controller/cache paths, primarily aiming to close a privilege-escalation risk in csrapprover by validating node CSR requester identity, while also addressing correctness issues in hub leader selection and yurthub cache/GC behaviors.
Changes:
csrapprover: add a Username vs CommonName validation gate for node client/serving CSRs.hubleader: avoid panics whenLeaderNodeLabelSelectoris nil under mark election strategy, and add a regression test.yurthub: fix pod GC key construction to include API version; improve listresourceVersionaggregation to handle large RVs safely and add unit tests.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/yurtmanager/controller/hubleader/hubleader_controller.go | Avoid overwriting matchLabels with a potentially nil selector map. |
| pkg/yurtmanager/controller/hubleader/hubleader_controller_test.go | Add a test case for mark strategy with nil LeaderNodeLabelSelector. |
| pkg/yurtmanager/controller/csrapprover/csr_approver_controller.go | Add Username/CommonName validation in node cert recognizers. |
| pkg/yurtmanager/controller/csrapprover/csr_approver_controller_test.go | Add unit test for mismatched node Username vs CommonName CSR. |
| pkg/yurthub/gc/gc.go | Include Version: "v1" when building pod storage keys during restart GC. |
| pkg/yurthub/gc/gc_test.go | New tests intended to validate pod GC key consistency. |
| pkg/yurthub/cachemanager/cache_manager.go | Parse list resourceVersions as uint64 and handle invalid/empty RVs safely. |
| pkg/yurthub/cachemanager/cache_manager_test.go | Add tests for completeListObjWithObjs RV aggregation behavior. |
Suppressed comments (2)
pkg/yurtmanager/controller/csrapprover/csr_approver_controller.go:369
- This check only enforces Username==CommonName when Username already has the "system:node:" prefix. If Username is non-node (or empty), a crafted CSR with CN "system:node:..." and the expected orgs can still be auto-approved, because approval is based largely on CSR contents. Tighten this recognizer to require csr.Spec.Username to be a node identity and to exactly match x509cr.Subject.CommonName; otherwise return false.
if strings.HasPrefix(csr.Spec.Username, "system:node:") && csr.Spec.Username != x509cr.Subject.CommonName {
return false
}
pkg/yurthub/gc/gc_test.go:153
- In this test, errors from KeyFunc are ignored and the “buggy lookup should MISS” path only logs instead of asserting, so the test can pass without actually validating the expected failure mode. Consider failing when the buggy lookup finds the cached key in enhancement mode, and handle KeyFunc errors explicitly.
buggyKey, _ := store.KeyFunc(storage.KeyBuildInfo{
Component: "kubelet",
Namespace: "kube-system",
Name: "coredns-abc123",
Resources: "pods",
})
currentPodKeysBuggy[buggyKey] = struct{}{}
// FIXED: with Version
currentPodKeysFixed := make(map[storage.Key]struct{})
fixedKey, _ := store.KeyFunc(storage.KeyBuildInfo{
Component: "kubelet",
Namespace: "kube-system",
Name: "coredns-abc123",
Resources: "pods",
Version: "v1",
})
currentPodKeysFixed[fixedKey] = struct{}{}
// Check buggy lookup — this should MISS (the bug)
if _, found := currentPodKeysBuggy[cachedKey]; found {
t.Logf("Buggy code found the cached key (storage in legacy mode)")
} else {
t.Logf("CONFIRMED BUG: Buggy code did NOT find cached key %q in currentPodKeys — pod would be incorrectly marked for GC", cachedKey.Key())
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+87
to
+92
| // The buggy key should NOT match the list key (proving the bug exists) | ||
| if buggyKey.Key() == listKey.Key() { | ||
| t.Logf("Keys match even without Version — storage may be in legacy mode, bug is not applicable in this mode") | ||
| } else { | ||
| t.Logf("CONFIRMED: Buggy key %q != list key %q — pod GC key mismatch exists in enhancement mode", buggyKey.Key(), listKey.Key()) | ||
| } |
Comment on lines
+336
to
+338
| if strings.HasPrefix(csr.Spec.Username, "system:node:") && csr.Spec.Username != x509cr.Subject.CommonName { | ||
| return false | ||
| } |
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.



What this PR does / why we need it
Fixes a security vulnerability in
csrapproverwhere node client and serving CSRs were auto-approved without verifying thatcsr.Spec.Usernamematched the requestedCommonName.Previously,
isYurtHubNodeCertandisYurtTLSServerCertonly checked that theCertificateRequestCommonNamehad prefixsystem:node:. They never examinedcsr.Spec.Username. If an authenticated node (system:node:node-a) submitted a CSR requesting a certificate forsystem:node:node-b,csrapproverauto-approved it — allowing any node to mint client certificates for any other node's identity and perform lateral movement to read secrets/pods.This PR adds a validation check requiring that when
csr.Spec.Usernamestarts withsystem:node:, it must matchx509cr.Subject.CommonName, mirroring upstream Kubernetes node CSR approver behavior.Which issue(s) this PR fixes
N/A
Special notes for your reviewer
yurthub node client CSR with mismatched node username and commonNameinpkg/yurtmanager/controller/csrapprover/csr_approver_controller_test.go.go test -racepass cleanly.