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