OpenZFS CVE-2026-79619: Lock Down /dev/zfs on Proxmox

OpenZFS CVE-2026-79619 lets any local user with /dev/zfs destroy your pools from an unprivileged namespace. Check your version and close the hole today.

Proxmox PulseProxmox Pulse
8 min read
openzfssecuritylxcuser-namespacesudev
Unlocked industrial gate valve with a lone padlock, symbolizing a locked-down device file.

A Fake Root Was Good Enough to Destroy Your Pool

CVE-2026-79619 is the kind of bug that reads worse the longer you look at it. Several OpenZFS authorization checks on Linux accepted a CAP_SYS_ADMIN capability held only inside a user-created, unprivileged namespace as if it were real host root. Any local user who could open /dev/zfs could therefore run pool-administrative operations - zpool create, zpool import, zpool destroy - plus the pool event log and the zinject fault-injection interface, with no prior access to the pool or its backing devices.

The precondition list is short, and a stock Proxmox VE host satisfies all of it out of the box. That is what makes this worth twenty minutes of your evening rather than a note in next quarter's backlog.

Key Takeaways

  • The check was namespace-local. zfs_secpolicy_config() used ns_capable(cr->user_ns, CAP_SYS_ADMIN), which is satisfied by the fake root you get inside unshare -Ur, instead of requiring CAP_SYS_ADMIN in the initial user namespace.
  • /dev/zfs is world-writable by default. OpenZFS ships a udev rule that sets mode 0666 on the control device. Nothing about a default Proxmox install narrows it.
  • Fixed in 2.4.4, 2.3.9 and 2.2.11, all released 21 August 2026. If cat /sys/module/zfs/version reports anything lower on your branch, you are exposed.
  • The researcher validated it on Proxmox VE 8.x running 2.2.10-pve1, alongside TrueNAS SCALE, Unraid and IncusOS - this is not a theoretical port of a FreeBSD finding.
  • Two fixes, not one. Patch the module, then narrow the device permissions so the next authorization bug in this surface has nobody to talk to.

What the Flaw Actually Is

OpenZFS enforces privileged operations through a family of secpolicy_* helpers. The one that gates pool configuration, secpolicy_sys_config(), asked the kernel whether the calling credential holds CAP_SYS_ADMIN in the caller's own user namespace. On Linux, creating a user namespace where you hold every capability is a single unprivileged syscall - that is the entire point of the feature, and it is how rootless containers work.

The correct question is whether the caller holds CAP_SYS_ADMIN in the initial user namespace, the one the host boots into. Asking the wrong question turned a root-only interface into an everyone interface. The same mistake applied to secpolicy_zinject(), which gates the fault-injection path.

Confirm the shape of the problem on your own node. On a stock host:

ls -l /dev/zfs
crw-rw-rw- 1 root root 10, 249 Aug 28 10:37 /dev/zfs

Mode 0666, major 10, minor 249. That is not a Proxmox decision - it comes straight from the shipped udev rule:

grep -n 'KERNEL=="zfs"' /lib/udev/rules.d/90-zfs.rules
10:KERNEL=="zfs", MODE="0666", OPTIONS+="static_node=zfs"

And unprivileged namespace creation is on by default on any Debian-derived kernel:

sysctl kernel.unprivileged_userns_clone
kernel.unprivileged_userns_clone = 1

Three defaults, each individually defensible, combining into a local privilege escalation.

Are You Affected? Three Commands

Run these on every node in the cluster, not just the one you happened to SSH into.

1. The version that is actually loaded.

cat /sys/module/zfs/version

This is the number that matters. It reports the module the running kernel loaded, which is the code enforcing the policy check. The dpkg -l zfsutils-linux version can be ahead of it if the module was never reloaded after an upgrade, and that gap has fooled people into thinking they patched.

Compare against the fixed releases:

Branch Fixed release Released
2.4.x 2.4.4 21 Aug 2026
2.3.x 2.3.9 21 Aug 2026
2.2.x 2.2.11 21 Aug 2026

Proxmox VE 9.2 ships the 2.4 branch; VE 8.x was on 2.2. Either way the rule is the same - match your branch, meet or beat the point release.

2. Who else can reach the device.

ls -l /dev/zfs
stat -c '%a %U:%G' /dev/zfs

3. Which guests were handed it.

grep -l "10:249" /etc/pve/lxc/*.conf

An unprivileged LXC container does not get /dev/zfs by accident; it has to be passed through with an explicit lxc.cgroup2.devices.allow and lxc.mount.entry pair. People do this for containerised backup tooling and ZFS exporters, and those are precisely the containers that turn this bug into a real path to the host. If that grep returns nothing, your containers were never in the blast radius - but every local shell account on the host still was.

Fixing It Properly

Step one: patch the module

The fix is in the Proxmox repositories through the normal ZFS packages. Nothing exotic is required:

apt update
apt full-upgrade

Then reboot. This is the part people try to skip, and it is the part that decides whether you are patched. A new zfs.ko on disk changes nothing while the old module is resident in the running kernel. On a cluster, migrate guests off the node first and take it one node at a time:

ha-manager crm-command node-maintenance enable $(hostname)
# wait for guests to drain, then
reboot

After it comes back, verify - do not assume:

cat /sys/module/zfs/version
zpool status

If you are still on Proxmox VE 8.x, note that the 8.x line reached end of support on 31 August 2026, so you are patching a platform that will not get the next one. That upgrade is now the more urgent piece of work; see our walkthrough of the three sane paths off Proxmox VE 8 before you plan the reboot window, because you may as well do both in a single maintenance slot.

Step two: narrow the device

Patching closes this bug. Restricting /dev/zfs closes the category. There is no reason for a world-writable control device on a hypervisor where every legitimate ZFS consumer - the storage plugin, pvestatd, zfs-zed, replication jobs - already runs as root.

Do not edit the shipped rule; a package upgrade will overwrite it. Add a higher-numbered file so it wins:

cat > /etc/udev/rules.d/91-zfs-restrict.rules <<'EOF'
KERNEL=="zfs", MODE="0600", OPTIONS+="static_node=zfs"
EOF

udevadm control --reload
udevadm trigger

Keeping OPTIONS+="static_node=zfs" matters: it preserves the static device node so the permissions apply from module load, not only after a udev event. Confirm:

ls -l /dev/zfs
crw------- 1 root root 10, 249 Sep  4 09:12 /dev/zfs

Then prove nothing broke, because this is the step with real blast radius:

zpool list
zfs list
systemctl status zfs-zed pvestatd
pvesm status

The things that legitimately break are non-root monitoring exporters reading pool stats, and any container you deliberately gave the device to. If you run a node_exporter or a ZFS metrics collector under its own user, either move it to root or give it a dedicated group and use MODE="0640", GROUP="zfsmon" instead. Decide that before the rule, not after the pager goes off.

Step three: keep it out of containers

If the grep above found container configs, the cleanest fix is to stop passing the device through at all and have the container talk to a host-side helper instead. Where that is not practical yet, deny the major and minor explicitly in the container config:

lxc.cgroup2.devices.deny = c 10:249 rwm

And do not hand CAP_SYS_ADMIN to guests. A container with that capability and a route to /dev/zfs is a host compromise waiting for someone to notice. Our guide to privileged versus unprivileged LXC containers covers where those capability grants tend to creep in.

Why This One Deserves Attention

It is tempting to file a local privilege escalation under "we trust everyone with a shell here." That reasoning gets weaker every year on a hypervisor. A Proxmox host accumulates accounts - backup agents, monitoring users, CI runners with SSH keys, the odd service account someone created for a script in 2023. Any one of them could run unshare -Ur and then zpool destroy the pool holding every VM disk on the node.

There is also no exploit complexity to hide behind. The precondition is opening a device that is world-writable by default, and the capability step is a documented, unprivileged kernel feature. The disclosure came out of research by Erica Windisch, was notified to CERT on 12 August 2026 and disclosed publicly on 16 August. The upstream fix - secpolicy_sys_config: only permit a global zone credential and secpolicy_zinject: only permit a global zone credential - landed in the point releases above. If you want the aggregated metadata, scoring history and reference set in one place, the CVE-2026-79619 record on CVE Explore tracks the advisory alongside the upstream patch links.

Worth noting what this is not: it is not a remote hole, it is not a way into a VM from outside, and it does not give an unprivileged LXC guest a path to the host unless that guest was explicitly given /dev/zfs. Calibrate accordingly - but do run the three commands.

Conclusion

Check cat /sys/module/zfs/version on every node. If it is below 2.4.4, 2.3.9 or 2.2.11 for your branch, upgrade and reboot so the fixed module is the one in memory. Then add the udev rule that takes /dev/zfs down from 0666 to 0600, verify your ZFS tooling still works, and audit any container config that references major:minor 10:249.

That is a twenty-minute job that removes an entire class of local escalation from your storage layer, and it pairs naturally with the reboot you already owe any node still sitting on the end-of-life 8.x line.

Share
Proxmox Pulse

Written by

Proxmox Pulse

Sysadmin-driven guides for getting the most out of Proxmox VE in production and homelab environments.

Related Articles

View all →