Revert "nixos/initrd: refactor secrets option into secretPaths and extraSecretsHook"

This commit is contained in:
Vladimír Čunát
2026-03-21 15:56:28 +01:00
parent f88ce72ccc
commit d6acf1b543
20 changed files with 86 additions and 229 deletions

View File

@@ -247,8 +247,6 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.
- `services.caddy` now supports setting `httpPort` and `httpsPort` and opening them in the firewall via `openFirewall`.
- `boot.initrd.secrets` is now deprecated in favour of `boot.initrd.secretPaths` and `boot.initrd.extraSecretsHook`.
- The latest available version of Nextcloud is v33 (available as `pkgs.nextcloud33`). The installation logic is as follows:
- If [`services.nextcloud.package`](#opt-services.nextcloud.package) is specified explicitly, this package will be installed (**recommended**)
- If [`system.stateVersion`](#opt-system.stateVersion) is >=26.05, `pkgs.nextcloud33` will be installed by default.

View File

@@ -86,7 +86,7 @@ in
description = ''
Append an additional file's contents to `/etc/iscsid.conf`. Use a non-store path
and store passwords in this file. Note: the file specified here must be available
in the initrd, see: `boot.initrd.secretPaths`.
in the initrd, see: `boot.initrd.secrets`.
'';
default = null;
type = nullOr str;

View File

@@ -14,9 +14,7 @@ let
children = lib.mapAttrs (
childName: childConfig: childConfig.configuration.system.build.toplevel
) config.specialisation;
hasInitrdSecrets =
(lib.length (lib.attrNames config.boot.initrd.secretPaths) > 0)
|| (config.boot.initrd.extraSecretsHook != "");
hasAtLeastOneInitrdSecret = lib.length (lib.attrNames config.boot.initrd.secrets) > 0;
schemas = {
v1 = rec {
filename = "boot.json";
@@ -35,7 +33,7 @@ let
// lib.optionalAttrs config.boot.initrd.enable {
initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
}
// lib.optionalAttrs hasInitrdSecrets {
// lib.optionalAttrs hasAtLeastOneInitrdSecret {
initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets";
};
}

View File

@@ -99,8 +99,8 @@ in
sed -i $out/bin/clevis-decrypt-tpm2 -e 's,tpm2_,tpm2 ,'
'';
secretPaths = lib.mapAttrs' (
name: value: lib.nameValuePair "/etc/clevis/${name}.jwe" { source = value.secretFile; }
secrets = lib.mapAttrs' (
name: value: lib.nameValuePair "/etc/clevis/${name}.jwe" value.secretFile
) cfg.devices;
systemd = {

View File

@@ -29,7 +29,7 @@ in
};
boot.initrd.network.openvpn.configuration = mkOption {
type = types.path; # Same type as boot.initrd.secretPaths.*.source
type = types.path; # Same type as boot.initrd.secrets
description = ''
The configuration file for OpenVPN.
@@ -74,8 +74,8 @@ in
"${pkgs.glibc}/lib/libnss_dns.so.2"
];
boot.initrd.secretPaths = {
"/etc/initrd.ovpn".source = cfg.configuration;
boot.initrd.secrets = {
"/etc/initrd.ovpn" = cfg.configuration;
};
# openvpn --version would exit with 1 instead of 0

View File

@@ -302,8 +302,8 @@ in
fi
'';
boot.initrd.secretPaths = listToAttrs (
map (path: nameValuePair (initrdKeyPath path) { source = path; }) cfg.hostKeys
boot.initrd.secrets = listToAttrs (
map (path: nameValuePair (initrdKeyPath path) path) cfg.hostKeys
);
# Systemd initrd stuff

View File

@@ -414,7 +414,7 @@ in
ln -s ${initrdPath} $out/initrd
${optionalString (config.boot.initrd.secretPaths != { }) ''
${optionalString (config.boot.initrd.secrets != { }) ''
ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
''}

View File

@@ -935,21 +935,21 @@ in
'')
(mkRemovedOptionModule [ "boot" "loader" "grub" "extraInitrd" ] ''
This option has been replaced with the bootloader agnostic
boot.initrd.secretPaths option. To migrate to the initrd secrets system,
boot.initrd.secrets option. To migrate to the initrd secrets system,
extract the extraInitrd archive into your main filesystem:
# zcat /boot/extra_initramfs.gz | cpio -idvmD /etc/secrets/initrd
/path/to/secret1
/path/to/secret2
then replace boot.loader.grub.extraInitrd with boot.initrd.secretPaths:
then replace boot.loader.grub.extraInitrd with boot.initrd.secrets:
boot.initrd.secretPaths = {
"/path/to/secret1".source = "/etc/secrets/initrd/path/to/secret1";
"/path/to/secret2".source = "/etc/secrets/initrd/path/to/secret2";
boot.initrd.secrets = {
"/path/to/secret1" = "/etc/secrets/initrd/path/to/secret1";
"/path/to/secret2" = "/etc/secrets/initrd/path/to/secret2";
};
See the boot.initrd.secretPaths option documentation for more information.
See the boot.initrd.secrets option documentation for more information.
'')
];

View File

@@ -482,7 +482,7 @@ sub addEntry {
die "failed to create initrd secrets $!\n";
} else {
say STDERR "warning: failed to create initrd secrets for \"$name\", an older generation";
say STDERR "note: this is normal after having modified or removed an entry in `boot.initrd.secretPaths`";
say STDERR "note: this is normal after having removed or renamed a file in `boot.initrd.secrets`";
}
}
# Check whether any secrets were actually added

View File

@@ -202,7 +202,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
print("warning: failed to create initrd secrets "
f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr)
print("note: this is normal after having removed "
"or modified an entry in `boot.initrd.secretPaths`", file=sys.stderr)
"or renamed a file in `boot.initrd.secrets`", file=sys.stderr)
entry_file = BOOT_MOUNT_POINT / "loader/entries" / generation_conf_filename(profile, generation, specialisation)
tmp_path = entry_file.with_suffix(".tmp")
kernel_params = "init=%s " % bootspec.init

View File

@@ -148,22 +148,23 @@ let
# Copy secrets if needed.
#
# TODO: move out to a separate script; see #85000.
${optionalString (!config.boot.loader.supportsInitrdSecrets) ''
${concatStringsSep "\n" (
mapAttrsToList (_: scfg: ''
mkdir -p $(dirname "$out/secrets${scfg.path}")
# Some programs (e.g. ssh) doesn't like secrets to be
# symlinks, so we use `cp -L` here to match the
# behaviour when secrets are natively supported.
# The assertion further up in this file (stage-1.nix)
# checks that all secretPaths are Nix store paths set via
# boot.initrd.secretPaths.*.source if the bootloader doesn't
# support initrd secrets.
cp -Lr ${scfg.source} "$out/secrets${scfg.path}"
'') config.boot.initrd.secretPaths
)}
${config.boot.initrd.extraSecretsHook}
''}
${optionalString (!config.boot.loader.supportsInitrdSecrets) (
concatStringsSep "\n" (
mapAttrsToList (
dest: source:
let
source' = if source == null then dest else source;
in
''
mkdir -p $(dirname "$out/secrets/${dest}")
# Some programs (e.g. ssh) doesn't like secrets to be
# symlinks, so we use `cp -L` here to match the
# behaviour when secrets are natively supported.
cp -Lr ${source'} "$out/secrets/${dest}"
''
) config.boot.initrd.secrets
)
)}
${config.boot.initrd.extraUtilsCommands}
@@ -435,9 +436,7 @@ let
exit 0
fi
${lib.optionalString (
config.boot.initrd.secretPaths == { } && config.boot.initrd.extraSecretsHook == ""
) "exit 0"}
${lib.optionalString (config.boot.initrd.secrets == { }) "exit 0"}
export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
@@ -452,25 +451,17 @@ let
${lib.concatStringsSep "\n" (
mapAttrsToList (
_: scfg:
dest: source:
let
prefix = lib.optionalString scfg.intermediateSecretsDir "/.initrd-secrets";
source' = if source == null then dest else toString source;
in
''
mkdir -p $(dirname "$tmp${prefix}${scfg.path}")
(
export out="$tmp${prefix}${scfg.path}"
${scfg.generateSecretCommand}
)
mkdir -p $(dirname "$tmp/.initrd-secrets/${dest}")
cp -a ${source'} "$tmp/.initrd-secrets/${dest}"
''
) config.boot.initrd.secretPaths
) config.boot.initrd.secrets
)}
(
cd "$tmp"
${config.boot.initrd.extraSecretsHook}
)
# mindepth 1 so that we don't change the mode of /
(cd "$tmp" && find . -mindepth 1 | xargs touch -amt 197001010000 && find . -mindepth 1 -print0 | sort -z | cpio --quiet -o -H newc -R +0:+0 --reproducible --null) | \
${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1"
@@ -659,113 +650,21 @@ in
boot.initrd.secrets = mkOption {
default = { };
type = types.attrsOf (types.nullOr types.path);
visible = false;
description = ''
Secrets to append to the initrd. This option has been deprecated in
favour of `boot.initrd.secretPaths`.
'';
example = literalExpression ''
{ "/etc/dropbear/dropbear_rsa_host_key" =
./secret-dropbear-key;
}
'';
};
boot.initrd.secretPaths = mkOption {
default = { };
type = types.attrsOf (
types.submodule (
{ config, name, ... }:
{
options = {
path = mkOption {
type = types.path;
default = name;
description = ''
The path the secret should be placed at in the initrd. Defaults
to the attribute name.
'';
};
intermediateSecretsDir = mkOption {
type = types.bool;
default = true;
description = ''
By default, the secrets will be copied over to the
`/.initrd-secrets` dir at initrd generation time, and then copied
over to their final location at boot time. This is because initrd secrets
that are supposed to be placed in `/run` would be overridden by
the tmpfs mount over `/run` otherwise.
Set this option to `false` to skip this intermediate step and
place the secret at its final location straightaway.
'';
};
source = mkOption {
type = types.nullOr types.path;
default = null;
description = ''
The absolute path on the filesystem to copy the secret from.
'';
example = "/var/lib/secrets/initrd/ssh_host_ed25519_key";
};
generateSecretCommand = mkOption {
type = types.path;
description = ''
The command to run to generate the secret. It should write
the secret to `$out`.
This is useful if you have a more advanced secrets provisioning
mechanism.
'';
example = ''
pkgs.writeShellScript "generate-secret" '''
''${lib.getExe pkgs.age} -d -i /etc/ssh/ssh_host_ed25519_key -o "$out" ''${./secret.age}
'''
'';
};
};
config = {
generateSecretCommand = lib.mkIf (config.source != null) (
pkgs.writeShellScript "copy-secret" ''
cp -Lr ${config.source} "$out"
''
);
};
}
)
);
description = ''
Secret paths to append to the initrd. The attribute name is the
path the secret should have inside the initrd.
Secrets to append to the initrd. The attribute name is the
path the secret should have inside the initrd, the value
is the path it should be copied from (or null for the same
path inside and out).
Note that `nixos-rebuild switch` will generate the initrd
also for past generations, so if secrets are moved or deleted
you will also have to garbage collect the generations that
use those secrets.
'';
example = {
"/etc/ssh/ssh_host_ed25519_key".source = "/var/lib/secrets/initrd/ssh_host_ed25519_key";
};
};
boot.initrd.extraSecretsHook = mkOption {
default = "";
type = types.lines;
description = ''
Extra commands to be executed after the initrd secrets generation phase.
This script should place files into the current workdir. These files
will then be copied over to the initrd to the corresponding absolute
paths, e.g. `etc/ssh/ssh_host_ed25519_key` will be copied over to
`/etc/ssh/ssh_host_ed25519_key`.
'';
example = ''
# Generate a new SSH host key for every generation.
ssh-keygen -f etc/ssh/ssh_host_ed25519_key
example = literalExpression ''
{ "/etc/dropbear/dropbear_rsa_host_key" =
./secret-dropbear-key;
}
'';
};
@@ -847,18 +746,15 @@ in
assertion =
!config.boot.loader.supportsInitrdSecrets
-> all (
scfg:
builtins.isPath scfg.source
|| (builtins.isString scfg.source && hasPrefix builtins.storeDir scfg.source)
) (attrValues config.boot.initrd.secretPaths);
source: builtins.isPath source || (builtins.isString source && hasPrefix builtins.storeDir source)
) (attrValues config.boot.initrd.secrets);
message = ''
When using a bootloader that doesn't natively support initrd secrets,
all `boot.initrd.secretPaths` values must be defined via
`boot.initrd.secretsPaths.*.source`, and the `source` values must be
unquoted paths, e.g.
boot.initrd.secrets values must be unquoted paths when
using a bootloader that doesn't natively support initrd
secrets, e.g.:
boot.initrd.secretPaths = {
"/etc/secret".source = /path/to/secret;
boot.initrd.secrets = {
"/etc/secret" = /path/to/secret;
};
Note that this will result in all secrets being stored
@@ -867,18 +763,6 @@ in
}
];
warnings = lib.optional (config.boot.initrd.secrets != { }) ''
The option `boot.initrd.secrets` has been deprecated in favour of `boot.initrd.secretPaths`.
'';
# Backwards compatibility to the legacy `boot.initrd.secrets` option.
boot.initrd.secretPaths = lib.mapAttrs' (dest: source: {
# The legacy boot.initrd.secrets option didn't type-check the attr
# names, so we need to optionally prepend a slash.
name = "${lib.optionalString (!lib.hasPrefix "/" dest) "/"}${dest}";
value.source = if dest != null then source else dest;
}) config.boot.initrd.secrets;
system.build = mkMerge [
{
inherit

View File

@@ -10,21 +10,14 @@
# Copy secrets into the initrd if they cannot be appended
boot.initrd.systemd.contents = lib.mkIf (!config.boot.loader.supportsInitrdSecrets) (
lib.mapAttrs' (
_: scfg:
let
prefix = lib.optionalString scfg.intermediateSecretsDir "/.initrd-secrets";
in
lib.nameValuePair "${prefix}${scfg.path}" { inherit (scfg) source; }
) config.boot.initrd.secretPaths
dest: source:
lib.nameValuePair "/.initrd-secrets/${dest}" { source = if source == null then dest else source; }
) config.boot.initrd.secrets
);
# Copy secrets to their respective locations
boot.initrd.systemd.services.initrd-nixos-copy-secrets =
lib.mkIf
(
(builtins.any (x: x.intermediateSecretsDir) (builtins.attrValues config.boot.initrd.secretPaths))
|| config.boot.initrd.extraSecretsHook != ""
)
lib.mkIf (config.boot.initrd.secrets != { })
{
description = "Copy secrets into place";
# Run as early as possible
@@ -41,12 +34,10 @@
# drop this service, we'd mount the /run tmpfs over the secret, making it
# invisible in stage 2.
script = ''
if [ -d /.initrd-secrets ]; then
for secret in $(cd /.initrd-secrets; find . -type f -o -type l); do
mkdir -p "$(dirname "/$secret")"
cp "/.initrd-secrets/$secret" "/$secret"
done
fi
for secret in $(cd /.initrd-secrets; find . -type f -o -type l); do
mkdir -p "$(dirname "/$secret")"
cp "/.initrd-secrets/$secret" "/$secret"
done
'';
serviceConfig = {

View File

@@ -128,7 +128,7 @@ in
environment.systemPackages = [ pkgs.jq ];
# It's probably the case, but we want to make it explicit here.
boot.initrd.enable = true;
boot.initrd.secretPaths."/some/example".source = pkgs.writeText "example-secret" "test";
boot.initrd.secrets."/some/example" = pkgs.writeText "example-secret" "test";
};
testScript = ''

View File

@@ -47,7 +47,7 @@ in
};
};
virtualisation.rootDevice = "/dev/mapper/cryptroot";
boot.initrd.secretPaths."/etc/cryptroot.key".source = keyfile;
boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
};
specialisation.boot-luks-missing-keyfile.configuration = {

View File

@@ -21,16 +21,16 @@ testing.makeTest {
boot.loader.grub.device = "/dev/vda";
boot.initrd.secretPaths = {
"/test".source = secret1InStore;
"/run/keys/test".source = secret1InStore;
boot.initrd.secrets = {
"/test" = secret1InStore;
"/run/keys/test" = secret1InStore;
};
boot.initrd.postMountCommands = "cp /test /mnt-root/secret-from-initramfs";
specialisation.secrets2System.configuration = {
boot.initrd.secretPaths = lib.mkForce {
"/test".source = secret2InStore;
"/run/keys/test".source = secret2InStore;
boot.initrd.secrets = lib.mkForce {
"/test" = secret2InStore;
"/run/keys/test" = secret2InStore;
};
};
};

View File

@@ -21,26 +21,14 @@ let
{ ... }:
{
virtualisation.useBootLoader = true;
boot.initrd.secretPaths = {
"/test" = {
source = secretInStore;
intermediateSecretsDir = false;
};
boot.initrd.secrets = {
"/test" = secretInStore;
# This should *not* need to be copied in postMountCommands
"/run/keys/test1".source = secretInStore;
"/run/keys/test2".generateSecretCommand = pkgs.writeShellScript "copy-secret" ''
cp ${secretInStore} "$out"
'';
"/run/keys/test" = secretInStore;
};
boot.initrd.extraSecretsHook = ''
mkdir -p etc/secrets
cp ${secretInStore} etc/secrets/test2
'';
boot.initrd.postMountCommands = ''
cp /test /mnt-root/secret-from-initramfs-1
cp /etc/secrets/test2 /mnt-root/secret-from-initramfs-2
cp /test /mnt-root/secret-from-initramfs
'';
boot.initrd.compressor = compressor;
# zstd compression is only supported from 5.9 onwards. Remove when 5.10 becomes default.
@@ -51,10 +39,8 @@ let
start_all()
machine.wait_for_unit("multi-user.target")
machine.succeed(
"cmp ${secretInStore} /secret-from-initramfs-1",
"cmp ${secretInStore} /secret-from-initramfs-2",
"cmp ${secretInStore} /run/keys/test1",
"cmp ${secretInStore} /run/keys/test2",
"cmp ${secretInStore} /secret-from-initramfs",
"cmp ${secretInStore} /run/keys/test",
)
'';
};

View File

@@ -67,7 +67,7 @@ let
boot.loader.systemd-boot.enable = true;
''}
boot.initrd.secretPaths."/etc/secret".source = "/etc/nixos/secret";
boot.initrd.secrets."/etc/secret" = "/etc/nixos/secret";
${optionalString clevisTest ''
boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ];
@@ -1385,7 +1385,7 @@ in
};
# Full disk encryption (root, kernel and initrd encrypted) using GRUB, GPT/UEFI,
# LVM-on-LUKS and a keyfile in initrd.secretPaths to enter the passphrase once
# LVM-on-LUKS and a keyfile in initrd.secrets to enter the passphrase once
fullDiskEncryption = makeInstallerTest "fullDiskEncryption" {
createPartitions = ''
installer.succeed(
@@ -1419,7 +1419,7 @@ in
boot.loader.grub.enableCryptodisk = true;
boot.loader.efi.efiSysMountPoint = "/boot/efi";
boot.initrd.secretPaths."/luks.key" = "/etc/nixos/luks.key";
boot.initrd.secrets."/luks.key" = "/etc/nixos/luks.key";
boot.initrd.luks.devices.crypt =
{ device = "/dev/vda2";
keyFile = "/luks.key";

View File

@@ -38,7 +38,7 @@ in
};
};
virtualisation.rootDevice = "/dev/mapper/cryptroot";
boot.initrd.secretPaths."/etc/cryptroot.key".source = keyfile;
boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
};
};

View File

@@ -60,8 +60,8 @@ cfgbootnone = """ # Disable bootloader.
"""
cfgbootgrubcrypt = """ # Setup keyfile
boot.initrd.secretPaths = {
"/boot/crypto_keyfile.bin".source = null;
boot.initrd.secrets = {
"/boot/crypto_keyfile.bin" = null;
};
boot.loader.grub.enableCryptodisk = true;

View File

@@ -252,7 +252,7 @@ if [[ -z $noBootLoader ]]; then
# system. This preserves the validity of their absolute paths after changing
# the root with `nixos-enter`.
# Without this the bootloader installation may fail due to options that
# contain paths referenced during evaluation, like initrd.secretPaths.
# contain paths referenced during evaluation, like initrd.secrets.
# when not root, re-execute the script in an unshared namespace
mount --rbind --mkdir / "$mountPoint"
mount --make-rslave "$mountPoint"