Restrict rpool import to rootDisks
Nix Flake Check / check (push) Successful in 2m49s

This commit is contained in:
2026-10-10 20:37:10 +00:00
parent 8719f8d49b
commit 4ec189a5a4
2 changed files with 39 additions and 11 deletions
+11 -9
View File
@@ -154,17 +154,19 @@ take `_:` as their argument). The script requires signed commits
ashift, xattr=sa, autoexpand, reservation, `chattr +i`). Consult it before
changing ZFS defaults.
- `modules/zfs.nix` also takes a `rootDisks ? [ ]` module argument (same style as
`boot.nix`) of full paths to whole disks or symlinks to them, any naming scheme
the host prefers (`by-path`, `by-id`, `by-uuid`, `/dev/sda`) — deliberately no
validation of the format. When non-empty a `zfs-rpool-links` oneshot (in the
initrd and in stage 2, with a `postDeviceCommands` fallback when initrd systemd
is off) symlinks them into `/dev/disk/rpool` numbered `1..N` like `/boot1
/boot2` in `boot.nix`, and `boot.zfs.devNodes` points there, so `zpool import -d`
can never see a foreign pool with the same name.
`boot.nix`) of full paths to whole disks. Only entries starting with
`/dev/disk/by-path/` are used: the `ID_PATH` is parsed out of each and turned
into udev rules — in stage 2 and in the initrd — symlinking every partition of
the matching disk into `/dev/disk/rpool`, numbered `1..N` like `/boot1 /boot2`
in `boot.nix`. Whole disks are deliberately not linked: UEFI requires a boot
partition, so the root pool always lives on a partition. When such entries exist
`boot.zfs.devNodes` points there, so `zpool import -d` can never see a foreign
pool with the same name.
`boot.zfs.devNodes` is a _directory_ (passed to `-d`), not a device list, and
`forceImportRoot` does not resolve duplicate pool names — it only overrides
in-use/dirty checks. A wrong `rootDisks` entry makes the disk invisible to the
import, so the host will not boot.
in-use/dirty checks. A `rootDisks` entry in another naming scheme (`by-id`,
`by-uuid`, `/dev/sda`) is silently ignored, and a wrong `by-path` entry makes
the disk invisible to the import, so the host will not boot.
- `modules/telegraf.nix` intentionally leaves `outputs.prometheus_client`
without a listen port (secure by default; hosts put it behind a reverse proxy).
Don't add a port here.
+28 -2
View File
@@ -85,7 +85,28 @@
% zfs get all -s local -t filesystem -r "${POOLNAME?}"
*/
{ lib, pkgs, ... }:
{
lib,
pkgs,
rootDisks ? [ ],
...
}:
let
byPathPrefix = "/dev/disk/by-path/";
# Only by-path names carry enough information to build a udev rule, so other
# naming schemes are ignored. Each entry names a whole disk; the root pool is
# assumed to live on a partition of it, because UEFI needs a separate boot
# partition anyway.
rootPaths = map (lib.removePrefix byPathPrefix) (lib.filter (lib.hasPrefix byPathPrefix) rootDisks);
hasRootPaths = rootPaths != [ ];
rpoolRules = ''
# Partitions inherit ID_PATH from the parent disk.
SUBSYSTEM=="block", ENV{ID_PATH}=="", ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_PATH"
''
+ lib.concatImapStrings (index: path: ''
SUBSYSTEM=="block", ENV{ID_PATH}=="${path}", ENV{DEVTYPE}=="partition", SYMLINK+="disk/rpool/${toString index}-part%n"
'') rootPaths;
in
{
services.zfs = {
trim = {
@@ -126,9 +147,14 @@
};
};
# Restrict the "rpool" import path to rootDisks, if specified. This prevents
# boot failures when a foreign disk containing ZFS pool "rpool" is inserted.
services.udev.extraRules = lib.mkIf hasRootPaths rpoolRules;
boot.initrd.services.udev.rules = lib.mkIf hasRootPaths rpoolRules;
# https://openzfs.github.io/openzfs-docs/Project%20and%20Community/FAQ.html#selecting-dev-names-when-creating-a-pool-linux
# Default /dev/disk/by-id doesn't work on VM, but /dev/disk/by-uuid could.
boot.zfs.devNodes = lib.mkDefault "/dev/disk/by-path";
boot.zfs.devNodes = lib.mkDefault (if hasRootPaths then "/dev/disk/rpool" else "/dev/disk/by-path");
# We set forceImportRoot to true because our servers have exclusive access to
# their local disks (no SANs/iSCSI). This guarantees they can recover and boot
# unattended after a sudden power loss or hard crash, instead of hanging