diff options
Diffstat (limited to 'nixos/modules/system')
22 files changed, 406 insertions, 198 deletions
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index dcf105eb7844..c2ac731d433d 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -19,6 +19,7 @@ let glibc # needed for getent shadow nettools # needed for hostname + utillinux # needed for mount and mountpoint ]; in @@ -168,12 +169,12 @@ in local options="$3" local fsType="$4" - if ${pkgs.utillinux}/bin/mountpoint -q "$mountPoint"; then + if mountpoint -q "$mountPoint"; then local options="remount,$options" else mkdir -m 0755 -p "$mountPoint" fi - ${pkgs.utillinux}/bin/mount -t "$fsType" -o "$options" "$device" "$mountPoint" + mount -t "$fsType" -o "$options" "$device" "$mountPoint" } source ${config.system.build.earlyMountScript} ''; diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index 8747c1e3d4ac..88e7847cf8c8 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -41,7 +41,7 @@ if ($action eq "switch" || $action eq "boot") { } # Just in case the new configuration hangs the system, do a sync now. -system("@coreutils@/bin/sync") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1"; +system("@coreutils@/bin/sync", "-f", "/nix/store") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1"; exit 0 if $action eq "boot"; @@ -383,6 +383,10 @@ system("@systemd@/bin/systemctl", "reset-failed"); # Make systemd reload its units. system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3; +# Set the new tmpfiles +print STDERR "setting up tmpfiles\n"; +system("@systemd@/bin/systemd-tmpfiles", "--create", "--remove", "--exclude-prefix=/dev") == 0 or $res = 3; + # Reload units that need it. This includes remounting changed mount # units. if (scalar(keys %unitsToReload) > 0) { diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 0c08375da646..e9897cc01b6a 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -45,11 +45,16 @@ let ln -s ${kernelPath} $out/kernel ln -s ${config.system.modulesTree} $out/kernel-modules + ${optionalString (pkgs.stdenv.platform.kernelDTB or false) '' + ln -s ${config.boot.kernelPackages.kernel}/dtbs $out/dtbs + ''} echo -n "$kernelParams" > $out/kernel-params ln -s ${config.system.build.initialRamdisk}/initrd $out/initrd + ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out + ln -s ${config.hardware.firmware}/lib/firmware $out/firmware ''} diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix index 59ecaf8d5a6d..d78775c27582 100644 --- a/nixos/modules/system/boot/initrd-ssh.nix +++ b/nixos/modules/system/boot/initrd-ssh.nix @@ -44,9 +44,10 @@ in description = '' RSA SSH private key file in the Dropbear format. - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! + WARNING: Unless your bootloader supports initrd secrets, this key is + contained insecurely in the global Nix store. Do NOT use your regular + SSH host private keys for this purpose or you'll expose them to + regular users! ''; }; @@ -56,9 +57,10 @@ in description = '' DSS SSH private key file in the Dropbear format. - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! + WARNING: Unless your bootloader supports initrd secrets, this key is + contained insecurely in the global Nix store. Do NOT use your regular + SSH host private keys for this purpose or you'll expose them to + regular users! ''; }; @@ -68,9 +70,10 @@ in description = '' ECDSA SSH private key file in the Dropbear format. - WARNING: This key is contained insecurely in the global Nix store. Do NOT - use your regular SSH host private keys for this purpose or you'll expose - them to regular users! + WARNING: Unless your bootloader supports initrd secrets, this key is + contained insecurely in the global Nix store. Do NOT use your regular + SSH host private keys for this purpose or you'll expose them to + regular users! ''; }; @@ -97,10 +100,6 @@ in boot.initrd.extraUtilsCommands = '' copy_bin_and_libs ${pkgs.dropbear}/bin/dropbear cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib - - ${optionalString (cfg.hostRSAKey != null) "install -D ${cfg.hostRSAKey} $out/etc/dropbear/dropbear_rsa_host_key"} - ${optionalString (cfg.hostDSSKey != null) "install -D ${cfg.hostDSSKey} $out/etc/dropbear/dropbear_dss_host_key"} - ${optionalString (cfg.hostECDSAKey != null) "install -D ${cfg.hostECDSAKey} $out/etc/dropbear/dropbear_ecdsa_host_key"} ''; boot.initrd.extraUtilsCommandsTest = '' @@ -116,9 +115,6 @@ in touch /var/log/lastlog mkdir -p /etc/dropbear - ${optionalString (cfg.hostRSAKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_rsa_host_key /etc/dropbear/dropbear_rsa_host_key"} - ${optionalString (cfg.hostDSSKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_dss_host_key /etc/dropbear/dropbear_dss_host_key"} - ${optionalString (cfg.hostECDSAKey != null) "ln -s $extraUtils/etc/dropbear/dropbear_ecdsa_host_key /etc/dropbear/dropbear_ecdsa_host_key"} mkdir -p /root/.ssh ${concatStrings (map (key: '' @@ -128,6 +124,11 @@ in dropbear -s -j -k -E -m -p ${toString cfg.port} ''; + boot.initrd.secrets = + (optionalAttrs (cfg.hostRSAKey != null) { "/etc/dropbear/dropbear_rsa_host_key" = cfg.hostRSAKey; }) // + (optionalAttrs (cfg.hostDSSKey != null) { "/etc/dropbear/dropbear_dss_host_key" = cfg.hostDSSKey; }) // + (optionalAttrs (cfg.hostECDSAKey != null) { "/etc/dropbear/dropbear_ecdsa_host_key" = cfg.hostECDSAKey; }); + }; } diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index e751ff141f70..cf70a891c0ca 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -176,7 +176,7 @@ in boot.initrd.availableKernelModules = [ # Note: most of these (especially the SATA/PATA modules) - # shouldn't be included by default since nixos-hardware-scan + # shouldn't be included by default since nixos-generate-config # detects them, but I'm keeping them for now for backwards # compatibility. diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 294fc1988e9f..cf47aed9fa99 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -53,12 +53,14 @@ let inherit (args) devices; inherit (efi) canTouchEfiVariables; inherit (cfg) - version extraConfig extraPerEntryConfig extraEntries forceInstall - extraEntriesBeforeNixOS extraPrepareConfig configurationLimit copyKernels + version extraConfig extraPerEntryConfig extraEntries forceInstall useOSProber + extraEntriesBeforeNixOS extraPrepareConfig extraInitrd configurationLimit copyKernels default fsIdentifier efiSupport efiInstallAsRemovable gfxmodeEfi gfxmodeBios; path = (makeBinPath ([ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.findutils pkgs.diffutils pkgs.btrfs-progs - pkgs.utillinux ] ++ (if cfg.efiSupport && (cfg.version == 2) then [pkgs.efibootmgr ] else []) + pkgs.utillinux ] + ++ (optional (cfg.efiSupport && (cfg.version == 2)) pkgs.efibootmgr) + ++ (optionals cfg.useOSProber [pkgs.busybox pkgs.os-prober]) )) + ":" + (makeSearchPathOutput "bin" "sbin" [ pkgs.mdadm pkgs.utillinux ]); @@ -237,6 +239,12 @@ in menuentry "Windows 7" { chainloader (hd0,4)+1 } + + # GRUB 2 with UEFI example, chainloading another distro + menuentry "Fedora" { + set root=(hd1,1) + chainloader /efi/fedora/grubx64.efi + } ''; description = '' Any additional entries you want added to the GRUB boot menu. @@ -265,6 +273,27 @@ in ''; }; + extraInitrd = mkOption { + type = types.nullOr types.path; + default = null; + example = "/boot/extra_initrafms.gz"; + description = '' + The path to a second initramfs to be supplied to the kernel. + This ramfs will not be copied to the store, so that it can + contain secrets such as LUKS keyfiles or ssh keys. + This implies that rolling back to a previous configuration + won't rollback the state of this file. + ''; + }; + + useOSProber = mkOption { + default = false; + type = types.bool; + description = '' + If set to true, append entries for other OSs detected by os-prober. + ''; + }; + splashImage = mkOption { type = types.nullOr types.path; example = literalExample "./my-background.png"; @@ -358,7 +387,6 @@ in efiInstallAsRemovable = mkOption { default = false; - example = true; type = types.bool; description = '' Whether to invoke <literal>grub-install</literal> with diff --git a/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixos/modules/system/boot/loader/grub/install-grub.pl index 24442ca12a30..5fcac5c8c6a4 100644 --- a/nixos/modules/system/boot/loader/grub/install-grub.pl +++ b/nixos/modules/system/boot/loader/grub/install-grub.pl @@ -49,6 +49,7 @@ my $extraPrepareConfig = get("extraPrepareConfig"); my $extraPerEntryConfig = get("extraPerEntryConfig"); my $extraEntries = get("extraEntries"); my $extraEntriesBeforeNixOS = get("extraEntriesBeforeNixOS") eq "true"; +my $extraInitrd = get("extraInitrd"); my $splashImage = get("splashImage"); my $configurationLimit = int(get("configurationLimit")); my $copyKernels = get("copyKernels") eq "true"; @@ -226,6 +227,13 @@ my $grubStore; if ($copyKernels == 0) { $grubStore = GrubFs($storePath); } +my $extraInitrdPath; +if ($extraInitrd) { + if (! -f $extraInitrd) { + print STDERR "Warning: the specified extraInitrd " . $extraInitrd . " doesn't exist. Your system won't boot without it.\n"; + } + $extraInitrdPath = GrubFs($extraInitrd); +} # Generate the header. my $conf .= "# Automatically generated. DO NOT EDIT THIS FILE!\n"; @@ -256,8 +264,6 @@ else { # ‘grub-reboot’ sets a one-time saved entry, which we process here and # then delete. if [ \"\${next_entry}\" ]; then - # FIXME: KDM expects the next line to be present. - set default=\"\${saved_entry}\" set default=\"\${next_entry}\" set next_entry= save_env next_entry @@ -338,6 +344,9 @@ sub addEntry { my $kernel = copyToKernelsDir(Cwd::abs_path("$path/kernel")); my $initrd = copyToKernelsDir(Cwd::abs_path("$path/initrd")); + if ($extraInitrd) { + $initrd .= " " .$extraInitrdPath->path; + } my $xen = -e "$path/xen.gz" ? copyToKernelsDir(Cwd::abs_path("$path/xen.gz")) : undef; # FIXME: $confName @@ -360,6 +369,9 @@ sub addEntry { if ($copyKernels == 0) { $conf .= $grubStore->search . "\n"; } + if ($extraInitrd) { + $conf .= $extraInitrdPath->search . "\n"; + } $conf .= " $extraPerEntryConfig\n" if $extraPerEntryConfig; $conf .= " multiboot $xen $xenParams\n" if $xen; $conf .= " " . ($xen ? "module" : "linux") . " $kernel $kernelParams\n"; @@ -426,10 +438,48 @@ if ($extraPrepareConfig ne "") { system((get("shell"), "-c", $extraPrepareConfig)); } -# Atomically update the GRUB config. +# write the GRUB config. my $confFile = $grubVersion == 1 ? "$bootPath/grub/menu.lst" : "$bootPath/grub/grub.cfg"; my $tmpFile = $confFile . ".tmp"; writeFile($tmpFile, $conf); + + +# check whether to install GRUB EFI or not +sub getEfiTarget { + if ($grubVersion == 1) { + return "no" + } elsif (($grub ne "") && ($grubEfi ne "")) { + # EFI can only be installed when target is set; + # A target is also required then for non-EFI grub + if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die } + else { return "both" } + } elsif (($grub ne "") && ($grubEfi eq "")) { + # TODO: It would be safer to disallow non-EFI grub installation if no taget is given. + # If no target is given, then grub auto-detects the target which can lead to errors. + # E.g. it seems as if grub would auto-detect a EFI target based on the availability + # of a EFI partition. + # However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386 + # architectures in NixOS. That would have to be fixed in the nixos modules first. + return "no" + } elsif (($grub eq "") && ($grubEfi ne "")) { + # EFI can only be installed when target is set; + if ($grubTargetEfi eq "") { die } + else {return "only" } + } else { + # prevent an installation if neither grub nor grubEfi is given + return "neither" + } +} + +my $efiTarget = getEfiTarget(); + +# Append entries detected by os-prober +if (get("useOSProber") eq "true") { + my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi; + system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile"); +} + +# Atomically switch to the new config rename $tmpFile, $confFile or die "cannot rename $tmpFile to $confFile\n"; @@ -479,36 +529,7 @@ sub getDeviceTargets { } return @devices; } - -# check whether to install GRUB EFI or not -sub getEfiTarget { - if ($grubVersion == 1) { - return "no" - } elsif (($grub ne "") && ($grubEfi ne "")) { - # EFI can only be installed when target is set; - # A target is also required then for non-EFI grub - if (($grubTarget eq "") || ($grubTargetEfi eq "")) { die } - else { return "both" } - } elsif (($grub ne "") && ($grubEfi eq "")) { - # TODO: It would be safer to disallow non-EFI grub installation if no taget is given. - # If no target is given, then grub auto-detects the target which can lead to errors. - # E.g. it seems as if grub would auto-detect a EFI target based on the availability - # of a EFI partition. - # However, it seems as auto-detection is currently relied on for non-x86_64 and non-i386 - # architectures in NixOS. That would have to be fixed in the nixos modules first. - return "no" - } elsif (($grub eq "") && ($grubEfi ne "")) { - # EFI can only be installed when target is set; - if ($grubTargetEfi eq "") { die } - else {return "only" } - } else { - # prevent an installation if neither grub nor grubEfi is given - return "neither" - } -} - my @deviceTargets = getDeviceTargets(); -my $efiTarget = getEfiTarget(); my $prevGrubState = readGrubState(); my @prevDeviceTargets = split/,/, $prevGrubState->devices; diff --git a/nixos/modules/system/boot/loader/raspberrypi/builder.sh b/nixos/modules/system/boot/loader/raspberrypi/builder.sh index ccb88ca1c529..f627d093eafb 100644 --- a/nixos/modules/system/boot/loader/raspberrypi/builder.sh +++ b/nixos/modules/system/boot/loader/raspberrypi/builder.sh @@ -61,12 +61,13 @@ addEntry() { local kernel=$(readlink -f $path/kernel) local initrd=$(readlink -f $path/initrd) + local dtb_path=$(readlink -f $path/kernel-modules/dtbs) if test -n "@copyKernels@"; then copyToKernelsDir $kernel; kernel=$result copyToKernelsDir $initrd; initrd=$result fi - + echo $(readlink -f $path) > $outdir/$generation-system echo $(readlink -f $path/init) > $outdir/$generation-init cp $path/kernel-params $outdir/$generation-cmdline.txt @@ -80,6 +81,11 @@ addEntry() { copyForced $kernel /boot/kernel7.img fi copyForced $initrd /boot/initrd + for dtb in $dtb_path/bcm*.dtb; do + dst="/boot/$(basename $dtb)" + copyForced $dtb "$dst" + filesCopied[$dst]=1 + done cp "$(readlink -f "$path/init")" /boot/nixos-init echo "`cat $path/kernel-params` init=$path/init" >/boot/cmdline.txt @@ -108,8 +114,8 @@ copyForced $fwdir/start_cd.elf /boot/start_cd.elf copyForced $fwdir/start_db.elf /boot/start_db.elf copyForced $fwdir/start_x.elf /boot/start_x.elf -# Remove obsolete files from /boot/old. -for fn in /boot/old/*linux* /boot/old/*initrd*; do +# Remove obsolete files from /boot and /boot/old. +for fn in /boot/old/*linux* /boot/old/*initrd-initrd* /boot/bcm*.dtb; do if ! test "${filesCopied[$fn]}" = 1; then rm -vf -- "$fn" fi diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix index eb8ea6130972..f246d04284ca 100644 --- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix +++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix @@ -33,7 +33,7 @@ in boot.loader.raspberryPi.version = mkOption { default = 2; - type = types.enum [ 1 2 ]; + type = types.enum [ 1 2 3 ]; description = '' ''; }; diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index 515136c904c5..704c574b822e 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -1,4 +1,4 @@ -#! @python3@/bin/python3 +#! @python3@/bin/python3 -B import argparse import shutil import os @@ -28,10 +28,15 @@ def write_loader_conf(generation): if "@timeout@" != "": f.write("timeout @timeout@\n") f.write("default nixos-generation-%d\n" % generation) + if not @editor@: + f.write("editor 0"); os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf") +def profile_path(generation, name): + return os.readlink("%s/%s" % (system_dir(generation), name)) + def copy_from_profile(generation, name, dry_run=False): - store_file_path = os.readlink("%s/%s" % (system_dir(generation), name)) + store_file_path = profile_path(generation, name) suffix = os.path.basename(store_file_path) store_dir = os.path.basename(os.path.dirname(store_file_path)) efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix) @@ -42,6 +47,11 @@ def copy_from_profile(generation, name, dry_run=False): def write_entry(generation, machine_id): kernel = copy_from_profile(generation, "kernel") initrd = copy_from_profile(generation, "initrd") + try: + append_initrd_secrets = profile_path(generation, "append-initrd-secrets") + subprocess.check_call([append_initrd_secrets, "@efiSysMountPoint@%s" % (initrd)]) + except FileNotFoundError: + pass entry_file = "@efiSysMountPoint@/loader/entries/nixos-generation-%d.conf" % (generation) generation_dir = os.readlink(system_dir(generation)) tmp_path = "%s.tmp" % (entry_file) @@ -99,11 +109,27 @@ def main(): parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot') args = parser.parse_args() + try: + with open("/etc/machine-id") as machine_file: + machine_id = machine_file.readlines()[0] + except IOError as e: + if e.errno != errno.ENOENT: + raise + # Since systemd version 232 a machine ID is required and it might not + # be there on newly installed systems, so let's generate one so that + # bootctl can find it and we can also pass it to write_entry() later. + cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"] + machine_id = subprocess.check_output(cmd).rstrip() + if os.getenv("NIXOS_INSTALL_GRUB") == "1": warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning) os.environ["NIXOS_INSTALL_BOOTLOADER"] = "1" if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1": + # bootctl uses fopen() with modes "wxe" and fails if the file exists. + if os.path.exists("@efiSysMountPoint@/loader/loader.conf"): + os.unlink("@efiSysMountPoint@/loader/loader.conf") + if "@canTouchEfiVariables@" == "1": subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"]) else: @@ -111,13 +137,6 @@ def main(): mkdir_p("@efiSysMountPoint@/efi/nixos") mkdir_p("@efiSysMountPoint@/loader/entries") - try: - with open("/etc/machine-id") as machine_file: - machine_id = machine_file.readlines()[0] - except IOError as e: - if e.errno != errno.ENOENT: - raise - machine_id = None gens = get_generations("system") remove_old_entries(gens) diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index cc43fb8bab4c..a5a88a99be8f 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -20,6 +20,8 @@ let timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else ""; + editor = if cfg.editor then "True" else "False"; + inherit (efi) efiSysMountPoint canTouchEfiVariables; }; in { @@ -36,6 +38,20 @@ in { description = "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager"; }; + + editor = mkOption { + default = true; + + type = types.bool; + + description = '' + Whether to allow editing the kernel command-line before + boot. It is recommended to set this to false, as it allows + gaining root access by passing init=/bin/sh as a kernel + parameter. However, it is enabled by default for backwards + compatibility. + ''; + }; }; config = mkIf cfg.enable { @@ -49,6 +65,8 @@ in { boot.loader.grub.enable = mkDefault false; + boot.loader.supportsInitrdSecrets = true; + system = { build.installBootLoader = gummibootBuilder; diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index 1f412fe2d8f2..6e867b674398 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -6,29 +6,38 @@ let luks = config.boot.initrd.luks; openCommand = name': { name, device, header, keyFile, keyFileSize, allowDiscards, yubikey, ... }: assert name' == name; '' - # Wait for luksRoot to appear, e.g. if on a usb drive. - # XXX: copied and adapted from stage-1-init.sh - should be - # available as a function. - if ! test -e ${device}; then - echo -n "waiting 10 seconds for device ${device} to appear..." - for try in $(seq 10); do - sleep 1 - if test -e ${device}; then break; fi - echo -n . - done - echo "ok" - fi + + # Wait for a target (e.g. device, keyFile, header, ...) to appear. + wait_target() { + local name="$1" + local target="$2" + + if [ ! -e $target ]; then + echo -n "Waiting 10 seconds for $name $target to appear" + local success=false; + for try in $(seq 10); do + echo -n "." + sleep 1 + if [ -e $target ]; then success=true break; fi + done + if [ $success = true ]; then + echo " - success"; + else + echo " - failure"; + fi + fi + } + + # Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g. + # if on a USB drive. + wait_target "device" ${device} ${optionalString (keyFile != null) '' - if ! test -e ${keyFile}; then - echo -n "waiting 10 seconds for key file ${keyFile} to appear..." - for try in $(seq 10); do - sleep 1 - if test -e ${keyFile}; then break; fi - echo -n . - done - echo "ok" - fi + wait_target "key file" ${keyFile} + ''} + + ${optionalString (header != null) '' + wait_target "header" ${header} ''} open_normally() { @@ -434,8 +443,8 @@ in chmod +x $out/bin/cryptsetup-askpass ${optionalString luks.yubikeySupport '' - copy_bin_and_libs ${pkgs.ykpers}/bin/ykchalresp - copy_bin_and_libs ${pkgs.ykpers}/bin/ykinfo + copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp + copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index b828ad53dc58..f96dde153610 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -79,7 +79,7 @@ let checkBond = checkUnitConfig "Bond" [ (assertOnlyFields [ "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec" - "UpDelaySec" "DownDelaySec" + "UpDelaySec" "DownDelaySec" "GratuitousARP" ]) (assertValueOneOf "Mode" [ "balance-rr" "active-backup" "balance-xor" @@ -667,8 +667,10 @@ in config = mkIf config.systemd.network.enable { - systemd.additionalUpstreamSystemUnits = - [ "systemd-networkd.service" "systemd-networkd-wait-online.service" ]; + systemd.additionalUpstreamSystemUnits = [ + "systemd-networkd.service" "systemd-networkd-wait-online.service" + "org.freedesktop.network1.busname" + ]; systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs diff --git a/nixos/modules/system/boot/readonly-mountpoint.c b/nixos/modules/system/boot/readonly-mountpoint.c deleted file mode 100644 index 27b666873821..000000000000 --- a/nixos/modules/system/boot/readonly-mountpoint.c +++ /dev/null @@ -1,20 +0,0 @@ -#include <sys/statvfs.h> -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char ** argv) { - struct statvfs stat; - if (argc != 2) { - fprintf(stderr, "Usage: %s PATH", argv[0]); - exit(2); - } - if (statvfs(argv[1], &stat) != 0) { - perror("statvfs"); - exit(3); - } - if (stat.f_flag & ST_RDONLY) - exit(0); - else - exit(1); -} - diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix index 4b7c545dcc0d..a3fb733c289d 100644 --- a/nixos/modules/system/boot/resolved.nix +++ b/nixos/modules/system/boot/resolved.nix @@ -71,7 +71,9 @@ in config = mkIf cfg.enable { - systemd.additionalUpstreamSystemUnits = [ "systemd-resolved.service" ]; + systemd.additionalUpstreamSystemUnits = [ + "systemd-resolved.service" "org.freedesktop.resolve1.busname" + ]; systemd.services.systemd-resolved = { wantedBy = [ "multi-user.target" ]; diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index f0699ad9832b..9a125dcb0aeb 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -8,6 +8,14 @@ export LD_LIBRARY_PATH=@extraUtils@/lib export PATH=@extraUtils@/bin ln -s @extraUtils@/bin /bin +# Copy the secrets to their needed location +if [ -d "@extraUtils@/secrets" ]; then + for secret in $(cd "@extraUtils@/secrets"; find . -type f); do + mkdir -p $(dirname "/$secret") + ln -s "@extraUtils@/secrets/$secret" "$secret" + done +fi + # Stop LVM complaining about fd3 export LVM_SUPPRESS_FD_WARNINGS=true @@ -146,6 +154,9 @@ for o in $(cat /proc/cmdline); do fi ln -s "$root" /dev/root ;; + copytoram) + copytoram=1 + ;; esac done @@ -466,6 +477,22 @@ while read -u 3 mountPoint; do # doing something with $device right now. udevadm settle + # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs. + if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then + fsType=$(blkid -o value -s TYPE "$device") + fsSize=$(blockdev --getsize64 "$device") + + mkdir -p /tmp-iso + mount -t "$fsType" /dev/root /tmp-iso + mountFS tmpfs /iso size="$fsSize" tmpfs + + cp -r /tmp-iso/* /mnt-root/iso/ + + umount /tmp-iso + rmdir /tmp-iso + continue + fi + mountFS "$device" "$mountPoint" "$options" "$fsType" done diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index 61def24efd88..e3a3b6f88cf2 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -82,6 +82,17 @@ let copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs ''} + # Copy secrets if needed. + ${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}") + cp -a ${source'} "$out/secrets/${dest}" + '' + ) config.boot.initrd.secrets)) + } + ${config.boot.initrd.extraUtilsCommands} # Copy ld manually since it isn't detected correctly @@ -242,6 +253,52 @@ let ]; }; + # Script to add secret files to the initrd at bootloader update time + initialRamdiskSecretAppender = + pkgs.writeScriptBin "append-initrd-secrets" + '' + #!${pkgs.bash}/bin/bash -e + function usage { + echo "USAGE: $0 INITRD_FILE" >&2 + echo "Appends this configuration's secrets to INITRD_FILE" >&2 + } + + if [ $# -ne 1 ]; then + usage + exit 1 + fi + + if [ "$1"x = "--helpx" ]; then + usage + exit 0 + fi + + ${lib.optionalString (config.boot.initrd.secrets == {}) + "exit 0"} + + export PATH=${pkgs.coreutils}/bin:${pkgs.cpio}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin + + function cleanup { + if [ -n "$tmp" -a -d "$tmp" ]; then + rm -fR "$tmp" + fi + } + trap cleanup EXIT + + tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX) + + ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source: + let source' = if source == null then dest else toString source; in + '' + mkdir -p $(dirname "$tmp/${dest}") + cp -a ${source'} "$tmp/${dest}" + '' + ) config.boot.initrd.secrets) + } + + (cd "$tmp" && find . | cpio -H newc -o) | gzip >>"$1" + ''; + in { @@ -370,6 +427,25 @@ in example = "xz"; }; + boot.initrd.secrets = mkOption + { internal = true; + default = {}; + type = types.attrsOf (types.nullOr types.path); + description = + '' + 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). + ''; + example = literalExample + '' + { "/etc/dropbear/dropbear_rsa_host_key" = + ./secret-dropbear-key; + } + ''; + }; + boot.initrd.supportedFilesystems = mkOption { default = [ ]; example = [ "btrfs" ]; @@ -377,6 +453,18 @@ in description = "Names of supported filesystem types in the initial ramdisk."; }; + boot.loader.supportsInitrdSecrets = mkOption + { internal = true; + default = false; + type = types.bool; + description = + '' + Whether the bootloader setup runs append-initrd-secrets. + If not, any needed secrets must be copied into the initrd + and thus added to the store. + ''; + }; + fileSystems = mkOption { options.neededForBoot = mkOption { default = false; @@ -404,9 +492,8 @@ in } ]; - system.build.bootStage1 = bootStage1; - system.build.initialRamdisk = initialRamdisk; - system.build.extraUtils = extraUtils; + system.build = + { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; }; system.requiredKernelConfig = with config.lib.kernelConfig; [ (isYes "TMPFS") diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh index f827e530f877..46aed44bf10f 100644 --- a/nixos/modules/system/boot/stage-2-init.sh +++ b/nixos/modules/system/boot/stage-2-init.sh @@ -2,7 +2,22 @@ systemConfig=@systemConfig@ -export HOME=/root +export HOME=/root PATH="@path@" + + +# Process the kernel command line. +for o in $(</proc/cmdline); do + case $o in + boot.debugtrace) + # Show each command. + set -x + ;; + resume=*) + set -- $(IFS==; echo $o) + resumeDevice=$2 + ;; + esac +done # Print a greeting. @@ -11,21 +26,6 @@ echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m" echo -# Set the PATH. -setPath() { - local dirs="$1" - export PATH=/empty - for i in $dirs; do - PATH=$PATH:$i/bin - if test -e $i/sbin; then - PATH=$PATH:$i/sbin - fi - done -} - -setPath "@path@" - - # Normally, stage 1 mounts the root filesystem read/writable. # However, in some environments, stage 2 is executed directly, and the # root is read-only. So make it writable here. @@ -61,7 +61,9 @@ echo "booting system configuration $systemConfig" > /dev/kmsg chown -f 0:30000 /nix/store chmod -f 1775 /nix/store if [ -n "@readOnlyStore@" ]; then - if ! readonly-mountpoint /nix/store; then + if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then + # FIXME when linux < 4.5 is EOL, switch to atomic bind mounts + #mount /nix/store /nix/store -o bind,remount,ro mount --bind /nix/store /nix/store mount -o remount,ro,bind /nix/store fi @@ -75,31 +77,12 @@ rm -f /etc/mtab* # not that we care about stale locks ln -s /proc/mounts /etc/mtab -# Process the kernel command line. -for o in $(cat /proc/cmdline); do - case $o in - boot.debugtrace) - # Show each command. - set -x - ;; - resume=*) - set -- $(IFS==; echo $o) - resumeDevice=$2 - ;; - esac -done - - # More special file systems, initialise required directories. [ -e /proc/bus/usb ] && mount -t usbfs usbfs /proc/bus/usb # UML doesn't have USB by default mkdir -m 01777 -p /tmp -mkdir -m 0755 -p /var /var/log /var/lib /var/db -mkdir -m 0755 -p /nix/var -mkdir -m 0700 -p /root -chmod 0700 /root -mkdir -m 0755 -p /bin # for the /bin/sh symlink -mkdir -m 0755 -p /home -mkdir -m 0755 -p /etc/nixos +mkdir -m 0755 -p /var/{log,lib,db} /nix/var /etc/nixos/ \ + /run/lock /home /bin # for the /bin/sh symlink +install -m 0700 -d /root # Miscellaneous boot time cleanup. @@ -111,9 +94,6 @@ rm -f /etc/{group,passwd,shadow}.lock rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots -mkdir -m 0755 -p /run/lock - - # For backwards compatibility, symlink /var/run to /run, and /var/lock # to /run/lock. ln -s /run /var/run @@ -127,8 +107,8 @@ fi # Use /etc/resolv.conf supplied by systemd-nspawn, if applicable. -if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then - cat /etc/resolv.conf | resolvconf -m 1000 -a host +if [ -n "@useHostResolvConf@" ] && [ -e /etc/resolv.conf ]; then + resolvconf -m 1000 -a host </etc/resolv.conf fi # Log the script output to /dev/kmsg or /run/log/stage-2-init.log. diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix index 7e4ec2a4a670..8db6d2d2f734 100644 --- a/nixos/modules/system/boot/stage-2.nix +++ b/nixos/modules/system/boot/stage-2.nix @@ -7,15 +7,6 @@ let kernel = config.boot.kernelPackages.kernel; activateConfiguration = config.system.activationScripts.script; - readonlyMountpoint = pkgs.stdenv.mkDerivation { - name = "readonly-mountpoint"; - unpackPhase = "true"; - installPhase = '' - mkdir -p $out/bin - cc -O3 ${./readonly-mountpoint.c} -o $out/bin/readonly-mountpoint - ''; - }; - bootStage2 = pkgs.substituteAll { src = ./stage-2-init.sh; shellDebug = "${pkgs.bashInteractive}/bin/bash"; @@ -23,11 +14,11 @@ let inherit (config.nix) readOnlyStore; inherit (config.networking) useHostResolvConf; inherit (config.system.build) earlyMountScript; - path = - [ pkgs.coreutils - pkgs.utillinux - pkgs.openresolv - ] ++ optional config.nix.readOnlyStore readonlyMountpoint; + path = lib.makeBinPath [ + pkgs.coreutils + pkgs.utillinux + pkgs.openresolv + ]; postBootCommands = pkgs.writeText "local-cmds" '' ${config.boot.postBootCommands} diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix index 997770b8beca..7c01f8ea9b7f 100644 --- a/nixos/modules/system/boot/systemd-lib.nix +++ b/nixos/modules/system/boot/systemd-lib.nix @@ -10,7 +10,7 @@ rec { makeUnit = name: unit: let - pathSafeName = lib.replaceChars ["@" ":" "\\"] ["-" "-" "-"] name; + pathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""] name; in if unit.enable then pkgs.runCommand "unit-${pathSafeName}" @@ -159,7 +159,13 @@ rec { fi done - # Created .wants and .requires symlinks from the wantedBy and + # Create service aliases from aliases option. + ${concatStrings (mapAttrsToList (name: unit: + concatMapStrings (name2: '' + ln -sfn '${name}' $out/'${name2}' + '') unit.aliases) units)} + + # Create .wants and .requires symlinks from the wantedBy and # requiredBy options. ${concatStrings (mapAttrsToList (name: unit: concatMapStrings (name2: '' diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix index 69af23981485..9be10a8283ed 100644 --- a/nixos/modules/system/boot/systemd-unit-options.nix +++ b/nixos/modules/system/boot/systemd-unit-options.nix @@ -52,6 +52,12 @@ in rec { description = "Units that want (i.e. depend on) this unit."; }; + aliases = mkOption { + default = []; + type = types.listOf types.str; + description = "Aliases of that unit."; + }; + }; concreteUnitOptions = sharedOptions // { @@ -322,7 +328,7 @@ in rec { Automatically start this unit at the given date/time, which must be in the format described in <citerefentry><refentrytitle>systemd.time</refentrytitle> - <manvolnum>5</manvolnum></citerefentry>. This is equivalent + <manvolnum>7</manvolnum></citerefentry>. This is equivalent to adding a corresponding timer unit with <option>OnCalendar</option> set to the value given here. ''; @@ -369,9 +375,9 @@ in rec { Each attribute in this set specifies an option in the <literal>[Timer]</literal> section of the unit. See <citerefentry><refentrytitle>systemd.timer</refentrytitle> - <manvolnum>5</manvolnum></citerefentry> and + <manvolnum>7</manvolnum></citerefentry> and <citerefentry><refentrytitle>systemd.time</refentrytitle> - <manvolnum>5</manvolnum></citerefentry> for details. + <manvolnum>7</manvolnum></citerefentry> for details. ''; }; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index a2ee51669715..f798862513cb 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -17,6 +17,7 @@ let "busnames.target" "sysinit.target" "sockets.target" + "exit.target" "graphical.target" "multi-user.target" "network.target" @@ -41,11 +42,14 @@ let "systemd-udevd.service" "systemd-udev-settle.service" "systemd-udev-trigger.service" + # hwdb.bin is managed by NixOS + # "systemd-hwdb-update.service" # Consoles. "getty.target" "getty@.service" "serial-getty@.service" + "console-getty.service" "container-getty@.service" "systemd-vconsole-setup.service" @@ -58,7 +62,6 @@ let # Login stuff. "systemd-logind.service" "autovt@.service" - #"systemd-vconsole-setup.service" "systemd-user-sessions.service" "dbus-org.freedesktop.login1.service" "dbus-org.freedesktop.machine1.service" @@ -72,6 +75,7 @@ let "systemd-journal-flush.service" "systemd-journal-gatewayd.socket" "systemd-journal-gatewayd.service" + "systemd-journal-catalog-update.service" "systemd-journald-audit.socket" "systemd-journald-dev-log.socket" "syslog.socket" @@ -104,6 +108,7 @@ let "systemd-random-seed.service" "systemd-backlight@.service" "systemd-rfkill.service" + "systemd-rfkill.socket" # Hibernate / suspend. "hibernate.target" @@ -111,8 +116,8 @@ let "sleep.target" "hybrid-sleep.target" "systemd-hibernate.service" - "systemd-suspend.service" "systemd-hybrid-sleep.service" + "systemd-suspend.service" # Reboot stuff. "reboot.target" @@ -136,10 +141,10 @@ let # Slices / containers. "slices.target" - "-.slice" "system.slice" "user.slice" "machine.slice" + "machines.target" "systemd-machined.service" "systemd-nspawn@.service" @@ -162,12 +167,12 @@ let "systemd-localed.service" "systemd-hostnamed.service" "systemd-binfmt.service" + "systemd-exit.service" ] ++ cfg.additionalUpstreamSystemUnits; upstreamSystemWants = - [ #"basic.target.wants" - "sysinit.target.wants" + [ "sysinit.target.wants" "sockets.target.wants" "local-fs.target.wants" "multi-user.target.wants" @@ -176,11 +181,18 @@ let upstreamUserUnits = [ "basic.target" + "bluetooth.target" + "busnames.target" "default.target" "exit.target" + "graphical-session-pre.target" + "graphical-session.target" "paths.target" + "printer.target" "shutdown.target" + "smartcard.target" "sockets.target" + "sound.target" "systemd-exit.service" "timers.target" ]; @@ -301,7 +313,7 @@ let ''; targetToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = '' [Unit] @@ -310,14 +322,14 @@ let }; serviceToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Service] ${let env = cfg.globalEnvironment // def.environment; in concatMapStrings (n: let s = optionalString (env."${n}" != null) - "Environment=\"${n}=${env.${n}}\"\n"; + "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n"; in if stringLength s >= 2048 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)} ${if def.reloadIfChanged then '' X-ReloadIfChanged=true @@ -330,7 +342,7 @@ let }; socketToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Socket] @@ -340,7 +352,7 @@ let }; timerToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Timer] @@ -349,7 +361,7 @@ let }; pathToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Path] @@ -358,7 +370,7 @@ let }; mountToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Mount] @@ -367,7 +379,7 @@ let }; automountToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Automount] @@ -376,7 +388,7 @@ let }; sliceToUnit = name: def: - { inherit (def) wantedBy requiredBy enable; + { inherit (def) aliases wantedBy requiredBy enable; text = commonUnitText def + '' [Slice] @@ -741,7 +753,8 @@ in # Keep a persistent journal. Note that systemd-tmpfiles will # set proper ownership/permissions. - mkdir -m 0700 -p /var/log/journal + # FIXME: revert to 0700 with systemd v233. + mkdir -m 0750 -p /var/log/journal ''; users.extraUsers.systemd-network.uid = config.ids.uids.systemd-network; @@ -816,7 +829,8 @@ in # Some overrides to upstream units. systemd.services."systemd-backlight@".restartIfChanged = false; - systemd.services."systemd-rfkill@".restartIfChanged = false; + systemd.services."systemd-fsck@".restartIfChanged = false; + systemd.services."systemd-fsck@".path = [ config.system.path ]; systemd.services."user@".restartIfChanged = false; systemd.services.systemd-journal-flush.restartIfChanged = false; systemd.services.systemd-random-seed.restartIfChanged = false; @@ -829,6 +843,7 @@ in systemd.services.systemd-journald.stopIfChanged = false; systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true; systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true; + systemd.targets.network-online.wantedBy = [ "multi-user.target" ]; systemd.services.systemd-binfmt.wants = [ "proc-sys-fs-binfmt_misc.automount" ]; # Don't bother with certain units in containers. |