Running virtual machines on Kubernetes with KubeVirt puts two security models on one platform, and a gap in either one exposes both. A VM brings a full guest operating system, persistent disks, consoles and live migration; Kubernetes brings RBAC, namespaces, network policy and pod security. KubeVirt maps VMs onto Kubernetes objects so the cluster's controls can govern them, but only if the cluster is configured with VMs in mind. This guide covers the KubeVirt components and their trust levels, RBAC for VM operations, feature gates, network isolation, storage and disk import and export, live migration, console and SSH access, confidential VMs, and monitoring. It is for platform engineers, Kubernetes administrators and security architects consolidating VM and container workloads.
This is an independent guide from NHI Mgmt Group, based on the KubeVirt project's public documentation as of November 2026 (user guide covering v1.9). It is not sponsored or reviewed by the KubeVirt project. Features change between releases, so confirm settings against the current KubeVirt documentation.
Key takeaways
- A VM should never grant more than a pod. KubeVirt's design principle is that running a VM gives a user no extra privileges compared with any other pod they could run; keep it that way by holding VM namespaces to the restricted Pod Security level.
- VM roles ride on namespace roles by default. KubeVirt's view, edit and admin ClusterRoles aggregate into the standard Kubernetes roles, so anyone with namespace edit can create VMs and open their consoles.
- Review feature gates on every upgrade. From v1.9, Beta feature gates are enabled by default, so an upgrade can switch on capabilities you have not assessed.
- Disk export, import and migration are data paths. VM export tokens, image import sources and live migration traffic all carry complete disk or memory contents and need the same care as database backups.
- The guest is still a server. KubeVirt isolates the VM; it does not patch, harden or monitor the operating system inside it.
Components and trust boundaries
KubeVirt is installed by an operator and extends the Kubernetes API with custom resources such as VirtualMachine (VM) and VirtualMachineInstance (VMI). Its security fundamentals, as described by the project, are least privilege, separation of trusted and untrusted components, and mutual TLS between components.
| Component | Role | Trust and privilege | What to protect |
|---|---|---|---|
| virt-api | API endpoint for KubeVirt resources and subresources (console, VNC) | Trusted control plane | Who can call console and VNC subresources; certificate secrets |
| virt-controller | Cluster-wide controller that creates virt-launcher pods for VMIs | Trusted control plane | Its service account and the KubeVirt namespace |
| virt-handler | Node DaemonSet that performs host-level tasks for VMs (devices, networking) | Privileged, trusted; SELinux is the main restriction on its host access | Node access; keep workloads out of the KubeVirt namespace |
| virt-launcher | One pod per VM, starts and monitors the qemu-kvm process | Non-privileged, treated as untrusted because the guest runs third-party code; no cluster API access | Pod security level, network policy, node placement |
| Containerized Data Importer (CDI) | Imports disk images from web servers or registries into DataVolumes | Separate operator | Allowed image sources, import credentials |
Components authenticate each other with certificates signed by the KubeVirt CA, mounted from secrets. The CA and certificates rotate on a configurable interval, and the project documents that a cluster administrator can force rotation after a suspected compromise by deleting the corresponding secrets in the KubeVirt install namespace. Treat that namespace like kube-system: restrict who can read its secrets and who can deploy into it. The virt-launcher design also exposes /dev/kvm through a device plugin rather than a hostPath volume, so VM pods do not need host filesystem access.
RBAC for VM operations
KubeVirt authorisation uses standard Kubernetes RBAC. The default ClusterRoles are:
- kubevirt.io:view: view all KubeVirt resources; cannot shut down a VM or open its console or VNC.
- kubevirt.io:edit: create, modify and delete VMs and VMIs, and access both console and VNC.
- kubevirt.io:admin: full permissions including deleting collections and, through a ClusterRoleBinding (or a RoleBinding in the KubeVirt namespace), changing the KubeVirt runtime configuration in the KubeVirt custom resource.
By default these roles aggregate into the Kubernetes admin, edit and view roles through aggregate-to-* labels, so existing namespace permissions automatically include VM permissions. Console and VNC access is equivalent to sitting at the VM's keyboard, which many organisations treat as privileged access. Two options:
| Approach | How | When it fits |
|---|---|---|
Keep aggregation (default, AggregateToDefault) | Control VM access through who holds namespace edit and admin | Teams that own both containers and VMs in a namespace; smaller clusters |
Manual binding (roleAggregationStrategy: Manual) | Enable the OptOutRoleAggregation feature gate (Alpha, from v1.8), then bind KubeVirt roles explicitly | Shared clusters, regulated workloads, or where console access must be separately approved |
| Custom roles | Write roles that grant VM lifecycle verbs without the console and VNC subresources | Operators who restart VMs but should not log in to them |
Never bind kubevirt.io:admin cluster-wide to application teams: that role reaches the KubeVirt configuration, including feature gates. See the Kubernetes RBAC Guide for binding hygiene.
Feature gates and configuration baseline
KubeVirt features move through Alpha, Beta and GA. Alpha gates are disabled until listed in featureGates; from v1.9, Beta gates are enabled by default and can be turned off through disabledFeatureGates; GA features are always on. From v1.9 each release ships a feature gate report listing every non-GA gate and its state.
- Review on every upgrade: diff the feature gate report between your current and target release, and add any Beta gate you have not assessed to
disabledFeatureGatesbefore upgrading production. - Gates that touch the host:
HostDisklets a VM use a disk image on a node path, similar to a Kubernetes hostPath. Keep it off unless a specific workload needs it, and restrict that workload to dedicated nodes. The documentation prefers PVCs over hostpath for sharing data. - Persistent TPM and UEFI state: the
VMPersistentStategate stores TPM and UEFI variables on backend storage PVCs; choose an encrypted storage class throughvmStateStorageClass, because that state can hold secrets such as disk encryption keys sealed to the vTPM. - Manage the KubeVirt CR as code: keep it in Git with review, and alert on changes to it through the Kubernetes audit log.
- Admission control: enforce VM specifications (allowed volume types, network bindings, host devices, required labels) with an admission policy engine. See the Kubernetes Admission Control Guide.
Pod security and node placement
The project states that VMs are designed to work with the restricted Pod Security level. Label VM namespaces with pod-security.kubernetes.io/enforce: restricted and treat any VM that needs an exception (for example a privileged component for SCSI persistent reservations or host devices) as a separate, reviewed workload. See the PodSecurityPolicy to Pod Security Admission guide.
- Dedicated nodes: production VM workloads need bare metal or nested-virtualisation-capable nodes. Use node selectors and taints so VMs and sensitive container workloads with different trust levels do not share nodes unless intended.
- Host devices and GPUs: PCI passthrough and mediated devices give a guest direct hardware access; allow them only through admission policy and only on nodes reserved for that purpose.
- Escape risk: a guest-to-host escape lands in the virt-launcher pod, not directly on the node, which is why its restricted profile and SELinux confinement matter (ATT&CK T1611 Escape to Host).
Network isolation
KubeVirt's documentation states that, by default, all VMIs in a namespace are reachable from other VMIs and network endpoints, and that NetworkPolicy applies to VMIs because VMI labels pass through to the virt-launcher pods. Add a deny-by-default policy in each VM namespace, then allow specific ports. See the Kubernetes Network Policy Guide.
- Pod network binding: masquerade binding keeps the VM behind the pod IP and works with live migration; bridge binding on the pod network blocks live migration according to the project documentation.
- Secondary networks: Multus attachments put VMs on VLANs outside the cluster network, where Kubernetes NetworkPolicy may not apply. Control them with network attachment definitions per namespace and with firewalls on the attached network.
- Exposure: expose VM services through Services and Ingress like any pod. Do not expose SSH or RDP on NodePorts; use virtctl or a bastion.
Storage, disk import and export
| Data path | Risk | Control |
|---|---|---|
| CDI import from web server or registry | Tampered or malicious base images (T1525 Implant Internal Image) | Import only from internal registries or mirrors; verify image digests or signatures; restrict who can create DataVolumes with external sources |
| VirtualMachineExport | Full disk contents leave the cluster (T1530 Data from Cloud Storage) | Restrict the export verb through RBAC; use long random tokens (the docs recommend at least 12 alphanumeric characters, longer is better); expose the export proxy only when needed; delete exports after use |
| Export auth-header secret | Contains a text version of the export token | Limit secret read access in the namespace (T1552.007 Container API) |
| Persistent volumes and snapshots | Disk data at rest and in snapshot copies | Encrypted storage classes; RBAC on snapshot and restore APIs |
| hostDisk volumes | VM data stored on a node path outside PVC controls | Keep HostDisk gate off; use PVCs |
Live migration
Live migration copies a running VM's memory and state between virt-handler instances on different nodes, using mutual TLS. The migrations section of the KubeVirt CR includes disableTLS; keep it at false. Other settings to set deliberately:
- Dedicated migration network: configure a separate network for migrations through a Multus NetworkAttachmentDefinition referenced in the KubeVirt CR. The documentation notes this needs a separate physical network on every node; it keeps memory contents off the general pod network.
- Bandwidth and concurrency:
bandwidthPerMigration,parallelMigrationsPerClusterandparallelOutboundMigrationsPerNodestop node drains from saturating the network. - Unsafe options: leave
unsafeMigrationOverrideatfalseand enableallowPostCopyonly after assessment, since the documentation notes post-copy needs additional node-level permissions in most environments. - Migration policies: use MigrationPolicy objects to apply stricter settings to sensitive namespaces.
Where identity fits: console, SSH and VM credentials
- Console and VNC: virtctl console and virtctl vnc go through virt-api, so access is governed by RBAC and recorded in the Kubernetes audit log. Treat the console subresource as privileged access and approve it just in time where possible.
- SSH keys: the access credentials API injects SSH public keys from a Kubernetes Secret, either statically at first boot through cloud-init metadata (kept separate from user data) or dynamically through the QEMU guest agent. Prefer dynamic injection so keys can be revoked without rebuilding the VM (ATT&CK T1021.004 SSH).
- Startup data: cloud-init user data, Ignition configs and Windows Sysprep answer files often contain passwords or tokens. Store them in Secrets, not ConfigMaps or inline VM specs, and rotate anything that has been in a VM manifest in Git.
- Service accounts: virt-launcher pods receive only the namespace's default service account and do not use cluster API access. Disable token automount on that default account so a compromised guest-side process cannot reach the API. See the Kubernetes Secrets Security Guide.
Confidential VMs
KubeVirt can run AMD SEV and SEV-SNP guests by setting spec.domain.launchSecurity.sev or spec.domain.launchSecurity.snp on AMD EPYC hardware; before v1.9 this needed the WorkloadEncryptionSEV feature gate. Memory encryption protects guest memory from the host. Check the documented limitations for your release before relying on it, for example that SEV guests cannot use PCI passthrough and that attestation is listed among current limitations, and plan attestation separately where your threat model needs it. See the Confidential Computing Guide.
Rolling out KubeVirt securely
- Design: decide which namespaces host VMs, which nodes run them, and whether VM roles aggregate or are bound manually.
- Install from pinned releases: install the operator and CDI from versioned release manifests in Git, and record the feature gate report for that version.
- Baseline: apply restricted Pod Security labels, deny-by-default NetworkPolicy, admission policies for VM specs and encrypted storage classes before the first VM.
- Pilot: migrate a small set of non-critical VMs; run admission policies in audit mode first and review violations.
- Harden the guests: apply the same OS baseline, patching and EDR you use for VMs elsewhere. See the Server Build and Golden Image Hardening Guide.
- Upgrade discipline: test upgrades in staging, diff feature gates, then roll to production.
- Rollback: keep VM disks on snapshotted PVCs and keep the previous operator manifests, so a failed upgrade or policy can be reverted without data loss.
Detection and measurement
Send Kubernetes audit logs for kubevirt.io resources and subresources to the SIEM. Alert on console and VNC sessions outside approved windows (T1609 Container Administration Command), creation of VirtualMachineExport objects, changes to the KubeVirt CR, new DataVolumes with external sources, VMs requesting host devices or hostDisk, and VMs started in unexpected namespaces, which can signal resource hijacking (T1496.001 Compute Hijacking). See the Kubernetes Audit Log Detection Guide. KubeVirt components also expose Prometheus metrics for VM and migration health.
- Policy coverage: percentage of VM namespaces with restricted Pod Security and a deny-by-default NetworkPolicy (target 100 percent).
- Console access: number of principals able to open consoles, and sessions per week, trending down.
- Exports: active VirtualMachineExport objects older than 24 hours (target zero).
- Feature gates: enabled Alpha and Beta gates without a recorded risk assessment (target zero).
- Guest patching: percentage of VMs within patch SLA, measured inside the guest.
Common failure patterns
- Edit means console: developers with namespace edit gain console and VNC access to production VMs through role aggregation.
- Flat VM namespaces: VMs migrated from segmented VLANs land in a namespace without NetworkPolicy and can reach each other freely.
- Unreviewed upgrades: a release enables Beta features by default, and nobody checks what changed.
- Secrets in user data: cloud-init scripts with passwords are committed to Git as part of VM manifests.
- Forgotten exports: a VirtualMachineExport created for a one-off copy stays reachable through an Ingress with a short token.
- Guests left behind: VMs moved onto Kubernetes drop out of the server patching and EDR programme because the platform team assumes the cluster covers them.
Framework mapping
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev. 5 | CIS Controls v8.1 | ATT&CK addressed |
|---|---|---|---|---|
| RBAC and console access | PR.AA-05 | AC-3, AC-6, AC-17 | Control 5, Control 6 | T1609, T1021.004 |
| Configuration and feature gates | PR.PS-01 | CM-2, CM-6, CM-7 | Control 4 | T1611 |
| Network isolation and migration network | PR.IR-01, PR.DS-02 | SC-7, SC-8 | Control 12, Control 13 | Lateral movement between VMs |
| Disk import, export and storage | PR.DS-01 | SC-28, SI-7 | Control 3 | T1525, T1530, T1552.007 |
| Process and workload isolation | PR.PS-01 | SC-39 | Control 4 | T1611 |
| Audit and monitoring | DE.CM-09, PR.PS-04 | AU-2 | Control 8 | T1496.001 |
Practitioner checklist
- Restrict access to the KubeVirt install namespace and its certificate secrets.
- Decide on role aggregation; use manual binding or custom roles where console access must be controlled separately.
- Keep VM namespaces at the restricted Pod Security level and reserve exceptions for dedicated nodes.
- Add a deny-by-default NetworkPolicy in every VM namespace and control Multus attachments.
- Diff the feature gate report on every upgrade and disable unassessed Beta gates.
- Keep
disableTLSfalse for migrations and use a dedicated migration network for sensitive workloads. - Import images only from internal sources and restrict and time-limit VM exports.
- Inject SSH keys through the access credentials API and keep startup data in Secrets.
- Audit console, VNC, export and KubeVirt CR changes in the SIEM.
- Keep guest OS patching and EDR in scope for every VM on the cluster.
Standards and references
- KubeVirt: Authorization
- KubeVirt: Activating and deactivating feature gates
- KubeVirt: Live Migration
- KubeVirt: NetworkPolicy
- KubeVirt: Export API
- KubeVirt: Security Fundamentals
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
- NIST: Cybersecurity Framework 2.0
Related NHI Mgmt Group resources: Kubernetes Security Guide · Hypervisor and Virtualisation Security Guide · Kubernetes RBAC Guide · Kubernetes Network Policy Guide · Kubernetes Secrets Security Guide