at 23.11-pre 6.4 kB view raw
1{ config, lib, pkgs, ... }: with lib; 2let 3 cfg = config.boot.iscsi-initiator; 4in 5{ 6 # If you're booting entirely off another machine you may want to add 7 # this snippet to always boot the latest "system" version. It is not 8 # enabled by default in case you have an initrd on a local disk: 9 # 10 # boot.initrd.postMountCommands = '' 11 # ln -sfn /nix/var/nix/profiles/system/init /mnt-root/init 12 # stage2Init=/init 13 # ''; 14 # 15 # Note: Theoretically you might want to connect to multiple portals and 16 # log in to multiple targets, however the authors of this module so far 17 # don't have the need or expertise to reasonably implement it. Also, 18 # consider carefully before making your boot chain depend on multiple 19 # machines to be up. 20 options.boot.iscsi-initiator = with types; { 21 name = mkOption { 22 description = lib.mdDoc '' 23 Name of the iSCSI initiator to boot from. Note, booting from iscsi 24 requires networkd based networking. 25 ''; 26 default = null; 27 example = "iqn.2020-08.org.linux-iscsi.initiatorhost:example"; 28 type = nullOr str; 29 }; 30 31 discoverPortal = mkOption { 32 description = lib.mdDoc '' 33 iSCSI portal to boot from. 34 ''; 35 default = null; 36 example = "192.168.1.1:3260"; 37 type = nullOr str; 38 }; 39 40 target = mkOption { 41 description = lib.mdDoc '' 42 Name of the iSCSI target to boot from. 43 ''; 44 default = null; 45 example = "iqn.2020-08.org.linux-iscsi.targethost:example"; 46 type = nullOr str; 47 }; 48 49 logLevel = mkOption { 50 description = lib.mdDoc '' 51 Higher numbers elicits more logs. 52 ''; 53 default = 1; 54 example = 8; 55 type = int; 56 }; 57 58 loginAll = mkOption { 59 description = lib.mdDoc '' 60 Do not log into a specific target on the portal, but to all that we discover. 61 This overrides setting target. 62 ''; 63 type = bool; 64 default = false; 65 }; 66 67 extraIscsiCommands = mkOption { 68 description = lib.mdDoc "Extra iscsi commands to run in the initrd."; 69 default = ""; 70 type = lines; 71 }; 72 73 extraConfig = mkOption { 74 description = lib.mdDoc "Extra lines to append to /etc/iscsid.conf"; 75 default = null; 76 type = nullOr lines; 77 }; 78 79 extraConfigFile = mkOption { 80 description = lib.mdDoc '' 81 Append an additional file's contents to `/etc/iscsid.conf`. Use a non-store path 82 and store passwords in this file. Note: the file specified here must be available 83 in the initrd, see: `boot.initrd.secrets`. 84 ''; 85 default = null; 86 type = nullOr str; 87 }; 88 }; 89 90 config = mkIf (cfg.name != null) { 91 # The "scripted" networking configuration (ie: non-networkd) 92 # doesn't properly order the start and stop of the interfaces, and the 93 # network interfaces are torn down before unmounting disks. Since this 94 # module is specifically for very-early-boot network mounts, we need 95 # the network to stay on. 96 # 97 # We could probably fix the scripted options to properly order, but I'm 98 # not inclined to invest that time today. Hopefully this gets users far 99 # enough along and they can just use networkd. 100 networking.useNetworkd = true; 101 networking.useDHCP = false; # Required to set useNetworkd = true 102 103 boot.initrd = { 104 network.enable = true; 105 106 # By default, the stage-1 disables the network and resets the interfaces 107 # on startup. Since our startup disks are on the network, we can't let 108 # the network not work. 109 network.flushBeforeStage2 = false; 110 111 kernelModules = [ "iscsi_tcp" ]; 112 113 extraUtilsCommands = '' 114 copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsid 115 copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsiadm 116 ${optionalString (!config.boot.initrd.network.ssh.enable) "cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib"} 117 118 mkdir -p $out/etc/iscsi 119 cp ${config.environment.etc.hosts.source} $out/etc/hosts 120 cp ${pkgs.openiscsi}/etc/iscsi/iscsid.conf $out/etc/iscsi/iscsid.fragment.conf 121 chmod +w $out/etc/iscsi/iscsid.fragment.conf 122 cat << 'EOF' >> $out/etc/iscsi/iscsid.fragment.conf 123 ${optionalString (cfg.extraConfig != null) cfg.extraConfig} 124 EOF 125 ''; 126 127 extraUtilsCommandsTest = '' 128 $out/bin/iscsiadm --version 129 ''; 130 131 preLVMCommands = let 132 extraCfgDumper = optionalString (cfg.extraConfigFile != null) '' 133 if [ -f "${cfg.extraConfigFile}" ]; then 134 printf "\n# The following is from ${cfg.extraConfigFile}:\n" 135 cat "${cfg.extraConfigFile}" 136 else 137 echo "Warning: boot.iscsi-initiator.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2 138 fi 139 ''; 140 in '' 141 ${optionalString (!config.boot.initrd.network.ssh.enable) '' 142 # stolen from initrd-ssh.nix 143 echo 'root:x:0:0:root:/root:/bin/ash' > /etc/passwd 144 echo 'passwd: files' > /etc/nsswitch.conf 145 ''} 146 147 cp -f $extraUtils/etc/hosts /etc/hosts 148 149 mkdir -p /etc/iscsi /run/lock/iscsi 150 echo "InitiatorName=${cfg.name}" > /etc/iscsi/initiatorname.iscsi 151 152 ( 153 cat "$extraUtils/etc/iscsi/iscsid.fragment.conf" 154 printf "\n" 155 ${optionalString cfg.loginAll ''echo "node.startup = automatic"''} 156 ${extraCfgDumper} 157 ) > /etc/iscsi/iscsid.conf 158 159 iscsid --foreground --no-pid-file --debug ${toString cfg.logLevel} & 160 iscsiadm --mode discoverydb \ 161 --type sendtargets \ 162 --discover \ 163 --portal ${escapeShellArg cfg.discoverPortal} \ 164 --debug ${toString cfg.logLevel} 165 166 ${if cfg.loginAll then '' 167 iscsiadm --mode node --loginall all 168 '' else '' 169 iscsiadm --mode node --targetname ${escapeShellArg cfg.target} --login 170 ''} 171 172 ${cfg.extraIscsiCommands} 173 174 pkill -9 iscsid 175 ''; 176 }; 177 178 services.openiscsi = { 179 enable = true; 180 inherit (cfg) name; 181 }; 182 183 assertions = [ 184 { 185 assertion = cfg.loginAll -> cfg.target == null; 186 message = "iSCSI target name is set while login on all portals is enabled."; 187 } 188 { 189 assertion = !config.boot.initrd.systemd.enable; 190 message = "systemd stage 1 does not support iscsi yet."; 191 } 192 ]; 193 }; 194}