-
-
Notifications
You must be signed in to change notification settings - Fork 15.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nixos/security/wrappers: use musl rather than glibc and explicitly unset insecure env vars #259039
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea in general, but I'd like to give a few other folks a chance to take a look. I'd merge in a few days though if there's no response from someone else %)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usage of pkgs.pkgsStatic
is usually frowned upon inside nixpkgs (because it reimports nixpkgs I believe), but this provides us with a way to counteract the vulnerability quickly and maybe perfect this idea on the long run.
I think the musl / static linking part of the patch is a wise idea even in the absence of a current CVE, but I'd like to get this merged fairly quickly given that we currently have an unpatched local privesc vulnerability with ~public exploits. |
We already use pkgsStatic to build busybox-sandbox-shell, which is used by Nix itself, so all NixOS system evaluations already involve pkgsStatic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is reasonable, even without the current exposure. The size difference likely doesn't matter for machines that are able to run NixOS and having less (runtime) dependencies for these things is always nice.
Given that there are already NixOS musl users I am not even concerned this might break use cases.
The thing that is a bit of an unknown to me are unknowns in terms of musl process setups etc.. Not sure if we might be opening up another hole by going this route but the code looks straight forward and I'd consider it a musl bug if we did.
Does musl unset |
The wrappers are 62K each (vs 16K before), so that comes out to 1.3M extra across all 31(!) wrappers on my system. I don't think that really moves the needle.
musl is specifically focused on security and simplicity, and the code is written accordingly. I'm pretty confident it's a better bet than glibc. |
It doesn't look like it. We probably want to cover the full set of variables listed in sysdeps/generic/unsecvars.h, which includes GLIBC_TUNABLES. |
3e1010e
to
b482c5c
Compare
I'm not disputing the fix being applied here, but
I wouldn't be so hasty to come to broad conclusions like this. musl's "simplicity" also means it's missing security features such as an implementation of |
…set insecure env vars This mitigates CVE-2023-4911, crucially without a mass-rebuild. We drop insecure environment variables explicitly, including glibc-specific ones, since musl doesn't do this by default. Change-Id: I591a817e6d4575243937d9ccab51c23a96bed6f9
ca69f63
to
09325d2
Compare
We can have the backport label and wait for enough experience for executing
the backport which was my intention.
The backport label is intent for backport, not backport.
Le ven. 6 oct. 2023 à 00:03, Pierre Bourdon ***@***.***> a
écrit :
… ***@***.**** approved this pull request.
—
Reply to this email directly, view it on GitHub
<#259039 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACMZRDADRA4HJXBHTG4I6LX54VEJAVCNFSM6AAAAAA5TALIWCVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMYTMNRQHAYTKOJQGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
That's a fair point, and makes me feel much more neutral about waiting out the glibc staging-23.05 build without a backport.
As mentioned upthread, we ship |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this is beneficial and don't see any issues after a semi-thorough read (I didn't try to go and see for myself what glibc does on startup that musl doesn't though).
Three potential future ideas:
- Ror most wrappers, we don't need to preserve ~any env vars. We could have a configuration option per wrapper that switches to a mode where only explicitly configured variables are preserved.
- Maybe we could avoid going through exec()? If we could, we could avoid the thrice-damned capability handling across exec() that prevents one from simulating file capabilities. The reason I'm doubtful this is possible is that I don't think that /proc/self/exe ever gets repointed.
- Maybe we could somehow actually simulate file capabilities?
gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html describes every single one of these environment variable aliases as "superseded" by the tunables.
It's not technically a hard guarantee, and certainly doesn't use the word "deprecated" anywhere, but I would be surprised if a new environment variable is proposed and accepted. |
I've only looked at code that pays attention to
I'm very much in favour of this, and had been considering it myself. I'm happy to drop a patch for that soon. Figuring out what env vars we need to whitelist for all of the programs we wrap generates a fair amount of long-tail work and will probably cause a bunch of small breakages, but I think it's worth giving a shot.
The only mechanism I can think of here is a bit cursed: we could operate as a program interpreter ourselves, such that exec doesn't even come into the picture. It'd be a bit of work to make work across architectures, but it's not insurmountable. The program interpreter is just a slightly unusual static binary in essence. It does have to do a little bit of ELF parsing, but the binaries we are wrapping are by definition already entrusted with the privileges the wrapper runs with, so that's not too concerning.
Quoting from myself in a sub-thread:
Relying on an LSM for this core a mechanism isn't ideal, since it'll silently fail if people are running unusual kernels (eg on embedded devices), but getting a bit more serious about making use of available kernel-side security facilities seems like a valuable general effort. |
FWIW (I already mentioned this in the security discussion channel), the advisory also contains this paragraph describing an alternative (and likely simpler) way to exploit this:
What "SUID-root program that setuid(0)s and execve()s" really means is "program that doesn't drop privileges and executes something without triggering AT_SECURE". And our capability wrapper does exactly that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about it more, I think I like this now. It fixes a real issue, and it isn't obviously broken at least. In case unexpected problems do arise, musl can probably cleanly be swapped for a fixed glibc (and the UNSECURE_ENVVARS_TUNABLES
part is probably good to stay in any case).
Backport failed for Please cherry-pick the changes locally. git fetch origin release-23.05
git worktree add -d .worktree/backport-259039-to-release-23.05 origin/release-23.05
cd .worktree/backport-259039-to-release-23.05
git checkout -b backport-259039-to-release-23.05
ancref=$(git merge-base 35502f30abc1b59a793926212f5dfcd907cd1fe6 09325d24b685a3ab5507dc906ab4019696d33d59)
git cherry-pick -x $ancref..09325d24b685a3ab5507dc906ab4019696d33d59 |
// Except for MALLOC_CHECK_ (which is marked SXID_ERASE), these are all | ||
// marked SXID_IGNORE (ignored in secure mode), so even the glibc version | ||
// of this wrapper would leave them intact. | ||
#define UNSECURE_ENVVARS_TUNABLES \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is our process to review the list of new environment variables in future?
Since we no longer use glibc, this is now a manual task now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was already mentioned somewhere in this (long) PR thread, but: this hardcoded list is not expected to grow, since glibc has switched to controlling these things through GLIBC_TUNABLES
instead. And other environment variables are grabbed directly from the glibc source (UNSECURE_ENVVARS).
Ah, OK. I had understood the first sentence in the PR description
at that a plan would be to roll this out also to stable users (thus "without a mass-rebuild").
@edef1c Thanks! |
I looks like this breaks
Interestingly, |
Related: #115363, 76552e9, https://bugs.launchpad.net/ubuntu/+source/gcc-4.4/+bug/503448 Removing I was curious as to whether aarch64 is broken at runtime with this PR, as discussed here: #114953 (comment). It turns out that the binaries are being unintentionally dynamically linked, but they appear to work fine. On aarch64:
On x86_64:
|
On armv6l, we get unintended dynamic linking like on aarch64, but we also get a segfault at runtime.
|
Static executables aren't actually relocated, so PIE flags don't end up super relevant. It would be nice to have ASLR for this code, but it's not a trivial undertaking.
Is this downstream of dynamically-linked musl work maybe? We do actually want to exclude the dynamic linker here. |
I'm not sure what work you are referring to, but I don't think the unintentional dynamic linking is anything new; the same behavior was described in #114953 (comment) in 2021 as a side effect of the PIE hardening. |
Fix in #259509 |
Non- We should probably figure out how to make PIE work on static musl, but it's currently meaningless. |
In an otherwise-abandoned branch of mine I made bintools-wrapper add |
Any curious in the size difference which is IMO whatever:
but the names of the wrappers are now rather long and ugly because we are doing cross compile but that's only cosmetic. |
I just noticed on a friends laptop that he needed to download the final GCC for musl which is ~350 MB just to build the wrappers which is suboptimal and a bit wasteful. |
When we had that discussion a few months back on whether it was fine to require a compiler to build custom wrapper binaries for each wrapper, the assumption was that this shouldn't happen in most cases because wrappers should get cached due to being built for NixOS tests (running on Hydra) already. I'm kind of surprised that this isn't the case. Which wrapper is causing this? It should get tests added. |
I can't reproduce it anymore but I think it could be that those wrappers are not a blocker for the channel advancement and then if you are an early bird, they are not cached, yet. |
We could also get rid of musl and use nolibc from the linux kernel :), just like I did for nix-ld. There is also still https://github.com/NixOS/nixpkgs/pull/201536/files which doesn't require the setuid binary to be recompiled at all. |
Description of changes
This mitigates CVE-2023-4911, crucially without a mass-rebuild.
See #258972 for the glibc upgrade that fixes the underlying vulnerability.
Things done
./result/bin/
)