KubeVirt VMI Denial-of-Service (DoS) Using Pod Impersonation
Description
KubeVirt is a virtual machine management add-on for Kubernetes. Prior to 1.7.0-beta.0, a logic flaw in the virt-controller allows an attacker to disrupt the control over a running VMI by creating a pod with the same labels as the legitimate virt-launcher pod associated with the VMI. This can mislead the virt-controller into associating the fake pod with the VMI, resulting in incorrect status updates and potentially causing a DoS (Denial-of-Service). This vulnerability is fixed in 1.7.0-beta.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
kubevirt.io/kubevirtGo | < 1.7.0-beta.0 | 1.7.0-beta.0 |
Affected products
1Patches
19a6f4a3a7079Merge pull request #15680 from fossedihelm/do-not-use-only-label-for-pod
13 files changed · +31 −75
pkg/controller/BUILD.bazel+0 −1 modified@@ -5,7 +5,6 @@ go_library( srcs = [ "conditions.go", "controller.go", - "controller_ref.go", "controller_ref_manager.go", "expectations.go", "keys.go",
pkg/controller/controller.go+3 −4 modified@@ -332,7 +332,7 @@ func CurrentVMIPod(vmi *v1.VirtualMachineInstance, podIndexer cache.Indexer) (*k var curPod *k8sv1.Pod = nil for _, pod := range pods { - if !IsControlledBy(pod, vmi) { + if !metav1.IsControlledBy(pod, vmi) { continue } @@ -366,7 +366,7 @@ func VMIActivePodsCount(vmi *v1.VirtualMachineInstance, vmiPodIndexer cache.Inde if pod.Status.Phase == k8sv1.PodSucceeded || pod.Status.Phase == k8sv1.PodFailed { // not interested in terminated pods continue - } else if !IsControlledBy(pod, vmi) { + } else if !metav1.IsControlledBy(pod, vmi) { // not interested pods not associated with the vmi continue } @@ -469,8 +469,7 @@ func AttachmentPods(ownerPod *k8sv1.Pod, podIndexer cache.Indexer) ([]*k8sv1.Pod attachmentPods := []*k8sv1.Pod{} for _, obj := range objs { pod := obj.(*k8sv1.Pod) - ownerRef := GetControllerOf(pod) - if ownerRef == nil || ownerRef.UID != ownerPod.UID { + if !metav1.IsControlledBy(pod, ownerPod) { continue } attachmentPods = append(attachmentPods, pod)
pkg/controller/controller_ref.go+0 −50 removed@@ -1,50 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. -Copyright 2017 The KubeVirt Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - k8sv1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - virtv1 "kubevirt.io/api/core/v1" -) - -// GetControllerOf returns the controllerRef if controllee has a controller, -// otherwise returns nil. -func GetControllerOf(pod *k8sv1.Pod) *metav1.OwnerReference { - controllerRef := metav1.GetControllerOf(pod) - if controllerRef != nil { - return controllerRef - } - // We may find pods that are only using CreatedByLabel and not set with an OwnerReference - if createdBy := pod.Labels[virtv1.CreatedByLabel]; len(createdBy) > 0 { - name := pod.Annotations[virtv1.DomainAnnotation] - uid := types.UID(createdBy) - vmi := virtv1.NewVMI(name, uid) - return metav1.NewControllerRef(vmi, virtv1.VirtualMachineInstanceGroupVersionKind) - } - return nil -} - -func IsControlledBy(pod *k8sv1.Pod, vmi *virtv1.VirtualMachineInstance) bool { - if controllerRef := GetControllerOf(pod); controllerRef != nil { - return controllerRef.UID == vmi.UID - } - return false -}
pkg/virt-controller/watch/dra/dra.go+5 −5 modified@@ -190,7 +190,7 @@ func (c *DRAStatusController) addPod(obj interface{}) { return } - controllerRef := controller.GetControllerOf(pod) + controllerRef := metav1.GetControllerOf(pod) vmi := c.resolveControllerRef(pod.Namespace, controllerRef) if vmi == nil { return @@ -218,7 +218,7 @@ func (c *DRAStatusController) deletePod(obj interface{}) { } } - controllerRef := controller.GetControllerOf(pod) + controllerRef := metav1.GetControllerOf(pod) vmi := c.resolveControllerRef(pod.Namespace, controllerRef) if vmi == nil { return @@ -244,8 +244,8 @@ func (c *DRAStatusController) updatePod(old interface{}, cur interface{}) { return } - curControllerRef := controller.GetControllerOf(curPod) - oldControllerRef := controller.GetControllerOf(oldPod) + curControllerRef := metav1.GetControllerOf(curPod) + oldControllerRef := metav1.GetControllerOf(oldPod) controllerRefChanged := !equality.Semantic.DeepEqual(curControllerRef, oldControllerRef) if controllerRefChanged { // The ControllerRef was changed. Sync the old controller, if any. @@ -275,7 +275,7 @@ func (c *DRAStatusController) resolveControllerRef(namespace string, controllerR return nil } pod, _ := obj.(*k8sv1.Pod) - controllerRef = controller.GetControllerOf(pod) + controllerRef = metav1.GetControllerOf(pod) } // We can't look up by UID, so look up by Name and then verify UID. // Don't even try to look up by Name if it is nil or the wrong Kind.
pkg/virt-controller/watch/drain/evacuation/evacuation_test.go+1 −0 modified@@ -554,6 +554,7 @@ func newPod(vmi *v1.VirtualMachineInstance, name string, phase k8sv1.PodPhase, o pod.Annotations = map[string]string{ v1.DomainAnnotation: vmi.Name, } + pod.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(vmi, v1.VirtualMachineInstanceGroupVersionKind)} } return pod
pkg/virt-controller/watch/migration/migration_test.go+2 −0 modified@@ -2371,6 +2371,7 @@ func newSourcePodForVirtualMachine(vmi *virtv1.VirtualMachineInstance) *k8sv1.Po Annotations: map[string]string{ virtv1.DomainAnnotation: vmi.Name, }, + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(vmi, virtv1.VirtualMachineInstanceGroupVersionKind)}, }, Status: k8sv1.PodStatus{ Phase: k8sv1.PodRunning, @@ -2399,6 +2400,7 @@ func newTargetPodForVirtualMachine(vmi *virtv1.VirtualMachineInstance, migration virtv1.DomainAnnotation: vmi.Name, virtv1.MigrationJobNameAnnotation: migration.Name, }, + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(vmi, virtv1.VirtualMachineInstanceGroupVersionKind)}, }, Status: k8sv1.PodStatus{ Phase: phase,
pkg/virt-controller/watch/node/node.go+2 −2 modified@@ -388,7 +388,7 @@ func (c *Controller) alivePodsOnNode(nodeName string) ([]*v1.Pod, error) { for i := range list.Items { pod := &list.Items[i] - if controllerRef := controller.GetControllerOf(pod); !isControlledByVMI(controllerRef) { + if controllerRef := metav1.GetControllerOf(pod); !isControlledByVMI(controllerRef) { continue } @@ -425,7 +425,7 @@ func filterStuckVirtualMachinesWithoutPods(vmis []*virtv1.VirtualMachineInstance if !ok { podsForVMI = map[string]*v1.Pod{} } - if controllerRef := controller.GetControllerOf(pod); isControlledByVMI(controllerRef) { + if controllerRef := metav1.GetControllerOf(pod); isControlledByVMI(controllerRef) { podsForVMI[string(controllerRef.UID)] = pod podsPerNamespace[pod.Namespace] = podsForVMI }
pkg/virt-controller/watch/node/node_test.go+1 −0 modified@@ -510,6 +510,7 @@ func NewHealthyPodForVirtualMachine(podName string, vmi *v1.VirtualMachineInstan v1.CreatedByLabel: string(vmi.UID), v1.AppLabel: "virt-launcher", }, + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(vmi, v1.VirtualMachineInstanceGroupVersionKind)}, }, Spec: k8sv1.PodSpec{NodeName: vmi.Status.NodeName}, Status: k8sv1.PodStatus{
pkg/virt-controller/watch/vmi/lifecycle.go+5 −5 modified@@ -873,7 +873,7 @@ func (c *Controller) deleteAllMatchingPods(vmi *virtv1.VirtualMachineInstance) e } vmiKey := controller.VirtualMachineInstanceKey(vmi) for _, pod := range pods { - if pod.DeletionTimestamp != nil && !isPodFinal(pod) || !controller.IsControlledBy(pod, vmi) { + if pod.DeletionTimestamp != nil && !isPodFinal(pod) || !v1.IsControlledBy(pod, vmi) { continue } if err = c.deletePod(vmiKey, pod, v1.DeleteOptions{}); err != nil { @@ -910,7 +910,7 @@ func (c *Controller) setActivePods(vmi *virtv1.VirtualMachineInstance) (*virtv1. activePods := make(map[types.UID]string) count := 0 for _, pod := range pods { - if !controller.IsControlledBy(pod, vmi) { + if !v1.IsControlledBy(pod, vmi) { continue } count++ @@ -929,7 +929,7 @@ func (c *Controller) allPodsDeleted(vmi *virtv1.VirtualMachineInstance) (bool, e return false, err } for _, pod := range pods { - if controller.IsControlledBy(pod, vmi) { + if v1.IsControlledBy(pod, vmi) { return false, nil } } @@ -1008,10 +1008,10 @@ func (c *Controller) waitForFirstConsumerTemporaryPods(vmi *virtv1.VirtualMachin if !isTempPod(pod) { continue } - if controller.IsControlledBy(pod, vmi) { + if v1.IsControlledBy(pod, vmi) { temporaryPods = append(temporaryPods, pod) } - if ownerRef := controller.GetControllerOf(pod); ownerRef != nil && ownerRef.UID == virtLauncherPod.UID { + if v1.IsControlledBy(pod, virtLauncherPod) { temporaryPods = append(temporaryPods, pod) } }
pkg/virt-controller/watch/vmi/vmi.go+5 −5 modified@@ -351,7 +351,7 @@ func (c *Controller) addPod(obj interface{}) { return } - controllerRef := controller.GetControllerOf(pod) + controllerRef := v1.GetControllerOf(pod) vmi := c.resolveControllerRef(pod.Namespace, controllerRef) if vmi == nil { return @@ -388,8 +388,8 @@ func (c *Controller) updatePod(old, cur interface{}) { return } - curControllerRef := controller.GetControllerOf(curPod) - oldControllerRef := controller.GetControllerOf(oldPod) + curControllerRef := v1.GetControllerOf(curPod) + oldControllerRef := v1.GetControllerOf(oldPod) controllerRefChanged := !equality.Semantic.DeepEqual(curControllerRef, oldControllerRef) if controllerRefChanged { // The ControllerRef was changed. Sync the old controller, if any. @@ -428,7 +428,7 @@ func (c *Controller) onPodDelete(obj interface{}) { } } - controllerRef := controller.GetControllerOf(pod) + controllerRef := v1.GetControllerOf(pod) vmi := c.resolveControllerRef(pod.Namespace, controllerRef) if vmi == nil { return @@ -506,7 +506,7 @@ func (c *Controller) resolveControllerRef(namespace string, controllerRef *v1.Ow return nil } pod, _ := obj.(*k8sv1.Pod) - controllerRef = controller.GetControllerOf(pod) + controllerRef = v1.GetControllerOf(pod) } // We can't look up by UID, so look up by Name and then verify UID. // Don't even try to look up by Name if it is nil or the wrong Kind.
pkg/virt-controller/watch/vmi/vmi_test.go+5 −2 modified@@ -2432,7 +2432,7 @@ var _ = Describe("VirtualMachineInstance watcher", func() { It("Should find vmi, from virt-launcher pod", func() { vmi := newPendingVirtualMachine("testvmi") pod := newPodForVirtualMachine(vmi, k8sv1.PodRunning) - controllerRef := kvcontroller.GetControllerOf(pod) + controllerRef := metav1.GetControllerOf(pod) addVirtualMachine(vmi) result := controller.resolveControllerRef(k8sv1.NamespaceDefault, controllerRef) @@ -2443,7 +2443,7 @@ var _ = Describe("VirtualMachineInstance watcher", func() { vmi := newPendingVirtualMachine("testvmi") pod := newPodForVirtualMachine(vmi, k8sv1.PodRunning) attachmentPod := newPodForVirtlauncher(pod, "hp-test", "abcd", k8sv1.PodRunning) - controllerRef := kvcontroller.GetControllerOf(attachmentPod) + controllerRef := metav1.GetControllerOf(attachmentPod) addVirtualMachine(vmi) addPod(pod) @@ -4249,6 +4249,9 @@ func newPodForVirtualMachine(vmi *virtv1.VirtualMachineInstance, phase k8sv1.Pod virtv1.CreatedByLabel: string(vmi.UID), }, Annotations: podAnnotations, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(vmi, virtv1.VirtualMachineInstanceGroupVersionKind), + }, }, Status: k8sv1.PodStatus{ Phase: phase,
pkg/virt-controller/watch/vmi/volume-hotplug.go+1 −1 modified@@ -315,7 +315,7 @@ func (c *Controller) deleteOrphanedAttachmentPods(vmi *v1.VirtualMachineInstance } for _, pod := range pods { - if !controller.IsControlledBy(pod, vmi) { + if !metav1.IsControlledBy(pod, vmi) { continue }
pkg/virt-controller/watch/workload-updater/workload-updater_test.go+1 −0 modified@@ -747,6 +747,7 @@ func newLauncherPodForVMI(vmi *v1.VirtualMachineInstance) *k8sv1.Pod { Annotations: map[string]string{ v1.DomainAnnotation: vmi.Name, }, + OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(vmi, v1.VirtualMachineInstanceGroupVersionKind)}, }, Status: k8sv1.PodStatus{ Phase: k8sv1.PodRunning,
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-9m94-w2vq-hcf9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-64435ghsaADVISORY
- github.com/kubevirt/kubevirt/commit/9a6f4a3a707992038ef705da4cb3bba8c89d36baghsax_refsource_MISCWEB
- github.com/kubevirt/kubevirt/security/advisories/GHSA-9m94-w2vq-hcf9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.