at 22.05-pre 6.7 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.boot.initrd.network.ssh; 8 9in 10 11{ 12 13 options.boot.initrd.network.ssh = { 14 enable = mkOption { 15 type = types.bool; 16 default = false; 17 description = '' 18 Start SSH service during initrd boot. It can be used to debug failing 19 boot on a remote server, enter pasphrase for an encrypted partition etc. 20 Service is killed when stage-1 boot is finished. 21 22 The sshd configuration is largely inherited from 23 <option>services.openssh</option>. 24 ''; 25 }; 26 27 port = mkOption { 28 type = types.int; 29 default = 22; 30 description = '' 31 Port on which SSH initrd service should listen. 32 ''; 33 }; 34 35 shell = mkOption { 36 type = types.str; 37 default = "/bin/ash"; 38 description = '' 39 Login shell of the remote user. Can be used to limit actions user can do. 40 ''; 41 }; 42 43 hostKeys = mkOption { 44 type = types.listOf (types.either types.str types.path); 45 default = []; 46 example = [ 47 "/etc/secrets/initrd/ssh_host_rsa_key" 48 "/etc/secrets/initrd/ssh_host_ed25519_key" 49 ]; 50 description = '' 51 Specify SSH host keys to import into the initrd. 52 53 To generate keys, use 54 <citerefentry><refentrytitle>ssh-keygen</refentrytitle><manvolnum>1</manvolnum></citerefentry>: 55 56 <screen> 57 <prompt># </prompt>ssh-keygen -t rsa -N "" -f /etc/secrets/initrd/ssh_host_rsa_key 58 <prompt># </prompt>ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key 59 </screen> 60 61 <warning> 62 <para> 63 Unless your bootloader supports initrd secrets, these keys 64 are stored insecurely in the global Nix store. Do NOT use 65 your regular SSH host private keys for this purpose or 66 you'll expose them to regular users! 67 </para> 68 <para> 69 Additionally, even if your initrd supports secrets, if 70 you're using initrd SSH to unlock an encrypted disk then 71 using your regular host keys exposes the private keys on 72 your unencrypted boot partition. 73 </para> 74 </warning> 75 ''; 76 }; 77 78 authorizedKeys = mkOption { 79 type = types.listOf types.str; 80 default = config.users.users.root.openssh.authorizedKeys.keys; 81 defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keys"; 82 description = '' 83 Authorized keys for the root user on initrd. 84 ''; 85 }; 86 87 extraConfig = mkOption { 88 type = types.lines; 89 default = ""; 90 description = "Verbatim contents of <filename>sshd_config</filename>."; 91 }; 92 }; 93 94 imports = 95 map (opt: mkRemovedOptionModule ([ "boot" "initrd" "network" "ssh" ] ++ [ opt ]) '' 96 The initrd SSH functionality now uses OpenSSH rather than Dropbear. 97 98 If you want to keep your existing initrd SSH host keys, convert them with 99 $ dropbearconvert dropbear openssh dropbear_host_$type_key ssh_host_$type_key 100 and then set options.boot.initrd.network.ssh.hostKeys. 101 '') [ "hostRSAKey" "hostDSSKey" "hostECDSAKey" ]; 102 103 config = let 104 # Nix complains if you include a store hash in initrd path names, so 105 # as an awful hack we drop the first character of the hash. 106 initrdKeyPath = path: if isString path 107 then path 108 else let name = builtins.baseNameOf path; in 109 builtins.unsafeDiscardStringContext ("/etc/ssh/" + 110 substring 1 (stringLength name) name); 111 112 sshdCfg = config.services.openssh; 113 114 sshdConfig = '' 115 Port ${toString cfg.port} 116 117 PasswordAuthentication no 118 ChallengeResponseAuthentication no 119 120 ${flip concatMapStrings cfg.hostKeys (path: '' 121 HostKey ${initrdKeyPath path} 122 '')} 123 124 KexAlgorithms ${concatStringsSep "," sshdCfg.kexAlgorithms} 125 Ciphers ${concatStringsSep "," sshdCfg.ciphers} 126 MACs ${concatStringsSep "," sshdCfg.macs} 127 128 LogLevel ${sshdCfg.logLevel} 129 130 ${if sshdCfg.useDns then '' 131 UseDNS yes 132 '' else '' 133 UseDNS no 134 ''} 135 136 ${cfg.extraConfig} 137 ''; 138 in mkIf (config.boot.initrd.network.enable && cfg.enable) { 139 assertions = [ 140 { 141 assertion = cfg.authorizedKeys != []; 142 message = "You should specify at least one authorized key for initrd SSH"; 143 } 144 145 { 146 assertion = cfg.hostKeys != []; 147 message = '' 148 You must now pre-generate the host keys for initrd SSH. 149 See the boot.initrd.network.ssh.hostKeys documentation 150 for instructions. 151 ''; 152 } 153 ]; 154 155 boot.initrd.extraUtilsCommands = '' 156 copy_bin_and_libs ${pkgs.openssh}/bin/sshd 157 cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib 158 ''; 159 160 boot.initrd.extraUtilsCommandsTest = '' 161 # sshd requires a host key to check config, so we pass in the test's 162 tmpkey="$(mktemp initrd-ssh-testkey.XXXXXXXXXX)" 163 cp "${../../../tests/initrd-network-ssh/ssh_host_ed25519_key}" "$tmpkey" 164 # keys from Nix store are world-readable, which sshd doesn't like 165 chmod 600 "$tmpkey" 166 echo -n ${escapeShellArg sshdConfig} | 167 $out/bin/sshd -t -f /dev/stdin \ 168 -h "$tmpkey" 169 rm "$tmpkey" 170 ''; 171 172 boot.initrd.network.postCommands = '' 173 echo '${cfg.shell}' > /etc/shells 174 echo 'root:x:0:0:root:/root:${cfg.shell}' > /etc/passwd 175 echo 'sshd:x:1:1:sshd:/var/empty:/bin/nologin' >> /etc/passwd 176 echo 'passwd: files' > /etc/nsswitch.conf 177 178 mkdir -p /var/log /var/empty 179 touch /var/log/lastlog 180 181 mkdir -p /etc/ssh 182 echo -n ${escapeShellArg sshdConfig} > /etc/ssh/sshd_config 183 184 echo "export PATH=$PATH" >> /etc/profile 185 echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> /etc/profile 186 187 mkdir -p /root/.ssh 188 ${concatStrings (map (key: '' 189 echo ${escapeShellArg key} >> /root/.ssh/authorized_keys 190 '') cfg.authorizedKeys)} 191 192 ${flip concatMapStrings cfg.hostKeys (path: '' 193 # keys from Nix store are world-readable, which sshd doesn't like 194 chmod 0600 "${initrdKeyPath path}" 195 '')} 196 197 /bin/sshd -e 198 ''; 199 200 boot.initrd.postMountCommands = '' 201 # Stop sshd cleanly before stage 2. 202 # 203 # If you want to keep it around to debug post-mount SSH issues, 204 # run `touch /.keep_sshd` (either from an SSH session or in 205 # another initrd hook like preDeviceCommands). 206 if ! [ -e /.keep_sshd ]; then 207 pkill -x sshd 208 fi 209 ''; 210 211 boot.initrd.secrets = listToAttrs 212 (map (path: nameValuePair (initrdKeyPath path) path) cfg.hostKeys); 213 }; 214 215}