On Thu, Nov 5, 2026 at 7:48 AM Hallgrimur H. Gunnarsson [off-list ref] wrote:
On Tue, Nov 03, 2026 at 06:37:46AM -0600, Serge E. Hallyn wrote:
quoted
Generally, if I, or some users I support, need to use unprivileged containers
or user namespaces, then when an 0day comes along, I can hopefully trust them
not to exploit it. The biggest danger comes from the fact that, right now, if
they can use them, anyone can. So if someone can trigger some other code running
as user nginx or something, that can become a root vector.
So what if we had a built-in feature or LSM which implemented a credential based
whitelist, entered at firstboot? Something like
# Default deny
uid == 1000 # approved
1005 < uid < 1010 # approved
gid == 1005
security_label = lxc_user_t # Not sure how far we want to go with this
tty # must have a session
Now, if you want to use user namespaces for generic sandboxes for all
users, e.g. to confine browser activity or nginx modules, then this model
may not fit. Or rather, it might need a few tweaks.
Thanks for taking a look and sharing the credential-based allowlist.
The latter scenario (generic sandboxes for all users) is actually the
primary use case that I'm targeting. I want to allow unprivileged users to
create user namespaces, e.g. for sandboxing, but without granting them
security-sensitive network capabilities (CAP_NET_ADMIN/CAP_NET_RAW) when
they are not strictly needed, given the historically high risk of local
privilege escalation associated with them.
The two approaches target complementary layers of the problem. A credential
allowlist at creation time controls who can create a user namespace, while
this patch series controls what capabilities are available inside that
namespace (and its descendants) once it is created.
Gating creation works well when there is a clear boundary between trusted
and untrusted users. However, in a multi-tenant environment (like a large
container fleet), we generally treat workloads as untrusted by default.
Because we still want to allow these untrusted workloads to use unprivileged
user namespaces for sandboxing, it is essential to keep the attack surface
small and follow the principle of least privilege whenever possible.
More specifically, this patch series enables that model of safe sandboxing
across three key use cases:
1. Configurable capability stripping: Administrators can enable unprivileged
user namespaces for all users to support sandboxing, but configure the
system to strip CAP_NET_ADMIN and CAP_NET_RAW by default in new user
namespaces created by unprivileged users. Basic sandboxing rarely needs
these (except to bring up loopback, which Patch 1 addresses via
CLONE_NEWNET_LOOPBACK).
The per-namespace bounding set can enforce this either globally via the
sysctl, or explicitly via clone3() at the container manager level. This
restriction is monotonic and persists down the namespace hierarchy.
2. Selectively granting CAP_NET_ADMIN: Some containerized workloads
legitimately need CAP_NET_ADMIN to configure advanced networking for
sub-containers. Because an unprivileged creator's child namespace is
bounded by:
parent_bset & (sysctl_ceiling | creator_effective)
Right after sending my last email, I had the following thought of a
variant of the model. Permission to gain CAP_NET_ADMIN via creating a
userns is kind of like a capability itself, right? We could get
pretty close to nice semantics if we allocated a capability bit for
this (CAP_USERNS_NET_ADMIN perhaps?) and, by default, giving it to all
users. Unfortunately, the latter part is tricky because even sticking
it in the ambient set won't keep it from being dropped by setuid.
Perhaps we could have an extra capability mask that is for default-on
capabilities.
The intuition for this is that permission to gain capabilities in a
userns is a mess if it's a sysctl (as sysctls are global and there is
no clean way to say that a particular unprivileged task may
nonetheless create namespaces with CAP_NET_ADMIN) and it's a mess if
it's a securebit (still has the latter problem), but it's much tidier
if it's tied to a task.