summaryrefslogtreecommitdiff
path: root/modules/stage-1/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'modules/stage-1/default.nix')
-rw-r--r--modules/stage-1/default.nix669
1 files changed, 669 insertions, 0 deletions
diff --git a/modules/stage-1/default.nix b/modules/stage-1/default.nix
new file mode 100644
index 00000000..5a14584e
--- /dev/null
+++ b/modules/stage-1/default.nix
@@ -0,0 +1,669 @@
1# This module builds the initial ramdisk, which contains an init
2# script that performs the first stage of booting the system: it loads
3# the modules necessary to mount the root file system, then calls the
4# init in the root file system to start the second boot stage.
5
6{ config, lib, utils, pkgs, ... }:
7
8with lib;
9
10let
11
12 udev = config.systemd.package;
13
14 kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
15
16 modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
17 firmware = config.hardware.firmware;
18
19
20 # Determine the set of modules that we need to mount the root FS.
21 modulesClosure = pkgs.makeModulesClosure {
22 rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
23 kernel = modulesTree;
24 firmware = firmware;
25 allowMissing = false;
26 };
27
28
29 # The initrd only has to mount `/` or any FS marked as necessary for
30 # booting (such as the FS containing `/nix/store`, or an FS needed for
31 # mounting `/`, like `/` on a loopback).
32 fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
33
34 # A utility for enumerating the shared-library dependencies of a program
35 findLibs = pkgs.buildPackages.writeShellScriptBin "find-libs" ''
36 set -euo pipefail
37
38 declare -A seen
39 left=()
40
41 patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
42
43 function add_needed {
44 rpath="$($patchelf --print-rpath $1)"
45 dir="$(dirname $1)"
46 for lib in $($patchelf --print-needed $1); do
47 left+=("$lib" "$rpath" "$dir")
48 done
49 }
50
51 add_needed "$1"
52
53 while [ ''${#left[@]} -ne 0 ]; do
54 next=''${left[0]}
55 rpath=''${left[1]}
56 ORIGIN=''${left[2]}
57 left=("''${left[@]:3}")
58 if [ -z ''${seen[$next]+x} ]; then
59 seen[$next]=1
60
61 # Ignore the dynamic linker which for some reason appears as a DT_NEEDED of glibc but isn't in glibc's RPATH.
62 case "$next" in
63 ld*.so.?) continue;;
64 esac
65
66 IFS=: read -ra paths <<< $rpath
67 res=
68 for path in "''${paths[@]}"; do
69 path=$(eval "echo $path")
70 if [ -f "$path/$next" ]; then
71 res="$path/$next"
72 echo "$res"
73 add_needed "$res"
74 break
75 fi
76 done
77 if [ -z "$res" ]; then
78 echo "Couldn't satisfy dependency $next" >&2
79 exit 1
80 fi
81 fi
82 done
83 '';
84
85 # Some additional utilities needed in stage 1, like mount, lvm, fsck
86 # etc. We don't want to bring in all of those packages, so we just
87 # copy what we need. Instead of using statically linked binaries,
88 # we just copy what we need from Glibc and use patchelf to make it
89 # work.
90 extraUtils = pkgs.runCommandCC "extra-utils"
91 { nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
92 allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
93 }
94 ''
95 set +o pipefail
96
97 mkdir -p $out/bin $out/lib
98 ln -s $out/bin $out/sbin
99
100 copy_bin_and_libs () {
101 [ -f "$out/bin/$(basename $1)" ] && rm "$out/bin/$(basename $1)"
102 cp -pdv $1 $out/bin
103 }
104
105 # Copy BusyBox.
106 for BIN in ${pkgs.busybox}/{s,}bin/*; do
107 copy_bin_and_libs $BIN
108 done
109
110 # Copy some util-linux stuff.
111 copy_bin_and_libs ${pkgs.util-linux}/sbin/blkid
112
113 # Copy dmsetup and lvm.
114 copy_bin_and_libs ${getBin pkgs.lvm2}/bin/dmsetup
115 copy_bin_and_libs ${getBin pkgs.lvm2}/bin/lvm
116
117 # Add RAID mdadm tool.
118 copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm
119 copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
120
121 # Copy udev.
122 copy_bin_and_libs ${udev}/bin/udevadm
123 copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
124 for BIN in ${udev}/lib/udev/*_id; do
125 copy_bin_and_libs $BIN
126 done
127 # systemd-udevd is only a symlink to udevadm these days
128 ln -sf udevadm $out/bin/systemd-udevd
129
130 # Copy modprobe.
131 copy_bin_and_libs ${pkgs.kmod}/bin/kmod
132 ln -sf kmod $out/bin/modprobe
133
134 # Copy resize2fs if any ext* filesystems are to be resized
135 ${optionalString (any (fs: fs.autoResize && (lib.hasPrefix "ext" fs.fsType)) fileSystems) ''
136 # We need mke2fs in the initrd.
137 copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
138 ''}
139
140 # Copy secrets if needed.
141 #
142 # TODO: move out to a separate script; see #85000.
143 ${optionalString (!config.boot.loader.supportsInitrdSecrets)
144 (concatStringsSep "\n" (mapAttrsToList (dest: source:
145 let source' = if source == null then dest else source; in
146 ''
147 mkdir -p $(dirname "$out/secrets/${dest}")
148 # Some programs (e.g. ssh) doesn't like secrets to be
149 # symlinks, so we use `cp -L` here to match the
150 # behaviour when secrets are natively supported.
151 cp -Lr ${source'} "$out/secrets/${dest}"
152 ''
153 ) config.boot.initrd.secrets))
154 }
155
156 ${config.boot.initrd.extraUtilsCommands}
157
158 # Copy ld manually since it isn't detected correctly
159 cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib
160
161 # Copy all of the needed libraries
162 find $out/bin $out/lib -type f | while read BIN; do
163 echo "Copying libs for executable $BIN"
164 for LIB in $(${findLibs}/bin/find-libs $BIN); do
165 TGT="$out/lib/$(basename $LIB)"
166 if [ ! -f "$TGT" ]; then
167 SRC="$(readlink -e $LIB)"
168 cp -pdv "$SRC" "$TGT"
169 fi
170 done
171 done
172
173 # Strip binaries further than normal.
174 chmod -R u+w $out
175 stripDirs "$STRIP" "lib bin" "-s"
176
177 # Run patchelf to make the programs refer to the copied libraries.
178 find $out/bin $out/lib -type f | while read i; do
179 if ! test -L $i; then
180 nuke-refs -e $out $i
181 fi
182 done
183
184 find $out/bin -type f | while read i; do
185 if ! test -L $i; then
186 echo "patching $i..."
187 patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
188 fi
189 done
190
191 if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then
192 # Make sure that the patchelf'ed binaries still work.
193 echo "testing patched programs..."
194 $out/bin/ash -c 'echo hello world' | grep "hello world"
195 export LD_LIBRARY_PATH=$out/lib
196 $out/bin/mount --help 2>&1 | grep -q "BusyBox"
197 $out/bin/blkid -V 2>&1 | grep -q 'libblkid'
198 $out/bin/udevadm --version
199 $out/bin/dmsetup --version 2>&1 | tee -a log | grep -q "version:"
200 LVM_SYSTEM_DIR=$out $out/bin/lvm version 2>&1 | tee -a log | grep -q "LVM"
201 $out/bin/mdadm --version
202
203 ${config.boot.initrd.extraUtilsCommandsTest}
204 fi
205 ''; # */
206
207
208 # Networkd link files are used early by udev to set up interfaces early.
209 # This must be done in stage 1 to avoid race conditions between udev and
210 # network daemons.
211 linkUnits = pkgs.runCommand "link-units" {
212 allowedReferences = [ extraUtils ];
213 preferLocalBuild = true;
214 } (''
215 mkdir -p $out
216 cp -v ${udev}/lib/systemd/network/*.link $out/
217 '' + (
218 let
219 links = filterAttrs (n: v: hasSuffix ".link" n) config.systemd.network.units;
220 files = mapAttrsToList (n: v: "${v.unit}/${n}") links;
221 in
222 concatMapStringsSep "\n" (file: "cp -v ${file} $out/") files
223 ));
224
225 udevRules = pkgs.runCommand "udev-rules" {
226 allowedReferences = [ extraUtils ];
227 preferLocalBuild = true;
228 } ''
229 mkdir -p $out
230
231 echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
232
233 cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
234 cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
235 cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/
236 cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
237 cp -v ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/
238 cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
239 ${config.boot.initrd.extraUdevRulesCommands}
240
241 for i in $out/*.rules; do
242 substituteInPlace $i \
243 --replace ata_id ${extraUtils}/bin/ata_id \
244 --replace scsi_id ${extraUtils}/bin/scsi_id \
245 --replace cdrom_id ${extraUtils}/bin/cdrom_id \
246 --replace ${pkgs.coreutils}/bin/basename ${extraUtils}/bin/basename \
247 --replace ${pkgs.util-linux}/bin/blkid ${extraUtils}/bin/blkid \
248 --replace ${getBin pkgs.lvm2}/bin ${extraUtils}/bin \
249 --replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
250 --replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
251 --replace ${udev} ${extraUtils}
252 done
253
254 # Work around a bug in QEMU, which doesn't implement the "READ
255 # DISC INFORMATION" SCSI command:
256 # https://bugzilla.redhat.com/show_bug.cgi?id=609049
257 # As a result, `cdrom_id' doesn't print
258 # ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
259 # /dev/disk/by-label symlinks from being created. We need these
260 # in the NixOS installation CD, so use ID_CDROM_MEDIA in the
261 # corresponding udev rules for now. This was the behaviour in
262 # udev <= 154. See also
263 # http://www.spinics.net/lists/hotplug/msg03935.html
264 substituteInPlace $out/60-persistent-storage.rules \
265 --replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
266 ''; # */
267
268
269 # The init script of boot stage 1 (loading kernel modules for
270 # mounting the root FS).
271 bootStage1 = pkgs.substituteAll {
272 src = ./stage-1-init.sh;
273
274 shell = "${extraUtils}/bin/ash";
275
276 isExecutable = true;
277
278 postInstall = ''
279 echo checking syntax
280 # check both with bash
281 ${pkgs.buildPackages.bash}/bin/sh -n $target
282 # and with ash shell, just in case
283 ${pkgs.buildPackages.busybox}/bin/ash -n $target
284 '';
285
286 inherit linkUnits udevRules extraUtils modulesClosure;
287
288 inherit (config.boot) resumeDevice;
289
290 inherit (config.system.build) earlyMountScript;
291
292 inherit (config.boot.initrd) checkJournalingFS verbose
293 preLVMCommands preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules;
294
295 resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
296 (filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable
297 # Don't include zram devices
298 && !(hasPrefix "/dev/zram" sd.device)
299 ) config.swapDevices);
300
301 fsInfo =
302 let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ];
303 in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
304
305 setHostId = optionalString (config.networking.hostId != null) ''
306 hi="${config.networking.hostId}"
307 ${if pkgs.stdenv.isBigEndian then ''
308 echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > /etc/hostid
309 '' else ''
310 echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > /etc/hostid
311 ''}
312 '';
313 };
314
315
316 # The closure of the init script of boot stage 1 is what we put in
317 # the initial RAM disk.
318 initialRamdisk = pkgs.makeInitrd {
319 name = "initrd-${kernel-name}";
320 inherit (config.boot.initrd) compressor compressorArgs prepend;
321
322 contents =
323 [ { object = bootStage1;
324 symlink = "/init";
325 }
326 { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
327 symlink = "/etc/mdadm.conf";
328 }
329 { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
330 src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
331 preferLocalBuild = true;
332 } ''
333 target=$out
334 ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
335 '';
336 symlink = "/etc/modprobe.d/ubuntu.conf";
337 }
338 { object = pkgs.kmod-debian-aliases;
339 symlink = "/etc/modprobe.d/debian.conf";
340 }
341 ];
342 };
343
344 # Script to add secret files to the initrd at bootloader update time
345 initialRamdiskSecretAppender =
346 let
347 compressorExe = initialRamdisk.compressorExecutableFunction pkgs;
348 in pkgs.writeScriptBin "append-initrd-secrets"
349 ''
350 #!${pkgs.bash}/bin/bash -e
351 function usage {
352 echo "USAGE: $0 INITRD_FILE" >&2
353 echo "Appends this configuration's secrets to INITRD_FILE" >&2
354 }
355
356 if [ $# -ne 1 ]; then
357 usage
358 exit 1
359 fi
360
361 if [ "$1"x = "--helpx" ]; then
362 usage
363 exit 0
364 fi
365
366 ${lib.optionalString (config.boot.initrd.secrets == {})
367 "exit 0"}
368
369 export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
370
371 function cleanup {
372 if [ -n "$tmp" -a -d "$tmp" ]; then
373 rm -fR "$tmp"
374 fi
375 }
376 trap cleanup EXIT
377
378 tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX)
379
380 ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
381 let source' = if source == null then dest else toString source; in
382 ''
383 mkdir -p $(dirname "$tmp/${dest}")
384 cp -aL ${source'} "$tmp/${dest}"
385 ''
386 ) config.boot.initrd.secrets)
387 }
388
389 (cd "$tmp" && find . -print0 | sort -z | cpio --quiet -o -H newc -R +0:+0 --reproducible --null) | \
390 ${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1"
391 '';
392
393in
394
395{
396 disabledModules = [ "system/boot/stage-1.nix" ];
397
398 options = {
399
400 boot.resumeDevice = mkOption {
401 type = types.str;
402 default = "";
403 example = "/dev/sda3";
404 description = ''
405 Device for manual resume attempt during boot. This should be used primarily
406 if you want to resume from file. If left empty, the swap partitions are used.
407 Specify here the device where the file resides.
408 You should also use <varname>boot.kernelParams</varname> to specify
409 <literal><replaceable>resume_offset</replaceable></literal>.
410 '';
411 };
412
413 boot.initrd.enable = mkOption {
414 type = types.bool;
415 default = !config.boot.isContainer;
416 defaultText = "!config.boot.isContainer";
417 description = ''
418 Whether to enable the NixOS initial RAM disk (initrd). This may be
419 needed to perform some initialisation tasks (like mounting
420 network/encrypted file systems) before continuing the boot process.
421 '';
422 };
423
424 boot.initrd.prepend = mkOption {
425 default = [ ];
426 type = types.listOf types.str;
427 description = ''
428 Other initrd files to prepend to the final initrd we are building.
429 '';
430 };
431
432 boot.initrd.checkJournalingFS = mkOption {
433 default = true;
434 type = types.bool;
435 description = ''
436 Whether to run <command>fsck</command> on journaling filesystems such as ext3.
437 '';
438 };
439
440 boot.initrd.mdadmConf = mkOption {
441 default = "";
442 type = types.lines;
443 description = ''
444 Contents of <filename>/etc/mdadm.conf</filename> in stage 1.
445 '';
446 };
447
448 boot.initrd.preLVMCommands = mkOption {
449 default = "";
450 type = types.lines;
451 description = ''
452 Shell commands to be executed immediately before LVM discovery.
453 '';
454 };
455
456 boot.initrd.preDeviceCommands = mkOption {
457 default = "";
458 type = types.lines;
459 description = ''
460 Shell commands to be executed before udev is started to create
461 device nodes.
462 '';
463 };
464
465 boot.initrd.postDeviceCommands = mkOption {
466 default = "";
467 type = types.lines;
468 description = ''
469 Shell commands to be executed immediately after stage 1 of the
470 boot has loaded kernel modules and created device nodes in
471 <filename>/dev</filename>.
472 '';
473 };
474
475 boot.initrd.postMountCommands = mkOption {
476 default = "";
477 type = types.lines;
478 description = ''
479 Shell commands to be executed immediately after the stage 1
480 filesystems have been mounted.
481 '';
482 };
483
484 boot.initrd.preFailCommands = mkOption {
485 default = "";
486 type = types.lines;
487 description = ''
488 Shell commands to be executed before the failure prompt is shown.
489 '';
490 };
491
492 boot.initrd.extraUtilsCommands = mkOption {
493 internal = true;
494 default = "";
495 type = types.lines;
496 description = ''
497 Shell commands to be executed in the builder of the
498 extra-utils derivation. This can be used to provide
499 additional utilities in the initial ramdisk.
500 '';
501 };
502
503 boot.initrd.extraUtilsCommandsTest = mkOption {
504 internal = true;
505 default = "";
506 type = types.lines;
507 description = ''
508 Shell commands to be executed in the builder of the
509 extra-utils derivation after patchelf has done its
510 job. This can be used to test additional utilities
511 copied in extraUtilsCommands.
512 '';
513 };
514
515 boot.initrd.extraUdevRulesCommands = mkOption {
516 internal = true;
517 default = "";
518 type = types.lines;
519 description = ''
520 Shell commands to be executed in the builder of the
521 udev-rules derivation. This can be used to add
522 additional udev rules in the initial ramdisk.
523 '';
524 };
525
526 boot.initrd.compressor = mkOption {
527 default = (
528 if lib.versionAtLeast config.boot.kernelPackages.kernel.version "5.9"
529 then "zstd"
530 else "gzip"
531 );
532 defaultText = "zstd if the kernel supports it (5.9+), gzip if not.";
533 type = types.unspecified; # We don't have a function type...
534 description = ''
535 The compressor to use on the initrd image. May be any of:
536
537 <itemizedlist>
538 <listitem><para>The name of one of the predefined compressors, see <filename>pkgs/build-support/kernel/initrd-compressor-meta.nix</filename> for the definitions.</para></listitem>
539 <listitem><para>A function which, given the nixpkgs package set, returns the path to a compressor tool, e.g. <literal>pkgs: "''${pkgs.pigz}/bin/pigz"</literal></para></listitem>
540 <listitem><para>(not recommended, because it does not work when cross-compiling) the full path to a compressor tool, e.g. <literal>"''${pkgs.pigz}/bin/pigz"</literal></para></listitem>
541 </itemizedlist>
542
543 The given program should read data from stdin and write it to stdout compressed.
544 '';
545 example = "xz";
546 };
547
548 boot.initrd.compressorArgs = mkOption {
549 default = null;
550 type = types.nullOr (types.listOf types.str);
551 description = "Arguments to pass to the compressor for the initrd image, or null to use the compressor's defaults.";
552 };
553
554 boot.initrd.secrets = mkOption
555 { default = {};
556 type = types.attrsOf (types.nullOr types.path);
557 description =
558 ''
559 Secrets to append to the initrd. The attribute name is the
560 path the secret should have inside the initrd, the value
561 is the path it should be copied from (or null for the same
562 path inside and out).
563 '';
564 example = literalExample
565 ''
566 { "/etc/dropbear/dropbear_rsa_host_key" =
567 ./secret-dropbear-key;
568 }
569 '';
570 };
571
572 boot.initrd.supportedFilesystems = mkOption {
573 default = [ ];
574 example = [ "btrfs" ];
575 type = types.listOf types.str;
576 description = "Names of supported filesystem types in the initial ramdisk.";
577 };
578
579 boot.initrd.verbose = mkOption {
580 default = true;
581 type = types.bool;
582 description =
583 ''
584 Verbosity of the initrd. Please note that disabling verbosity removes
585 only the mandatory messages generated by the NixOS scripts. For a
586 completely silent boot, you might also want to set the two following
587 configuration options:
588
589 <itemizedlist>
590 <listitem><para><literal>boot.consoleLogLevel = 0;</literal></para></listitem>
591 <listitem><para><literal>boot.kernelParams = [ "quiet" "udev.log_priority=3" ];</literal></para></listitem>
592 </itemizedlist>
593 '';
594 };
595
596 boot.loader.supportsInitrdSecrets = mkOption
597 { internal = true;
598 default = false;
599 type = types.bool;
600 description =
601 ''
602 Whether the bootloader setup runs append-initrd-secrets.
603 If not, any needed secrets must be copied into the initrd
604 and thus added to the store.
605 '';
606 };
607
608 fileSystems = mkOption {
609 type = with lib.types; attrsOf (submodule {
610 options.neededForBoot = mkOption {
611 default = false;
612 type = types.bool;
613 description = ''
614 If set, this file system will be mounted in the initial ramdisk.
615 Note that the file system will always be mounted in the initial
616 ramdisk if its mount point is one of the following:
617 ${concatStringsSep ", " (
618 forEach utils.pathsNeededForBoot (i: "<filename>${i}</filename>")
619 )}.
620 '';
621 };
622 });
623 };
624
625 };
626
627 config = mkIf config.boot.initrd.enable {
628 assertions = [
629 { assertion = any (fs: fs.mountPoint == "/") fileSystems;
630 message = "The ‘fileSystems’ option does not specify your root file system.";
631 }
632 { assertion = let inherit (config.boot) resumeDevice; in
633 resumeDevice == "" || builtins.substring 0 1 resumeDevice == "/";
634 message = "boot.resumeDevice has to be an absolute path."
635 + " Old \"x:y\" style is no longer supported.";
636 }
637 # TODO: remove when #85000 is fixed
638 { assertion = !config.boot.loader.supportsInitrdSecrets ->
639 all (source:
640 builtins.isPath source ||
641 (builtins.isString source && hasPrefix builtins.storeDir source))
642 (attrValues config.boot.initrd.secrets);
643 message = ''
644 boot.loader.initrd.secrets values must be unquoted paths when
645 using a bootloader that doesn't natively support initrd
646 secrets, e.g.:
647
648 boot.initrd.secrets = {
649 "/etc/secret" = /path/to/secret;
650 };
651
652 Note that this will result in all secrets being stored
653 world-readable in the Nix store!
654 '';
655 }
656 ];
657
658 system.build =
659 { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
660
661 system.requiredKernelConfig = with config.lib.kernelConfig; [
662 (isYes "TMPFS")
663 (isYes "BLK_DEV_INITRD")
664 ];
665
666 boot.initrd.supportedFilesystems = map (fs: fs.fsType) fileSystems;
667
668 };
669}