-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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: sign kernel modules; add security.enforceModuleSignature option #39286
Conversation
So a question on signatures in Linux kernel- what makes the signature invalid? Does the kernel just have 1 valid signature that it accepts for modules? I have concerns that this could break Nix's substitution logic. |
The kernel only accepts modules signed with the key it was built with. So you cannot use modules from another kernel build. This would need to persist the key across builds, and thus putting it world readable in the store. |
@matthewbauer : since the modules are compressed, I believe nix's substitution logic wouldn't touch them, thus preserving the signatures |
Not sure whether kernel builds are binary reproducible now, but this would prevent them from ever being reproducible. |
Fair point. I had not thought of it. Anyway, for now, kernel builds are not reproducible: build ids contain the date
at least :) Honestly, I would like to avoid making signatures optional, as it would force users to rebuild their kernel themselves (as in this module: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/misc/crashdump.nix#L63-L73 ) which is quite long on not-so-powerful devices. |
@@ -0,0 +1,23 @@ | |||
import ./make-test.nix ({ pkgs, ...} : { |
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 we want this file in release.nix so that it is run by Hydra.
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 just pushed a commit adding it to release.nix.
@GrahamcOfBorg test kernel-signed-modules |
No attempt on aarch64-linux (full log) The following builds were skipped because they don't evaluate on aarch64-linux: tests.kernel-signed-modules Partial log (click to expand)
|
No attempt on x86_64-linux (full log) The following builds were skipped because they don't evaluate on x86_64-linux: tests.kernel-signed-modules Partial log (click to expand)
|
mmh apparently the merge is at fault. I will investigate tomorrow. |
This enables the following kernel options: MODULE_SIG: when a module is loaded, a signature is checked. If it is not present or invalid, then: * if the kernel booted with module.sig_enforce (which is the consequence of setting security.enforceModuleSignature=true) then the module is rejected * otherwise the kernel is just tainted. MODULE_SIG_ALL: when building the kernel, a key is created and modules are automatically signed with it. When the build is finished, we cannot persist the key since this would put it world readable in the store so it is just dropped. As a consequence, *only upstream kernel modules are signed*. Third party modules like zfs cannot be signed and cannot be loaded with security.enforceModuleSignature=true. A nixos test is provided to test that everything works as intended. Reference: https://www.kernel.org/doc/html/v4.10/admin-guide/module-signing.html
A rebase on the current |
@GrahamcOfBorg test kernel-signed-modules |
👎 on this since signing with a random key breaks binary reproducibility (building the kernel twice will produce a different result). |
@symphorien Unless something has broken recently, the timestamp embedded in the kernel is supposed to be |
Success on aarch64-linux (full log) Attempted: tests.kernel-signed-modules Partial log (click to expand)
|
Well, I just checked with a small kernel config (just enough to run nixos tests):
and
|
Failure on x86_64-linux (full log) Attempted: tests.kernel-signed-modules Partial log (click to expand)
|
I think the case of binary reproducibility vs. security is not a necessary obvious choice in favor of reproducibility (well, at least I would rather have module signature than perfect binary reproducibility of the kernel, as a rebuild doesn't take that long when not trusting the builder), and the best way may be to both enable module signature enforcement and “reproducibility modulo a sanitizer” that would remove the signature-specific bits? |
It doesn’t seem too bad for reproducibility since it is optional. I guess I’m not sure what use cases you have for it, though. The binary cache has signatures already and you should be able to trust what you build from source. |
Well, the security advantage of signed modules seems pretty minor without full TPM. E.g. an attacker can just build a new kernel and reboot into that. You can get most of the advantages by disabling module loading post-boot. Also, I don't really believe in the notion that protecting against a compromised root user is a useful thing to do on Linux. You're pretty much screwed at that point anyway. |
@matthewbauer The idea of signing modules is mostly to protect against a compromised root user @edolstra Well, with FDE the attacker can't build a new kernel and reboot into that, as he (normally) doesn't have access to the FDE passphrase. I do agree with you that, currently, this module only brings only a bit of hardening to NixOS. However, I think this is a good step on the path forward to more hardening in NixOS, as protecting against a compromised root user is among quite a number of the steps taken by hardening. I also agree that disabling module loading post-boot gets most of the advantages of using only signed modules, but not all (some use cases require module loading at rather random points in time, eg. plugging a usb wireless card). And currently NixOS is a bit lagging behind on security (necessary reference to NixOS/rfcs#5), so I don't think adding a “starting point” for architectural-level hardening (and encouraging future work in this direction, not only in package-level hardening) would be a bad idea? :) |
Well, with FDE the attacker can't build a new kernel and reboot into that, as he (normally) doesn't have access to the FDE passphrase.
With FDE the attacker that has root can just dump the key from memory, patch all calls to `cryptsetup` in initrd to hardcode the key, and then reboot with the new kernel.
Another problem with kernel module onetime-key signing is that you won't be able to use out-of-tree modules, which, IIRC, all nvidia users do.
|
Well, the point is he can't dump the key from memory if he's root on a hardened kernel. Root only has ring3 access, not ring0 access, and the key resides only in the kernel memory (and IIRC with the TRESOR patch it even resides only in debug registers, not even in the memory). However, if kernel module loading is enabled without enforcing digital signature (which means, on a kernel without this PR), then root can indeed load any module into ring0 and recover the encryption key. Basically, you're right without this PR, but with this PR segregating root from ring0 (and thus from the disk encryption key) becomes at least a possibility, even if it's likely not all paths are closed yet :) As for one-time key signing, indeed that's an issue mentioned in the initial PR message. What could be done for nvidia users (as part of later work, imho) is to make a kernelWithSignedModules derivation that'd take a kernel along with a module set and build a kernel that signs the said signed modules. An alternative (exposed in the initial PR message too) would be to use an out-of-tree key, and have a hydra key for signing all modules built by hydra, and included by default in the kernel -- I'm somehow less convinced with this idea, as in my opinion it loses the benefit of composability NixOS brings, but it'd likely be a level of security equivalent to what eg. RHEL provides, which likely would be enough. |
Well, the point is he can't dump the key from memory if he's root on a hardened kernel.
I though so too before I tried running
dmsetup table --target crypt --showkey /dev/mapper/device
under a hardened kernel. Apparently, this is by design.
the TRESOR patch
If it disables the above, then maybe. But AFAIK TRESOR tries to fight cold boot attacks, not compromised root. And, btw, you can't really win against cold boot without encrypted RAM even with TRESOR.
Also note that compromised root can has all your files from all mounted FDE volumes regardless.
To me signed modules seem more of a DRM feature than a security feature.
|
Oh. Thanks for the So indeed FDE is useless here, but secure boot would still be a way to make sure the kernel is legit, without resorting to the artillery of TPM and measured boot. About root able to have all the files from all mounted volumes regardless, indeed, with the current state of hardening in NixOS ring0 hardening by itself does quite little, but coupled with a RBAC (that NixOS currently doesn't have either) like SELinux, AppArmor or grsecurity RBAC it would allow to effectively restrain root. Sure these RBAC's policy should also block module loading (if it's not configured to allow root to load modules for eg. usb wifi chipsets), but the main principle in hardening is to layer security defenses, so kernel module loading restriction is among the restrictions that I think should be put in place when possible, because it costs very little with a potentially big benefit (ring3 → ring0 escalation blocking) As for the DRM vs security feature, both are quite close, like when grsecurity claimed having been ripped off by an unnamed company for locking users out of devices they owned without respecting the GPL terms. But now we're straying very far off-track, so let's maybe try to recenter the discussion? What I remember of discussion up to now
So the question is whether bit-perfect binary reproducibility is actually a design goal of nixpkgs, and if so whether it is more or less important than hardening. In my mind what is important is reproducibility modulo-useless-stuff, not strict bit-perfect reproducibility. That is, if two built kernels are bitwise identical with the signatures and public keys stripped, then they are “identical enough” for my purposes (that is detecting rogue builders). Having a procedure for handling this kind of almost-bitwise-reproducibility isn't done yet, though. Also, I'm thinking such an almost-bitwise-reproducibility may help with eg. license-keyed software: it'd become possible (well, easier) to just embed the license key and say “when checking for equality, don't consider the license key”. It could take the form of eg. another All that said, I think the benefits in hardening outstrip the loss in bit-perfect reproducibility (because almost-bit-perfect-reproducibility still comes in), but that's just my opinion, and it isn't as strong as I initially thought when I didn't know of the |
SLNOS ML wants me to point out that kernel module signing does make sense at least for one use case: when using trusted/secure/measured boot (firmware checks/measures kernel) but allowing kernel module side-loading (modules can get loaded from unsigned/unmeasured sources) it does make sense to sign kernel modules.
Not that NixOS does any of that. (And I think the setup is pretty strange anyway.)
|
This enables the following kernel options:
MODULE_SIG: when a module is loaded, a signature is checked. If it is
not present or invalid, then:
of setting
security.enforceModuleSignature=true
) then the module isrejected
MODULE_SIG_ALL: when building the kernel, a key is created and modules
are automatically signed with it. When the build is finished, we cannot
persist the key since this would put it world readable in the store so
it is just dropped. As a consequence, only upstream kernel modules are
signed. Third party modules like zfs cannot be signed and cannot be
loaded with
security.enforceModuleSignature=true
.A nixos test is provided to test that everything works as intended.
Reference:
https://www.kernel.org/doc/html/v4.10/admin-guide/module-signing.html
Motivation for this change
Enabling module signature
Alternative designs
require the users to create themselves a key and point us to it in their configuration so that out of tree
modules can be signed
This would be way more complicated, just for the sake of out-of-tree modules users.
Note that tainting the kernel just for having loaded an unsigned module even for users who don't care about this feature is not a drawback since loading an out-of-tree module already entails tainting the kernel.
Things done
Only tested with 4.14 because building a full kernel is quite long, but the feature exists since 4.3 in this
form, so this should be fine. Anyway, there is a test.
build-use-sandbox
innix.conf
on non-NixOS)