1# This module creates netboot media containing the given NixOS 2# configuration. 3 4{ 5 config, 6 lib, 7 pkgs, 8 modulesPath, 9 ... 10}: 11 12with lib; 13 14{ 15 imports = [ 16 ../../image/file-options.nix 17 ]; 18 19 options = { 20 21 netboot.squashfsCompression = mkOption { 22 default = "zstd -Xcompression-level 19"; 23 description = '' 24 Compression settings to use for the squashfs nix store. 25 ''; 26 example = "zstd -Xcompression-level 6"; 27 type = types.str; 28 }; 29 30 netboot.storeContents = mkOption { 31 example = literalExpression "[ pkgs.stdenv ]"; 32 description = '' 33 This option lists additional derivations to be included in the 34 Nix store in the generated netboot image. 35 ''; 36 }; 37 38 }; 39 40 config = { 41 # Don't build the GRUB menu builder script, since we don't need it 42 # here and it causes a cyclic dependency. 43 boot.loader.grub.enable = false; 44 45 fileSystems."/" = mkImageMediaOverride { 46 fsType = "tmpfs"; 47 options = [ "mode=0755" ]; 48 }; 49 50 # In stage 1, mount a tmpfs on top of /nix/store (the squashfs 51 # image) to make this a live CD. 52 fileSystems."/nix/.ro-store" = mkImageMediaOverride { 53 fsType = "squashfs"; 54 device = "../nix-store.squashfs"; 55 options = [ 56 "loop" 57 ] 58 ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi"; 59 neededForBoot = true; 60 }; 61 62 fileSystems."/nix/.rw-store" = mkImageMediaOverride { 63 fsType = "tmpfs"; 64 options = [ "mode=0755" ]; 65 neededForBoot = true; 66 }; 67 68 fileSystems."/nix/store" = mkImageMediaOverride { 69 overlay = { 70 lowerdir = [ "/nix/.ro-store" ]; 71 upperdir = "/nix/.rw-store/store"; 72 workdir = "/nix/.rw-store/work"; 73 }; 74 neededForBoot = true; 75 }; 76 77 boot.initrd.availableKernelModules = [ 78 "squashfs" 79 "overlay" 80 ]; 81 82 boot.initrd.kernelModules = [ 83 "loop" 84 "overlay" 85 ]; 86 87 # Closures to be copied to the Nix store, namely the init 88 # script and the top-level system configuration directory. 89 netboot.storeContents = [ config.system.build.toplevel ]; 90 91 # Create the squashfs image that contains the Nix store. 92 system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix { 93 storeContents = config.netboot.storeContents; 94 comp = config.netboot.squashfsCompression; 95 }; 96 97 # Create the initrd 98 system.build.netbootRamdisk = pkgs.makeInitrdNG { 99 inherit (config.boot.initrd) compressor; 100 prepend = [ "${config.system.build.initialRamdisk}/initrd" ]; 101 102 contents = [ 103 { 104 source = config.system.build.squashfsStore; 105 target = "/nix-store.squashfs"; 106 } 107 ]; 108 }; 109 110 system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" '' 111 #!ipxe 112 # Use the cmdline variable to allow the user to specify custom kernel params 113 # when chainloading this script from other iPXE scripts like netboot.xyz 114 kernel ${pkgs.stdenv.hostPlatform.linux-kernel.target} init=${config.system.build.toplevel}/init initrd=initrd ${toString config.boot.kernelParams} ''${cmdline} 115 initrd initrd 116 boot 117 ''; 118 119 # A script invoking kexec on ./bzImage and ./initrd.gz. 120 # Usually used through system.build.kexecTree, but exposed here for composability. 121 system.build.kexecScript = pkgs.writeScript "kexec-boot" '' 122 #!/usr/bin/env bash 123 if ! kexec -v >/dev/null 2>&1; then 124 echo "kexec not found: please install kexec-tools" 2>&1 125 exit 1 126 fi 127 SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 128 kexec --load ''${SCRIPT_DIR}/bzImage \ 129 --initrd=''${SCRIPT_DIR}/initrd.gz \ 130 --command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}" 131 kexec -e 132 ''; 133 134 # A tree containing initrd.gz, bzImage and a kexec-boot script. 135 system.build.kexecTree = pkgs.linkFarm "kexec-tree" [ 136 { 137 name = "initrd.gz"; 138 path = "${config.system.build.netbootRamdisk}/initrd"; 139 } 140 { 141 name = "bzImage"; 142 path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}"; 143 } 144 { 145 name = "kexec-boot"; 146 path = config.system.build.kexecScript; 147 } 148 ]; 149 150 image.extension = "tar.xz"; 151 image.filePath = "tarball/${config.image.fileName}"; 152 system.nixos.tags = [ "kexec" ]; 153 system.build.image = config.system.build.kexecTarball; 154 system.build.kexecTarball = 155 pkgs.callPackage "${toString modulesPath}/../lib/make-system-tarball.nix" 156 { 157 fileName = config.image.baseName; 158 storeContents = [ 159 { 160 object = config.system.build.kexecScript; 161 symlink = "/kexec_nixos"; 162 } 163 ]; 164 contents = [ ]; 165 }; 166 167 boot.loader.timeout = 10; 168 169 boot.postBootCommands = '' 170 # After booting, register the contents of the Nix store 171 # in the Nix database in the tmpfs. 172 ${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration 173 174 # nixos-rebuild also requires a "system" profile and an 175 # /etc/NIXOS tag. 176 touch /etc/NIXOS 177 ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 178 179 # Set password for user nixos if specified on cmdline 180 # Allows using nixos-anywhere in headless environments 181 for o in $(</proc/cmdline); do 182 case "$o" in 183 live.nixos.passwordHash=*) 184 set -- $(IFS==; echo $o) 185 ${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && ${pkgs.shadow}/bin/usermod -p "$2" root 186 ;; 187 live.nixos.password=*) 188 set -- $(IFS==; echo $o) 189 ${pkgs.gnugrep}/bin/grep -q "root::" /etc/shadow && echo "root:$2" | ${pkgs.shadow}/bin/chpasswd 190 ;; 191 esac 192 done 193 ''; 194 }; 195}