Re: [PATCH 0/8] userns: per-namespace capability bounding set and opt-in netns loopback

From: Hallgrimur H. Gunnarsson

Date: Thu Nov 05 2026 - 11:01:10 EST


On Tue, Nov 03, 2026 at 06:37:46AM -0600, Serge E. Hallyn wrote:
> 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)

a container that is explicitly granted CAP_NET_ADMIN in its own user
namespace automatically propagates that capability into the bounding
set of any child namespace it creates (unless it is explicitly
dropped).

3. Dropping capabilities for nested namespaces: If a container with
CAP_NET_ADMIN needs to spawn an inner user namespace to run untrusted
code in a nested sandbox, it can use CLONE_NEWUSER_BSET to voluntarily
drop CAP_NET_ADMIN from the inner namespace's bounding set. This
guarantees the nested sandbox cannot access the capability and the
restriction persists for all further descendants.

As a quick clarification on how this interacts with the hierarchy: the
bounding set restricts tasks inside the namespace and parent tasks
relying purely on the parent-ownership check. It does not restrict
tasks in an ancestor userns that inherently hold the capability.
Therefore, in the nested sandbox scenario above (#3), the parent
container with CAP_NET_ADMIN can still configure the sandbox's network
from the outside (by using setns() to enter the nested netns directly)
even though CAP_NET_ADMIN is stripped inside the sandbox userns.