Overwrite removed env strings so they do not leak via /proc/PID/environ - #800
Abhinavmadake wants to merge 1 commit into
Conversation
Fixes containers#725 Signed-off-by: Abhinav Madake <abhinavmadake@gmail.com>
d33e97f to
f99cfd6
Compare
|
I don't think bubblewrap ever intended it to be a security guarantee that As a general design principle, I would recommend reducing the scope of sensitive environment variables to be as narrow as possible, and preferring to reference a file containing secrets (for example this is how |
|
@alexlarsson, @cgwalters: do you think this makes sense as hardening, or do you think it's too much complexity? The implementation here looks reasonable to me at first glance. |
|
cc @swick: same question as above ^ |
|
While I do think that we should make the host environment inaccessible for anything inside the sandbox, I'm really not a fan of the implementation here. I'm also not really sure if there is a better way. But IIRC (load bearing if), there is an exec somewhere between the "initial" bwrap exec and it being PID 1 inside the sandbox, so that might be a more reasonable point to get rid of the environment? |
Unfortunately, no. After entering the sandbox, bubblewrap can't re-exec itself, because its executable will not usually be visible inside the sandbox - and even if our executable was made visible, the libraries it depends on will not. In the Flatpak-like use-case with a new pid namespace, we have three processes, something like this: The grandparent (pid 1388669 here) is the original bwrap process, outside the container. The parent (pid 1388684 here) is pid 1 in the container. It is this process's environment variables that @Abhinavmadake is trying to hide/remove. The child (1388685) was forked from what would become pid 1, and then exec'd the final "payload" executable, in this case the gnome-recipes Flatpak app. |
|
In fully-featured container-runners like Docker and Podman, the canonical way to have a pid 1 reaper/init inside the container is to have a statically-linked binary (Podman on Debian uses I'm not sure we really want to be doing that in flatpak/bubblewrap, though? |
Thinking about it, we can't do this generically in bubblewrap, because there is no location that we can guarantee to be able to write: the entire filesystem is under the caller's control, and bubblewrap doesn't reserve any locations for its own use. podman uses |
I was thinking more about an exec while we still have the host mount namespace. Do we not have to do that anyway once for the user namespace to make us root and be able to set up the other namespaces? |
No, we don't have to re-exec to get (limited) privileges: this isn't setuid or setcap. We aren't "root", but after the In the example Also, if we re-exec'd bubblewrap, then all relevant state from "before" would have to be serialized into a pipe/socket/etc. and sent to the new bubblewrap process to de-serialize; so, another non-trivial parser written in C without the benefit of libraries. I don't think we want that! And another problem with re-execing bubblewrap in an empty or minimal environment is that some environment variables might be functionally necessary to run it - for example it might be relying on a If we do want to scrub the environment that's visible in One thing that might be nicer would be:
(But I don't know whether the payload command would be able to bypass this by tracing the pid 1 that is visible to it, and reading its memory...) |
|
Are there any valid use cases for bwrap itself using an environment variable but not passing it to a child? ($PATH perhaps?) IOW what would break if we processed I am uncertain, I think perhaps the simplest immediate guidance to give here is "don't pass sensitive environment variables into bwrap at all, i.e. have the thing calling it filter out unnecessary environment variables first"? |
|
As a completely different approach: could we make pid 1 non-dumpable, so that the payload command can't read its environment? |
It doesn't really matter when we process them, the original environment block is still going to be what's visible at kernel level unless you go to heroic lengths to overwrite it (as is done in this PR). Normally
That's what you need to do with all past and present versions of bubblewrap, regardless of whether we change the behaviour of future versions. #725 indicates that (some) users of bubblewrap were surprised by this. |
I think only the new use of In general bwrap doesn't read environment variables for its own purposes, because historically it was sometimes setuid, therefore it couldn't trust its environment to be non-malicious. |
clearenv()/unsetenv()only drop pointers; the initial strings stay readable in/proc/PID/environ, and bwrap is pid 1 inside the sandbox. Overwrite them once unreferenced. Tests read/proc/1/environunder--unshare-pid.Fixes #725