Skip to content
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: support systemd-veritysetup-generator #305999

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

msanft
Copy link
Contributor

@msanft msanft commented Apr 22, 2024

Description of changes

This adds support for using verity-protected root filesystems on NixOS, implemented by systemd-veritysetup-generator. This generator looks for verity partitions (e.g. as created by systemd-repart) and mounts them automatically. It acts as an alternative to the other generators supported at the moment. (Namely systemd-gpt-auto-generator and systemd-fstab-generator).

The tentative API looks as follows:

config.boot.initrd.systemd.root = "verity" # This option specifies how root should be mounted at all.
config.boot.initrd.systemd.verityRootHash = "..." # This option specifies the root hash of the verity partition, which systemd uses to identify the correct device to mount.

I'm not sure yet whether this API is good. I couldn't think of anything else you'd need to have configurability of yet, but I'm happy to get some feedback on this.

Closes #285254

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@msanft
Copy link
Contributor Author

msanft commented Apr 22, 2024

@ofborg test verity-protected-root

@flokli
Copy link
Contributor

flokli commented Apr 23, 2024

This looks like a nice way into the right direction! Also thanks for writing a test for it ❤️ .

Copy link
Contributor

@nikstur nikstur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool that you're interested in dm-verity protected NixOS. That's something I've been working on for while as well!

My biggest issue with this PR is that it's all very entangled with the root kernel command line parameter while it doesn't have to be and probably shouldn't be.

Instead of adding another variant to the root enum, I believe we should add a separate module that you can enable. See https://github.com/nikstur/nix-store-veritysetup-generator/blob/main/nix/modules/verity.nix

If you really want a dm-verity protected root, you can then:

  • enable this module
  • set the roothash= kernel commandline
  • and set fileSystems."/" to use /dev/mapper/root and whichever fs options (e.g. ro) you need.

That's all that should be needed and still allows to use dm-verity differently, e.g. to protect /usr or /nix/store with nix-store-veritysetup-generator

IMO the test shows (by it's sheer complexity), how ill-suited a verity protected root fs is for NixOS. We'd get the same (or better) security properties if we just protect the Nix store and use some form of impermanence for the root. The simplest form of impermanence would be a tmpfs. What I've already tested and which works remarkable well is to mount the Nix store under /usr/nix/store and then bind mount it to /nix/store. Then you can just use the systemd veritysetup-generator.

# For NixOS activation to succeed, we need to have root writable in initrd. So unless root
# is verity-protected (and thus read-only), where the user will have to take care of putting
# overlay mounts in place, let's add "rw" to the kernel command line.
++ lib.optional (config.boot.initrd.systemd.root == "gpt-auto" && cfg.root != "verity") "rw";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the module system already enfore that this can't be both gpt-auto and verity because the option is an enum?

Comment on lines +389 to +391
{
assertion = cfg.root == "verity" -> !(builtins.hasAttr "/" config.fileSystems);
message = "The `fileSystems.\"/\"` option should not be set when `root` is set to `verity`.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. The systemd-veritysetup-generator only creates oneshot units that call systemd-veritysetup. It doesn't itself create any mount units. Only systemd-gpt-auto-generator or systemd-fstab-generator do that. You should be able to use the veritysetup-generator while still providing your own mount configs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's not the case when using roothash.

However, I'm more and more unsure about whether roothash is the thing to do, as it also requires a DDI. If we used systemd.verity_root_data=, systemd.verity_root_hash=, we could probably offer a universal interface that doesn't mount anything, see this Matrix discussion

@@ -211,7 +216,7 @@ in {
};

root = lib.mkOption {
type = lib.types.enum [ "fstab" "gpt-auto" ];
type = lib.types.enum [ "fstab" "gpt-auto" "verity" ];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this is the right abstraction. You still need an fstab or gpt-auto for this to work because systemd-veritysetup-generator only opens dm devices. It doesn't mount anything and indeed you could configure your mounts with either the fstab or gpt-auto when you use dm-verity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does mount stuff (see roothash in https://www.freedesktop.org/software/systemd/man/latest/systemd-veritysetup-generator.html), and it's incompatible with both root=gpt-auto and root=fstab unfortunately. I'd very much liked the latter to not be the case, but it is.

Re "automatically mounting", also see this Matrix discussion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supportedFilesystems = [ "erofs" ];

# This adds some udev rules that are required for dm-verity to work.
services.lvm.enable = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it odd that you need to enable this yourself. This should be enabled when you enable dm-verity support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree. But looking around, I think it's uncommon to have modules enable things in other modules. Is there some kind of agreement around this?

@nikstur nikstur requested a review from WilliButz April 24, 2024 08:36
@msanft
Copy link
Contributor Author

msanft commented Apr 24, 2024

IMO the test shows (by it's sheer complexity), how ill-suited a verity protected root fs is for NixOS. We'd get the same (or better) security properties if we just protect the Nix store and use some form of impermanence for the root. The simplest form of impermanence would be a tmpfs. What I've already tested and which works remarkable well is to mount the Nix store under /usr/nix/store and then bind mount it to /nix/store. Then you can just use the systemd veritysetup-generator.

For desktops, or more volatile use-cases (say image-based Linux with A/B-boot), I agree that it's way too complex currently. But for immutable image-based servers, I think this isn't a problem. And for that, at least in my case, I just don't want a writable root at all. I want to specifically enable where I can write to (e.g. /var, ...) with a whitelist, instead of allowing everything. This way, I can take notice of the stuff that doesn't behave correctly more strictly.

My biggest issue with this PR is that it's all very entangled with the root kernel command line parameter while it doesn't have to be and probably shouldn't be.

I agree more or less, but unfortunately, using systemd-veritysetup-generator with roothash doesn't work with that, as it's incompatible with the other root options. I think we might want to explore the other API I've mentioned above here.

@nbraud
Copy link
Contributor

nbraud commented May 6, 2024

@msanft The comments, option values, etc. should probably say “dm-verity” rather than just “verity,” as there's another filesystem integrity feature in Linux called “fs-verity”... and I've been working on integrating that, at least within my config repo

(my goal being to extend the guarantees from Secure Boop to the whole system environment, and have the fs-verity root metadata be part of boot measurement... which your PR also achieves, fs- and dm-verity just have different tradeoffs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

/ does not need to be mounted if roothash is specified in the kernel command line
6 participants