12 cfg = config.boot.initrd.network.ssh;
13 shell = if cfg.shell == null then "/bin/ash" else cfg.shell;
14 inherit (config.programs.ssh) package;
18 initrd = config.boot.initrd;
20 (initrd.network.enable || initrd.systemd.network.enable) && cfg.enable;
26 options.boot.initrd.network.ssh = {
31 Start SSH service during initrd boot. It can be used to debug failing
32 boot on a remote server, enter pasphrase for an encrypted partition etc.
33 Service is killed when stage-1 boot is finished.
35 The sshd configuration is largely inherited from
36 {option}`services.openssh`.
44 Port on which SSH initrd service should listen.
49 type = types.nullOr types.str;
51 defaultText = ''"/bin/ash"'';
53 Login shell of the remote user. Can be used to limit actions user can do.
58 type = types.listOf (types.either types.str types.path);
61 "/etc/secrets/initrd/ssh_host_rsa_key"
62 "/etc/secrets/initrd/ssh_host_ed25519_key"
65 Specify SSH host keys to import into the initrd.
68 {manpage}`ssh-keygen(1)`
72 ssh-keygen -t rsa -N "" -f /etc/secrets/initrd/ssh_host_rsa_key
73 ssh-keygen -t ed25519 -N "" -f /etc/secrets/initrd/ssh_host_ed25519_key
77 Unless your bootloader supports initrd secrets, these keys
78 are stored insecurely in the global Nix store. Do NOT use
79 your regular SSH host private keys for this purpose or
80 you'll expose them to regular users!
82 Additionally, even if your initrd supports secrets, if
83 you're using initrd SSH to unlock an encrypted disk then
84 using your regular host keys exposes the private keys on
85 your unencrypted boot partition.
90 ignoreEmptyHostKeys = mkOption {
94 Allow leaving {option}`config.boot.initrd.network.ssh.hostKeys` empty,
95 to deploy ssh host keys out of band.
99 authorizedKeys = mkOption {
100 type = types.listOf types.str;
101 default = config.users.users.root.openssh.authorizedKeys.keys;
102 defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keys";
104 Authorized keys for the root user on initrd.
105 You can combine the `authorizedKeys` and `authorizedKeyFiles` options.
108 "ssh-rsa AAAAB3NzaC1yc2etc/etc/etcjwrsh8e596z6J0l7 example@host"
109 "ssh-ed25519 AAAAC3NzaCetcetera/etceteraJZMfk3QPfQ foo@bar"
113 authorizedKeyFiles = mkOption {
114 type = types.listOf types.path;
115 default = config.users.users.root.openssh.authorizedKeys.keyFiles;
116 defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keyFiles";
118 Authorized keys taken from files for the root user on initrd.
119 You can combine the `authorizedKeyFiles` and `authorizedKeys` options.
123 extraConfig = mkOption {
126 description = "Verbatim contents of {file}`sshd_config`.";
134 mkRemovedOptionModule
145 The initrd SSH functionality now uses OpenSSH rather than Dropbear.
147 If you want to keep your existing initrd SSH host keys, convert them with
148 $ dropbearconvert dropbear openssh dropbear_host_$type_key ssh_host_$type_key
149 and then set options.boot.initrd.network.ssh.hostKeys.
160 # Nix complains if you include a store hash in initrd path names, so
161 # as an awful hack we drop the first character of the hash.
164 if isString path then
168 name = builtins.baseNameOf path;
170 builtins.unsafeDiscardStringContext ("/etc/ssh/" + substring 1 (stringLength name) name);
172 sshdCfg = config.services.openssh;
177 Port ${toString cfg.port}
179 PasswordAuthentication no
180 AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys2 /etc/ssh/authorized_keys.d/%u
181 ChallengeResponseAuthentication no
183 ${flip concatMapStrings cfg.hostKeys (path: ''
184 HostKey ${initrdKeyPath path}
188 + lib.optionalString (sshdCfg.settings.KexAlgorithms != null) ''
189 KexAlgorithms ${concatStringsSep "," sshdCfg.settings.KexAlgorithms}
191 + lib.optionalString (sshdCfg.settings.Ciphers != null) ''
192 Ciphers ${concatStringsSep "," sshdCfg.settings.Ciphers}
194 + lib.optionalString (sshdCfg.settings.Macs != null) ''
195 MACs ${concatStringsSep "," sshdCfg.settings.Macs}
199 LogLevel ${sshdCfg.settings.LogLevel}
202 if sshdCfg.settings.UseDns then
212 ${optionalString (!config.boot.initrd.systemd.enable) ''
213 SshdSessionPath /bin/sshd-session
222 assertion = cfg.authorizedKeys != [ ] || cfg.authorizedKeyFiles != [ ];
223 message = "You should specify at least one authorized key for initrd SSH";
227 assertion = (cfg.hostKeys != [ ]) || cfg.ignoreEmptyHostKeys;
229 You must now pre-generate the host keys for initrd SSH.
230 See the boot.initrd.network.ssh.hostKeys documentation
236 warnings = lib.optional (config.boot.initrd.systemd.enable && cfg.shell != null) ''
237 Please set 'boot.initrd.systemd.users.root.shell' instead of 'boot.initrd.network.ssh.shell'
240 boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
241 copy_bin_and_libs ${package}/bin/sshd
242 copy_bin_and_libs ${package}/libexec/sshd-session
243 cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib
246 boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable) ''
247 # sshd requires a host key to check config, so we pass in the test's
248 tmpkey="$(mktemp initrd-ssh-testkey.XXXXXXXXXX)"
249 cp "${../../../tests/initrd-network-ssh/ssh_host_ed25519_key}" "$tmpkey"
250 # keys from Nix store are world-readable, which sshd doesn't like
252 echo -n ${escapeShellArg sshdConfig} |
253 $out/bin/sshd -t -f /dev/stdin \
258 boot.initrd.network.postCommands = mkIf (!config.boot.initrd.systemd.enable) ''
259 echo '${shell}' > /etc/shells
260 echo 'root:x:0:0:root:/root:${shell}' > /etc/passwd
261 echo 'sshd:x:1:1:sshd:/var/empty:/bin/nologin' >> /etc/passwd
262 echo 'passwd: files' > /etc/nsswitch.conf
264 mkdir -p /var/log /var/empty
265 touch /var/log/lastlog
268 echo -n ${escapeShellArg sshdConfig} > /etc/ssh/sshd_config
270 echo "export PATH=$PATH" >> /etc/profile
271 echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> /etc/profile
276 echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
277 '') cfg.authorizedKeys
281 cat ${keyFile} >> /root/.ssh/authorized_keys
282 '') cfg.authorizedKeyFiles
285 ${flip concatMapStrings cfg.hostKeys (path: ''
286 # keys from Nix store are world-readable, which sshd doesn't like
287 chmod 0600 "${initrdKeyPath path}"
293 boot.initrd.postMountCommands = mkIf (!config.boot.initrd.systemd.enable) ''
294 # Stop sshd cleanly before stage 2.
296 # If you want to keep it around to debug post-mount SSH issues,
297 # run `touch /.keep_sshd` (either from an SSH session or in
298 # another initrd hook like preDeviceCommands).
299 if ! [ -e /.keep_sshd ]; then
304 boot.initrd.secrets = listToAttrs (
305 map (path: nameValuePair (initrdKeyPath path) path) cfg.hostKeys
308 # Systemd initrd stuff
309 boot.initrd.systemd = mkIf config.boot.initrd.systemd.enable {
318 users.root.shell = mkIf (
319 config.boot.initrd.network.ssh.shell != null
320 ) config.boot.initrd.network.ssh.shell;
323 "/etc/ssh/sshd_config".text = sshdConfig;
324 "/etc/ssh/authorized_keys.d/root".text = concatStringsSep "\n" (
325 config.boot.initrd.network.ssh.authorizedKeys
326 ++ (map (file: lib.fileContents file) config.boot.initrd.network.ssh.authorizedKeyFiles)
330 "${package}/bin/sshd"
331 "${package}/libexec/sshd-session"
335 description = "SSH Daemon";
336 wantedBy = [ "initrd.target" ];
339 "initrd-nixos-copy-secrets.service"
341 before = [ "shutdown.target" ];
342 conflicts = [ "shutdown.target" ];
344 # Keys from Nix store are world-readable, which sshd doesn't
345 # like. If this were a real nix store and not the initrd, we
346 # neither would nor could do this
347 preStart = flip concatMapStrings cfg.hostKeys (path: ''
348 /bin/chmod 0600 "${initrdKeyPath path}"
350 unitConfig.DefaultDependencies = false;
352 ExecStart = "${package}/bin/sshd -D -f /etc/ssh/sshd_config";
354 KillMode = "process";
355 Restart = "on-failure";