kubelet: Fix nil panic in podcertificatemanager

If the the PCR that kubelet created gets deleted before it is issued, a
nil panic will be thrown while composing the error message.
This commit is contained in:
Taahir Ahmed
2025-12-05 14:23:45 -08:00
parent ff9971ec7f
commit 8d4237fde8
2 changed files with 112 additions and 1 deletions

View File

@@ -481,7 +481,7 @@ func (m *IssuingManager) handleProjection(ctx context.Context, key projectionKey
// remember creating the PCR, then we must be in case 2. Return to
// credStateInitial so we create a new PCR.
rec.curState = &credStateInitial{}
return fmt.Errorf("PodCertificateRequest %q appears to have been deleted", pcr.ObjectMeta.Namespace+"/"+pcr.ObjectMeta.Name)
return fmt.Errorf("PodCertificateRequest %q appears to have been deleted", key.Namespace+"/"+state.pcrName)
} else if err != nil {
return fmt.Errorf("while getting PodCertificateRequest %q: %w", key.Namespace+"/"+state.pcrName, err)
}

View File

@@ -178,6 +178,117 @@ func TestTransitionInitialToWait(t *testing.T) {
}
}
func TestPCRDeletedWhileWaiting(t *testing.T) {
ctx, cancel := context.WithCancel(ktesting.Init(t))
defer cancel()
kc := fake.NewClientset()
clock := testclock.NewFakeClock(mustRFC3339(t, "2010-01-01T00:00:00Z"))
signerName := "foo.com/signer"
pcrStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
pcrLister := certlistersv1beta1.NewPodCertificateRequestLister(pcrStore)
nodeStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
nodeLister := corelistersv1.NewNodeLister(nodeStore)
node1 := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
UID: "node1-uid",
},
}
if err := nodeStore.Add(node1); err != nil {
t.Fatalf("Unexpected error adding node: %v", err)
}
workloadSA, err := kc.CoreV1().ServiceAccounts("ns1").Create(ctx, &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns1",
Name: "workload",
},
}, metav1.CreateOptions{})
if err != nil {
t.Fatalf("Unexpected error creating workload serviceaccount: %v", err)
}
node1PodManager := &FakeSynchronousPodManager{
pods: []*corev1.Pod{},
}
workloadPod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "ns1",
Name: "workload",
},
Spec: corev1.PodSpec{
ServiceAccountName: workloadSA.ObjectMeta.Name,
NodeName: "node1",
Containers: []corev1.Container{
{
Name: "main",
Image: "notarealimage",
VolumeMounts: []corev1.VolumeMount{
{
Name: "certificate",
MountPath: "/run/foo-cert",
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "certificate",
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: []corev1.VolumeProjection{
{
PodCertificate: &corev1.PodCertificateProjection{
SignerName: signerName,
KeyType: "ED25519",
CredentialBundlePath: "creds.pem",
MaxExpirationSeconds: ptr.To[int32](86400), // Defaulting doesn't work with a fake client.
UserAnnotations: map[string]string{"test.domain/foo": "bar"},
},
},
},
},
},
},
},
},
}
node1PodManager.pods = append(node1PodManager.pods, workloadPod)
node1PodCertificateManager := &IssuingManager{
kc: kc,
podManager: node1PodManager,
pcrLister: pcrLister,
nodeLister: nodeLister,
nodeName: types.NodeName("node1"),
clock: clock,
credStore: map[projectionKey]*projectionRecord{},
}
// Step the handling state machine by one step. We should now be in wait state.
if err := node1PodCertificateManager.handleProjection(ctx, projectionKey{workloadPod.ObjectMeta.Namespace, workloadPod.ObjectMeta.Name, string(workloadPod.ObjectMeta.UID), "certificate", 0}); err != nil {
t.Fatalf("Unexpected error while running handleProjection: %v", err)
}
// Clear all PCRs and advance time past assumeDeletedThreshold.
if err := kc.CertificatesV1beta1().PodCertificateRequests("ns1").DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{}); err != nil {
t.Fatalf("Unexpected error while deleting all PCRs in ns1: %v", err)
}
clock.Step(assumeDeletedThreshold + 1*time.Minute)
// Calling handleProjection again should return an error, *not* nil panic.
err = node1PodCertificateManager.handleProjection(ctx, projectionKey{workloadPod.ObjectMeta.Namespace, workloadPod.ObjectMeta.Name, string(workloadPod.ObjectMeta.UID), "certificate", 0})
if err == nil { // EQUALS nil
t.Fatalf("Got no error from handleProjection, but wanted an error")
}
}
func TestFullFlow(t *testing.T) {
ctx, cancel := context.WithCancel(ktesting.Init(t))
defer cancel()