Vivian Voss

Rootless Containers Cost Root

like for like freebsd jails rootless containers

The line is user.max_user_namespaces = 0, and in the last week of October 2026 it turned up in vendor advisories, CloudLinux's and TuxCare's among them, as the first thing to type. CloudLinux put it plainly: "Mitigation: set user.max_user_namespaces to 0. It applies without a reboot and stops rootless containers for ordinary accounts." The reason was ZDI-CAN-33367, a kernel flaw that, in the same advisory's words, "lets any local user rewrite files they can only read, which leads to root". Its exploit was public before it had a CVE, because the Zero Day Initiative had sent its reports to the kernel security team on 20 and 21 October and, "by mistake, to public mailing lists as well".

As it happens, the line is older than the flaw. Debian printed it in the release notes of Debian 11, in 2021, for anyone who preferred to keep the old default, and the same paragraph explained why the old default had existed in the first place.

Why anyone set it to anything else

A user namespace lets an ordinary account be root inside a small world of its own. Rootless Podman is built on that, and its tutorial says so with some pride: "Rootless Podman is not, and will never be, root; it's not a setuid binary, and gains no privileges when it runs. Instead, Podman makes use of a user namespace to shift the UIDs and GIDs". Set against Docker's daemon, which runs as root on the host and holds every container in its hand, that is a real improvement, and the price of it took a few years to show. Browsers wanted the same thing for their sandboxes, and so did Flatpak.

Debian weighed the bargain in public and wrote the weighing down. Its release notes for Debian 11 read: "The previous Debian default was to restrict this feature to processes running as root, because it exposed more security issues in the kernel. However, as the implementation of this feature has matured, we are now confident that the risk of enabling it is outweighed by the security benefits it provides." Until then, the bubblewrap package explains, the feature had been "disabled by default to reduce the kernel's attack surface". From Linux 5.10 on, Debian let every account create one, and the attack surface came along with the permission.

Who may build an isolated world March 2000 FreeBSD 4.0: jails, made by root Linux 3.8 any process may create a user namespace 2021 Debian 11 opens them to every account October 2023 Ubuntu 23.10 starts restricting them again October 2026 advice: max_user_namespaces = 0

That was the bargain. Every user got a little more of the kernel, and in return a container no longer needed root to start.

What the line shuts

The manual page describes what an account receives when it creates a user namespace. The first process in it "gains a full set of capabilities in that namespace". Those capabilities are meant to count only inside the namespace, and for many operations they do. The kernel code they unlock, however, is the one kernel every process on the machine shares: the networking stack, the packet filter, the traffic shaper, a fair stretch of the filesystem code. Code that was written for a caller with root on the host now answers to any account that has typed unshare.

Canonical described it better than any critic, in October 2023, when Ubuntu began to restrict the feature again: unprivileged user namespaces "expose kernel interfaces that are normally restricted to processes with privileged capabilities (root) to use by unprivileged users". The same post cites a report from Google in which "44% of the exploits they saw required unprivileged user namespaces as part of their exploit chain".

ZDI-CAN-33367 is this week's example. AlmaLinux's notice says the published proof of concept "runs as an ordinary user with no capabilities", using a namespace the distribution permits by default to set up what it needs. CloudLinux's kernel team confirmed root with it on Debian 12 and 13, on Ubuntu 24.04 and 26.04 and on AlmaLinux 8 to 10, and then on its own CloudLinux 9 and 10 (nine releases in all, or seven, since CloudLinux ships AlmaLinux's kernels and counting both is rather generous to the exploit). Ubuntu's AppArmor restriction did not help either: TuxCare found that the exploit "gets around it by launching through the AppArmor profile of a container launcher package that's allowed to create user namespaces". The tooling that makes rootless containers convenient was, in that case, the way round the fence put up to protect them. A fix was posted to the netdev list on 22 October; at the time of writing it has not been merged.

I wanted to know where the kernel decides what a namespace's root may do, and counted on 27 October 2026 in Linux 7.3, the mainline release of 18 October. The kernel asks the question through a family of helpers (ns_capable(), netlink_net_capable(), sk_ns_capable() and their relatives), and outside the file that defines them they are called 298 times in 129 source files, 145 of those calls in the networking code alone. Each call is a separate judgement by a separate maintainer that the operation behind it is safe for a root who is only root inside a namespace. Most of those judgements are sound. A user namespace makes every one of them reachable by every account on the machine, so one unsound judgement is enough.

Linux 7.3, rootless: what stands between an account and the kernel ordinary account no privileges on the host its own user namespace "a full set of capabilities in that namespace" 298 checks in 129 files, 145 in networking each decides on its own whether a namespace's root may proceed the one kernel every process shares networking stack, packet filter, traffic control, filesystems Own count, 27 October 2026, Linux 7.3 source tree

A fence that only takes away

FreeBSD reached the same problem from the other side, twenty-six years ago. Poul-Henning Kamp and Robert Watson called their paper on jails "Jails: Confining the omnipotent root", and FreeBSD 4.0, released in March 2000, was the first version to carry the code. The premise is in the title. Root inside a jail is not trusted, and so every rule a jail brings takes something away from root.

That premise survives in one function. prison_priv_check() in sys/kern/kern_jail.c of FreeBSD 15.1 begins with a comment that could stand as the design document: "We have a specific list of accepted privileges; the rest are denied." I counted that list on 27 October 2026. sys/sys/priv.h defines 244 privileges. A jail without a network stack of its own lets its root keep at most 78 of them, and a number of those only when the administrator has set a named switch; jail(8) says that with two exceptions "these boolean parameters are off by default". A jail with its own network stack, a VNET jail, adds 51 network privileges to the list, and creating one is again a job for root. Loading a kernel module is on no list at all, and neither is writing to kernel memory.

FreeBSD 15.1: what root keeps inside a jail 78 of 244 privileges 78 kept listed in prison_priv_check(), some only behind allow switches, off by default 166 denied everything not on the list, kernel modules and kernel memory among them +51 in a VNET jail network privileges, and only root creates one Own count, 27 October 2026, FreeBSD releng/15.1

Creating a jail is a privilege too, and an ordinary account does not hold it. jail(2) fails with EPERM, its manual page says, "because it is not the super-user". So the question this week's advisories asked Linux administrators to settle with a sysctl has no counterpart on a FreeBSD server. An account without privileges reaches the kernel through the same interfaces it reached yesterday, and nothing it can create widens them.

The same job looks like this on both systems. A developer on a shared server wants an isolated web service. On Ubuntu that is podman run from the developer's own account, and the kernel underneath has just handed that account a set of capabilities. On FreeBSD the administrator writes the jail once, in /etc/jail.conf:

web {
    path = "/jails/web";
    host.hostname = "web";
    ip4.addr = "lo1|192.0.2.10";
    mount.devfs;
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
}

service jail start web brings it up, and the developer works inside it with an ordinary account. What the developer may manage beyond that is delegated right by right: zfs allow hands out snapshot and rollback on the jail's dataset, and mac_do(4), which according to its manual page "allows unprivileged users to change process credentials according to rules configured by the administrator", does so per jail. Nobody involved is root in a namespace of their own.

                                Linux 7.3, rootless             FreeBSD 15.1, jail
who creates the isolation       any account                     root on the host
what its root holds             a full set of capabilities      at most 78 of 244 privileges
where that is decided           298 calls in 129 files          one function, one list
network administration inside   yes, in its own namespace       only in a VNET jail root made
this week's sysctl              user.max_user_namespaces = 0    nothing to set

Counted on 27 October 2026 from the Linux 7.3 source tree on kernel.org and the releng/15.1 branch of the FreeBSD source tree.

The limit

No ordinary account starts its own jail. A developer who wants a fresh isolated environment at three in the afternoon asks the administrator, or uses the rights the administrator delegated. Where the requirement is that any user may start containers at will, FreeBSD does not offer it, and on purpose.

Both jail escapes of 2026 needed the administrator's consent. FreeBSD-SA-26:02.jail and FreeBSD-SA-26:04.jail, both fixed this year, let a jailed process reach the host's filesystem, and each needs a configuration the administrator chose: the first a jail permitted to mount nullfs, and then only for root inside it, the second two jails sharing a directory through nullfs. Neither is reachable by an account on the host that holds no privileges.

The count measures call sites. Each of the 298 is a place where someone decided carefully, and the decisions are, nearly always, spot on. The figure measures how widely the question is spread, which is what a user namespace exposes.

Linux can close the door, at the price of what it was opened for. CloudLinux closes it by default on its newer installations, and the one sysctl closes it anywhere. CloudLinux names the cost, and Debian adds that "various desktop and container features will not work with this restriction in place, including web browsers".

The vendors' advice this week amounts to running a Linux server, for a while, the way a FreeBSD server has always run.

On FreeBSD there is no line to type, and the jails kept working throughout.