From: Josef Bacik <josef@toxicpanda.com>
To: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Christian Brauner <brauner@kernel.org>,
James Bottomley <James.Bottomley@hansenpartnership.com>,
Andrew Halaney <andrew@amutable.com>,
Jeremy Bingham <jbingham@gmail.com>,
Tahera Fahimi <taherafahimi@linux.microsoft.com>,
Serge Hallyn <sergeh@kernel.org>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH RFC v3 01/90] Documentation: add a design document for cells
Date: Thu, 05 Nov 2026 18:23:22 +0000 [thread overview]
Message-ID: <20261105-cells-v3-reply-serge-7c41e0b9a6d3@toxicpanda.com> (raw)
In-Reply-To: <auy3kgm1YytennDE@hallyn.com>
On Thu, Nov 05, 2026 at 11:06:26AM -0600, Serge E. Hallyn wrote:
Thanks for going through this. I'm going to answer your last question
first, because the rest follows from it.
> So what is your goal here? You list several properties of the current
> namespaces implementation, and imply that they are a problem. But what
> is the final goal: is it to implement a security sandbox that is
> stronger than what you can currently build from userspace? That's the
> impression I get. Or is it to avoid bugs in userspace, by simplifying
> what userspace needs to do? Knowing the goal will help review the
> patchset.set.
Both. I want a security sandbox that's stronger than what userspace can
build today, and I want the interface to be simple, with fewer footguns
and fewer unexplained consequences that turn into holes.
They're the same thing to me. You're right that you can get a lot of
this out of user namespaces today, you point out one way below. But you
get there by taking things away. You start with a copy of everything the
parent had and a full set of capabilities, and then you have to know
about each thing that needs to be dropped, hidden or locked. A cell
starts as a blank space and you add what you want it to have. If you
forget something with namespaces you have a hole, and nothing tells you.
If you forget something with a cell, the thing doesn't work, and you go
add it.
The document doesn't say any of that up front, which is a pretty big
thing to leave out. I'll fix that.
> > +Privilege hangs off ids.
> > + A user namespace gives its creator a full set of capabilities over everything
> > + the namespace owns,
>
> Yes this was explicitly one of the 4 user namespace design goals,
> and I don't really want code running on my system that I can't
> later control.
You can still control it. A capability you hold in an ancestor user
namespace applies to a cell like it does to any other user namespace, so
root on the host can signal and ptrace the tasks of a cell. The user who
made the cell controls it with the cellfd, and a cell only lives as long
as somebody holds one. When the last one is closed the cell is killed,
so you can't lose track of one and have it keep running.
The only thing that's gone is the special case in cap_capable() where
having the same euid as the creator gets you everything. I don't want a
uid to be the authority.
> > which puts a large amount of code that was written for root
> > + within reach of every user.
>
> You imply that cells don't do this, but the capabilities section below suggests
> otherwise.
[...]
> It seems to me most of the bugs relating to capabilities in user
> namespace were not about the privilege leaking, which you seem to
> suggest here, but rather about kernel code under an ns_capable()
> check being reachable when, without unprivileged user namespaces,
> unprivileged users could not have reached them. Since you open quite
> a few capabilities for instance with net_may(), that code is still
> reachable. So you are probably reducing the reachable code, which
> is good, but the above is over-stating it.
I agree that reachable code is the problem, that's what I was trying to
say and the document says it badly. I'll rewrite it around that.
Here's what is reachable. The user namespace of a cell owns its uts, ipc
and pid namespaces and nothing else. It doesn't own the mount namespace,
so there's no mount(), and a task of a cell can't make any namespace, so
it can't get one that it does own. That's where most of that code is.
The network namespace isn't the cell's either, so CAP_NET_ADMIN in the
cell gets you nothing by itself. It takes the right in the slot as well,
and whoever fills the slot has to pass net_may() for each right on that
network namespace themselves. So an unprivileged user can't use a cell
to get at any of that code unless they could get at it already. Somebody
who is allowed to do it has to hand it over, one right at a time.
So it isn't nothing, you're right, but it's a lot less, and it's opt in.
> > Who may do what to whom is decided by comparing
> > + uids,
>
> It's also decided by namespace targeted capabilities (and LSM
> credentials).
Yup, I'll fix the wording.
> > and whether two uids are the same depends on how several runtimes agreed
> > + to carve up ``/etc/subuid``.
>
> Well, you are adding one more runtime (https://xkcd.com/927/ :) This
> particular problem would be better solved using a subuid NSS module.
Fair :). The difference I care about is who enforces it. /etc/subuid is
an agreement between the tools that read it. Here the kernel hands out
the block, and it makes sure nobody else has those ids: a legacy user
namespace can't map into the window, and a filesystem that isn't the
cell's won't store an id from it. There's nothing to configure and
nothing to agree on.
> Of course userspace could do this now - create a userns, create an mntns,
> set up the mounts, and then create another userns with identity uid mapping,
> so then the container cannot get ns_capable(mnt_ns->userns, CAP_SYS_ADMIN) to
> unmount.
>
> I can envision this being a good strategy for sandbox style runners.
Right, and this is the example I'd use for the above. It works, but you
have to know to do it, and the inner user namespace still comes with a
full set of capabilities, so it can unshare a mount namespace that it
does own and we're back to relying on MNT_LOCKED. You then have to take
that away as well.
> > +A legacy user namespace hands its first task every capability there is. A cell hands
> > +out none.
>
> This seems misleading. It hands any that you want, you just have to specify
> them differently, right?
Right, none unless the builder names them. I'll reword it. The point is
the default.
> Every task? But you do say setuid() drops them as usual. Are you saying
> that the first process is uid 0, and capability inheritance just works
> as usual? If not, then some examples would be helpful - and in that
> case it sounds like this would be an unexpected method of operation
> which would confuse userspace (which can be dangerous).
Yes, it works as usual, the document is sloppy here. A task that is
started in a cell is uid 0 of the cell and has what's in the slot as its
permitted, effective and bounding sets. If you start it as another uid
it loses them, the same as if it had called setresuid(). "Every task"
was meant to say that this goes for each task you start in the cell from
the outside, and not just for the first one.
The two differences are that every task has no_new_privs, and that file
capabilities are ignored. So what a task has let go of doesn't come back
at the next exec, uid 0 or not.
> Also I would continue to be careful about uid mapped filesystems. If
> the host creates a cell with host uid 0 mapped to uid N in the
> cell, can a cell process chmod u+s the file, and collude with an
> unprivileged host process to run it as host-root?
No, on both counts.
Host uid 0 is never in a cell. A cell either has a block of ids out of
the window, or the one id of whoever made it, and root doesn't get to
make one of those, that's EPERM.
But on disk ids are the real problem, since images are stored with ids
from 0 up and the mount is idmapped for the cell, so uid 0 of the cell
does own files that are uid 0 on disk. So nothing a cell writes imparts
privilege:
- S_ISUID, and S_ISGID on anything but a directory, are dropped from
the mode of whatever a task of a cell creates, and from any mode it
sets.
- An existing file loses its set-id bits and its file capabilities as
soon as a task of a cell opens it for writing, or is handed it open
for writing. write() clears them already, a store through a shared
mapping doesn't.
- CAP_SETFCAP can't be put into the caps slot, so a cell can't write a
file capability.
That's the "Files" section of the document and patches 62 to 64, and
there are tests for it. Please do poke at it, this is one of the places
where I'd really like more eyes.
> Also, how does this jive with criu? Is it introducing any cell identifiers
> that can't be recreated? I believe that historically that has been the
> biggest blocker to a "container" like object in the kernel.
Good question, and there is one. cell_info(CELL_SELF) tells a task the
id of its own cell, which comes from a counter and is never reused.
Nothing needs that as far as I can tell, so I think the answer is to not
tell the inside at all.
Other than that I don't think so. On the inside the ids are always 0 to
65535, whichever block the cell got. The rest of cell_info() takes a
cellfd, which only the outside has, and cells don't nest, so there's no
cellfd on the inside to restore. The pid namespace is the cell's, so
clone3() with set_tid should work given CAP_CHECKPOINT_RESTORE in the
slot. What's missing is a way to pick the pid of a task that's started
from the outside.
That's all from reading the code though, I haven't tried criu on one.
I'll add it to the list of what isn't there yet. Thanks,
Josef
next prev parent reply other threads:[~2026-11-05 18:23 UTC|newest]
Thread overview: 93+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 14:56 [PATCH RFC v3 00/90] cells: a container as one kernel object Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 01/90] Documentation: add a design document for cells Josef Bacik
2026-11-05 17:06 ` Serge E. Hallyn
2026-11-05 18:23 ` Josef Bacik [this message]
2026-11-05 14:56 ` [PATCH RFC v3 02/90] Documentation: add examples " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 03/90] cells: reserve a window of ids Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 04/90] user_namespace: let a cell own a user namespace Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 05/90] user_namespace: refuse setns() into a cell Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 06/90] capability: give the owner no say over " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 07/90] ucount: count the id blocks a user holds Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 08/90] user_namespace: let the kernel map the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 09/90] user_namespace: keep legacy maps out of the window Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 10/90] user: add uid_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 11/90] keys: add key_user_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 12/90] fs: add kuid_has_fsmapping() and kgid_has_fsmapping() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 13/90] fs: never store an id that was lent to a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 14/90] fs: take no id of a cell for a mount option Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 15/90] fs: let an idmapping pin the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 16/90] cgroup: let a cgroup become the budget " Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 17/90] cgroup: add cgroup_cell_chown() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 18/90] cgroup: give the budget of a cell back Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 19/90] cgroup: add cgroup_cell_kill() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 20/90] cgroup: root a cgroup namespace at a given cgroup Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 21/90] fs: seal a mount namespace for a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 22/90] nsfs: make open_namespace() available outside of fs/ Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 23/90] nsproxy: build the nsproxy the tasks of a cell share Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 24/90] cells: add the cell object and cell_create() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 25/90] cells: add cell_kill() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 26/90] cells: add cell_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 27/90] cells: add the "budget" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 28/90] cells: add the "root" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 29/90] cells: add the "ids" slot, for a single id Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 30/90] cells: lend a cell a block of ids Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 31/90] cells: reserve the "spawn" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 32/90] cells: add CELL_CMD_SEAL Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 33/90] cells: add cell_open() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 34/90] pid: allocate a struct pid ahead of its numbers Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 35/90] fork: let copy_process() place a task in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 36/90] cgroup: fork a task of a cell into the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 37/90] x86: give a task without an mm no user state Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 38/90] uprobes: cope with a new task that has no mm Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 39/90] fork: add tasks that start out with nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 40/90] pidfd: add PIDFD_EMPTY Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 41/90] pidfd: add pidfd_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 42/90] pidfd: let pidfd_configure() place files Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 43/90] pidfd: let pidfd_configure() set credentials Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 44/90] pidfd: let pidfd_configure() name a cgroup to start in Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 45/90] ptrace: keep everybody off an embryo Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 46/90] cells: end a cell when its init exits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 47/90] cells: refuse a task of a cell a change of namespaces Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 48/90] fs: refuse a task of a cell a new mount namespace Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 49/90] nsproxy: refuse to enter a namespace of a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 50/90] cgroup: keep a task of a cell within the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 51/90] cgroup: let nothing but a spawn into a budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 52/90] kernfs: let a filesystem refuse a change to a node Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 53/90] cgroup: let a cell change nothing outside its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 54/90] cgroup: leave the limits of a budget to who is above Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 55/90] fs: let a filesystem hold ids that go with the cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 56/90] cgroup: let a cell make cgroups below its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 57/90] resctrl: let a cell change nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 58/90] cred: let no id count across cells Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 59/90] ptrace: refuse PTRACE_TRACEME " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 60/90] keys: ignore an equal uid or gid " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 61/90] fs: accept a cellfd for MOUNT_ATTR_IDMAP Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 62/90] fs: let a cell set no set-id bits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 63/90] fs: drop privileges from what a cell may write to Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 64/90] cells: add the "caps" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 65/90] capabilities: take nothing from a file in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 66/90] capabilities: let a cell shrink its bounding sets Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 67/90] af_unix: deliver ancillary data only within a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 68/90] af_unix: tell a sender that control data will not cross Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 69/90] net: do not identify a peer in another cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 70/90] seccomp: let a listener put no file into a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 71/90] umh: run no program for a task of " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 72/90] coredump: let no core dump leave " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 73/90] cells: add the "net" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 74/90] net: give a cell without a network no sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 75/90] net: add net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 76/90] rtnetlink: use net_may() for requests that change state Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 77/90] net: use net_may() in the configuration ioctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 78/90] netfilter: use net_may() for configuration Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 79/90] tun: use net_may() to create and attach to devices Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 80/90] net: use net_may() for the net sysctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 81/90] net: use net_may() for privileged socket options Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 82/90] net: use net_may() for raw and packet sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 83/90] cells: add the "net-rights" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 84/90] net: honour the rights of a cell in net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 85/90] tun: ignore an equal uid or gid across cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 86/90] cells: add cell_info() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 87/90] cells: add CONFIG_CELLS Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 88/90] arch: wire up the cell system calls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 89/90] MAINTAINERS: add an entry for cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 90/90] selftests: add tests " Josef Bacik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105-cells-v3-reply-serge-7c41e0b9a6d3@toxicpanda.com \
--to=josef@toxicpanda.com \
--cc=James.Bottomley@hansenpartnership.com \
--cc=andrew@amutable.com \
--cc=brauner@kernel.org \
--cc=jbingham@gmail.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=sergeh@kernel.org \
--cc=taherafahimi@linux.microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®