Skip to content

fix(kubernetes): let kubevirt-csi hot-unplug from an orphaned VMI - #4563

Merged
IvanHunters merged 1 commit into
mainfrom
fix/kubevirt-csi-rbac-vmi-removevolume
Oct 5, 2026
Merged

IvanHunters merged 1 commit into
mainfrom
fix/kubevirt-csi-rbac-vmi-removevolume

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

This lets the kubevirt-csi controller detach a volume from a VM instance that no VM owns. Right now that call is always denied. Closes #4452.

The driver has a fallback for a volume that is still attached to the VMI but is no longer in the VM spec. It calls the VMI-level removevolume subresource. The -kcsi Role grants only the two VM-level subresources, so the call ends in 403. Nothing else reaches this service account with that grant. Only its own Role and ClusterRole name it as a subject, and the group bindings that cover every service account grant nothing on this subresource. At v1.9.0, kubevirt.io:default grants only kubevirts, version and guestfs. kubevirt.io:edit and kubevirt.io:admin do include the VMI subresource, but they aggregate only into the built-in edit and admin roles, and no tenant binding gives those to this account. The cozystack tenant roles aggregate on their own labels and don't list it.

The fallback is still needed after the flag in #4453 goes to false. The flag only skips VMIs that a VM owns. When a VM is deleted with propagationPolicy=Orphan, the KubeVirt VM controller drops its finalizer and leaves the VMI running (vm.go at v1.9.0). The tenant node stays up with its volumes attached. When a pod moves off that node, the driver has to detach through the VMI, and on a 403 the hot-plug pod keeps holding the infra volume. Until #4453 lands, the fallback also runs for VM-owned VMIs and gets the same 403.

The grant is update on virtualmachineinstances/removevolume and nothing more. The account can already remove a volume from any VM in the namespace, so it gets no new reach. virt-api also rejects the VMI-level call on a VM-owned VMI once HotplugVolumes is off. virtualmachineinstances/addvolume stays out because the driver only attaches through the VM.

The new helm-unittest pins the Role's subresources.kubevirt.io rule to these three subresources with update. I haven't reproduced the 403 on a cluster. This shows it on a tenant namespace, before and after:

kubectl auth can-i update virtualmachineinstances.subresources.kubevirt.io --subresource=removevolume \
  --namespace <tenant-ns> --as system:serviceaccount:<tenant-ns>:<release>-kcsi

Upstream kubevirt/csi-driver has the same gap in deploy/infra-cluster-service-account.yaml.

Screenshots

No UI change.

Downstream repositories

Release note

fix(kubernetes): the kubevirt-csi controller can now hot-unplug a volume from a VMI that no VM owns, instead of failing with 403 Forbidden.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Kubernetes CSI volume removal for virtual machine instances, including cases where a volume is not listed in the virtual machine specification. This helps the driver complete volume removal in scenarios where the Kubernetes virtualization API accepts the instance-level request. Existing permissions for adding and removing volumes from virtual machines remain unchanged.

The driver falls back to the VMI-level removevolume subresource when
a volume is still attached to the VMI but no longer in the VM spec.
The -kcsi Role grants only the VM-level subresources, and nothing
else bound to the service account covers the VMI one: kubevirt.io:
default grants no subresource of it, and the kubevirt.io:edit and
admin roles that do are aggregated only into the built-in edit and
admin roles, which the tenant namespace never binds to this account.
Every such call ends in 403, the unpublish retries until it fails,
and the hot-plug pod keeps the infra volume attached.

The call stays needed once the driver stops falling back for a
VM-owned VMI (kubevirt/csi-driver#382), because it still makes it
for a VMI no VM owns. Deleting a VM with orphan propagation leaves
its VMI running, since the KubeVirt VM controller then drops its
finalizer without stopping the VMI, so the tenant node stays up and
its volumes stay attached until the driver detaches them.

The grant adds no reach the account lacks: it can already remove a
volume from any VM in the namespace, and virt-api refuses the VMI
call on a VM-owned VMI once the HotplugVolumes gate is off.
virtualmachineinstances/addvolume stays ungranted, as the driver
only attaches through the VM.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
@github-actions github-actions Bot added area/kubernetes Issues or PRs related to the tenant Kubernetes app kind/bug Categorizes issue or PR as related to a bug size/S This PR changes 10-29 lines, ignoring generated files labels Sep 29, 2026
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 34c228de-a025-479d-9d1d-3b581b65bc04

📥 Commits

Reviewing files that changed from the base of the PR and between b314514 and 2b7e0db.

📒 Files selected for processing (2)
  • packages/apps/kubernetes/templates/csi/infra-cluster-service-account.yaml
  • packages/apps/kubernetes/tests/csi_rbac_test.yaml

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The infra-cluster CSI Role now grants update access to virtualmachineinstances/removevolume. A Helm test checks that the rule contains exactly the specified VM and VMI subresources and the update verb.

Changes

CSI VMI RBAC

Layer / File(s) Summary
Add and verify VMI removevolume access
packages/apps/kubernetes/templates/csi/infra-cluster-service-account.yaml, packages/apps/kubernetes/tests/csi_rbac_test.yaml
The CSI Role adds virtualmachineinstances/removevolume to its update rule. The Helm test checks the exact subresource list and verb.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 2b7e0

The permission change appears ready to merge after normal checks; no actionable merge-blocking risk is identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 2b7e0

The new permission is limited to the CSI controller’s namespace and enables a specific recovery path. The controller checks that the requested hot-plug volume is still reported by the VMI, but the available evidence does not fully establish who can initiate a detach or how volume ownership is enforced.

Retained concerns

  • Low · security · inferred: The newly authorized VMI fallback relies on upstream request validation: locally, a matching hot-plug volume name is sufficient to select a VMI for removal. Whether a caller can supply unrelated node and volume IDs is not established.
Security review details

Security Blast Radius

  • inferred — The additional authority can affect VMI hot-plug volumes in the release namespace reached with this service account; this Role does not grant VMI removal across namespaces. Other independently granted identities or permissions outside the chart were not established.

Security Findings and Attack Paths

  • inferred — If a caller can cause an unpublish request with unrelated node and volume IDs to pass upstream validation, the newly permitted fallback could remove a matching hot-plug volume from that VMI. The available evidence does not establish such caller access or bypass, so this is a conditional path, not a verified exploit.

Trust Boundaries and Controls

  • observed — The deployed controller defaults to in-cluster credentials, while the RoleBinding ties the new permission to its namespaced service account. The local detach path requires an existing infrastructure PVC, upstream unpublish success, and a matching VMI hot-plug status entry; it does not itself check PVC-to-VMI ownership.

Resilience and Maintainability Implications

  • inferred — Retries can converge when VMI status no longer lists the volume, but the local read-then-remove sequence does not prove safety against a concurrent detach and reattach using the same name. KubeVirt API behavior for stale requests remains unverified.

Hardening Proposals

  • proposed — Establish the upstream unpublish and KubeVirt API ownership, authorization, and concurrency guarantees for orphaned VMIs. If those guarantees do not bind the request to the intended attachment, add that binding before using the VMI-level permission.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Kubernetes RBAC fix that lets kubevirt-csi hot-unplug volumes from orphaned VMIs. It is concise and directly matches the main change.
Linked Issues check ✅ Passed Issue #4452 requires the kubevirt-csi service account to access the VMI-level removevolume fallback. The Role now grants update on subresources.kubevirt.io/virtualmachineinstances/removevolume. …
Out of Scope Changes check ✅ Passed The changes are limited to the required Role permission, explanatory comments, and a focused RBAC test. These changes directly support issue #4452 and add no unrelated behavior or permissions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Small, correct RBAC change. The -kcsi Role gains update on virtualmachineinstances/removevolume and nothing else, which is exactly the subresource the driver's hot-unplug fallback calls. I checked the grant against the pinned client contract, the caller path, data safety, tenant scope, and the new test.

What I checked:

  • Contract. RemoveVolumeFromVMI in the pinned fork (kubevirt/[email protected], pkg/kubevirt/client.go:205) issues PUT /apis/subresources.kubevirt.io/<ver>/namespaces/<ns>/virtualmachineinstances/<name>/removevolume. apiGroup, resource/subresource and verb (update == PUT) match the new rule.
  • Caller. unpublishHotplugVolume (controller.go:421-465) is the only call site, and without this rule the apiserver returns 403 before virt-api ever sees the request. The Go driver is untouched here, so this is the companion grant for the fallback that already landed.
  • Data safety. The fallback runs only inside ControllerUnpublishVolume, only for the exact volume id in the request, and only when the VMI still reports that id as a HotplugVolume. A NotFound VMI returns success with no detach; any other get error returns Unavailable and retries. It can't detach an unrelated or still-wanted volume, and the namespace is pinned to the infra namespace rather than taken from the request.
  • Least privilege. addvolume on the VMI is deliberately left out. The Role is namespaced and bound to the single -kcsi service account, with nothing aggregating into it. The template is gated off on the proxmox substrate, so the grant renders only where the KubeVirt CSI runs.
  • Tests. The new helm-unittest pins the whole subresources.kubevirt.io rule, and reverting the grant reddens it. The full chart suite is green (343/343) on the branch merged with current main.

One note, out of scope for this PR: unpublishHotplugVolume reads VMI volume status and then removes by name, so a volume detached and reattached under the same name between those two steps is a theoretical race. That code is already merged and isn't in this diff, the window is narrow, and the volume is an infra PVC under the CSI's own teardown, so I'm only flagging it for awareness.

@IvanHunters
IvanHunters merged commit 86bf2ba into main Oct 5, 2026
20 checks passed
@IvanHunters
IvanHunters deleted the fix/kubevirt-csi-rbac-vmi-removevolume branch October 5, 2026 12:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/kubernetes Issues or PRs related to the tenant Kubernetes app kind/bug Categorizes issue or PR as related to a bug size/S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kubevirt-csi controller has no RBAC for the VMI-level removevolume it falls back to

2 participants