Skip to content

OpenSSH server

Markus Hansmair edited this page Mar 2, 2026 · 2 revisions

Note

OpenSSH is a feature-packed and thus quite heavy SSH server. In the likely case that you just need shell access to the initramfs environment of your system TinySSH or Dropbear SSH server are probably a better choice. Those options are 21 times and 7 times (resp.) smaller than OpenSSH, keeping the initramfs image at a reasonable size and boot times low.

sd-openssh enables SSH access during the initramfs phase of the Linux boot process by means of OpenSSH. The install hook sd-openssh takes care of providing the initramfs image with all binaries and configuration files required by OpenSSH and enabling the listening TCP port 22 (or some other). Every incoming connection request triggers a separate OpenSSH process that handles one SSH session.

There are two vital prerequisites that must be met:

  • sd-network from package mkinitcpio-systemd-extras must also be activated and configured in /etc/mkinitcpio.conf.

  • Package openssh must have been installed so that OpenSSH's binaries can be copied into the initramfs.

Like every SSH server also OpenSSH in the initramfs must be able to present a server key (aka host key). OpenSSH supports several key algorithms, the corresponding key files are ssh_host_ed25519_key, ssh_host_rsa_key and ssh_host_ecdsa_key. These files are copied from $SD_OPENSSH_KEYDIR (default /etc/ssh).

It is highly recommended to provide the server keys in encrypted form (ending in .enc), created with systemd-creds. (See section Server keys and security considerations below for details.)

If neither encrypted nor plaintext server keys are found the install hook sd-openssh aborts with an error.

The hook sd-openssh also copies all public keys of root (from $SD_OPENSSH_AUTHORIZED_KEYS, default /root/.ssh/authorized_keys) to the initramfs image. This allows to log in with ssh as root.

The hook sd-openssh enlarges the initramfs image by approximately 3210KiB, that's around 1321KiB for the zstd compressed archive.

Caution

Probably the most common use case for this hook is to remotely unlock an encrypted partition (or device) before it can be mounted by systemd. Mind that by default systemd only waits 90 seconds for the unlocked partition (or device). After that systemd will give up and enter some emergency mode. You are then effectively locked out! To avoid this painful situation define the kernel parameter rootflags=x-systemd.device-timeout=0 in the configuration of your boot loader. With this setting systemd will wait forever. See systemd.mount for details.

Server keys and security considerations

sd-openssh produces a warning when plaintext server keys are copied from the default location /etc/ssh because SD_OPENSSH_KEYDIR has not been set:

Reusing the host keys from the regular operating environment poses a security risk. See https://github.com/wolegis/mkinitcpio-systemd-extras/wiki/OpenSSH-server for details.

This warning is issued under the assumption that you also use OpenSSH for the regular operating environment. It is trivially easy to extract the server key from the initramfs image located on the unencrypted boot partition. This may allow a man in the middle to impersonate your server and thus intercept the traffic between you and your SSH server. The attack will go unnoticed since from the client's perspective the server key was as expected.

The best you can do is to go with encrypted server keys. The second best option is to use a completely separate set of server keys (distinct from the 'regular' server keys).

Using encrypted server keys

The basic idea of this option is to store the server keys in encrypted form in the initramfs image. systemd will decrypt these keys only when a new SSH session is initiated and provide them to the sshd process on an ephemeral filesystem. Thus the plaintext server keys will never touch persistent storage.

Important

This option requires that a TPM2 compliant module is present in your system to encrypt and later decrypt the secret server key. Nowadays, this is almost always true for Intel or AMD based desktop systems and also for many dedicated servers. However, this is not true for many virtual servers and ARM based systems.

sd-openssh expects the encrypted server keys as ssh_host_{keytype}_key.enc in /etc/ssh. When at least one of these files is present they are copied into the initramfs image instead of the plaintext server key files. The systemd server unit for sshd is set up in a way that the encrypted server keys are decrypted on-the-fly during startup of each sshd service. (See Credentials in systemd's documentation for details.)

Preparations

  1. Check if your system has a TPM2 chip:

    systemd-analyze has-tpm2
    

    The tool needs to report yes. Otherwise encrypting and decrypting the server keys will not work.

  2. Encrypt the server keys:

    systemd-creds encrypt --with-key=tpm2 --name=ssh_host_{keytype}_key \
        /etc/ssh/ssh_host_{keytype}_key \
        /etc/ssh/ssh_host_{keytype}_key.enc
    

    with {keytype} being ed25519, rsa and ecdsa (or a subset thereof)[1].

    This encrypts the OpenSSH server keys with a key hidden in the TPM. The keys are thus bound to this very system. In other words: Always encrypt and decrypt on the same system!

    As desired, you will end up with encrypted keys in your initramfs image. However, anyone who can boot ANY operating system on the machine (e.g. from a USB stick) will be able to decrypt the server keys. It is therefore recommended to additionally bind against at least PCR0 (firmware) and PCR7 (secure boot state). Just add option --tpm2-pcrs=0+7 to the list of options of systemd-creds:

    systemd-creds encrypt --with-key=tpm2 --tpm2-pcrs=0+7 --name=ssh_host_{keytype}_key \
        /etc/ssh/ssh_host_{keytype}_key \
        /etc/ssh/ssh_host_{keytype}_key.enc
    

    Mind that after a firmware update or a secure boot policy change the key must be encrypted again.

  3. Recreate your initramfs image with

    mkinitcpio -P
    

    The hook sd-openssh will recognize the encrypted server keys by their names ssh_host_{keytype}_key.enc and setup everything so that these encrypted keys will be used.

Using a separate set of server keys

In case you cannot go with encrypted server keys, the second best option is to use a different set of server keys for your initramfs image. This way at least the server keys of your regular operating environment cannot leak.

Create the separate keys with:

ssh-keygen -t {keytype} -f /etc/ssh/initramfs_host_keys/ssh_host_{keytype}_key

with {keytype} being ed25519, rsa and ecdsa (or a subset thereof)[1].

Additionally, you have to set

SD_OPENSSH_KEYDIR=/etc/ssh/initramfs_host_keys

somewhere in mkinitcpio.conf. Of course, you are free to choose any directory you like.

As nearly always in IT the added security comes with a loss in usability. Depending on whether you connect to your server during initramfs phase (e.g. to unlock your encrypted root partition) or during regular operation the server will present different server keys - and your SSH client will complain.

One possible solution to deal with that is to configure separate Host sections in $HOME/.ssh/config on the client side, e.g.:

Host myserver
    Hostname myserver.mydoma.in
    IdentityFile ....
    IdentitiesOnly yes
    CheckHostIP yes
    User root

Host myserver-init
    Hostname myserver.mydoma.in
    IdentityFile ....
    IdentitiesOnly yes
    CheckHostIP no
    User root
    HostKeyAlias myserver-init

This way you have to use ssh myserver-init to connect to OpenSSH during initramfs phase and ssh myserver to connect to the same machine while running in regular operating environment.

Silencing the security warning

There may be situations where you have good reasons not to go with any of the two options mentioned above. To at least silence the security warning you have to set configuration variable SD_OPENSSH_KEYDIR in mkminitcpio.conf.

SD_OPENSSH_KEYDIR=/etc/ssh

Configuration

Add sd-openssh to the array HOOKS in /etc/mkinitcpio.conf. The entry must be positioned somewhere after (right of) systemd. Apart from that the concrete position is irrelevant.

There are five configuration variables affecting the behavior of the install hook or the OpenSSH server. These can be specified somewhere in /etc/mkinitcpio.conf (or in some file in /etc/mkinitcpio.conf.d):

  • SD_OPENSSH_COMMAND: With the hook sd-openssh you can log into the Linux system during the initramfs phase as root. By default, you get a shell (busybox dash) and can perform interactive tasks. This variable defines a shell command that is used as argument of sh -c (i.e. it may contain blanks, double quotes and all kinds of special characters that are interpreted by the shell , but see note below). The resulting command is executed instead of sh after login. The SSH session terminates as soon as this command terminates.

    A good usage example is to set

    SD_OPENSSH_COMMAND="systemd-tty-ask-password-agent --query"

    This allows to unlock LUKS encrypted devices - but nothing more.

Note

Due to the way how the command specified by SD_OPENSSH_COMMAND is passed to the sshd executable usage of single quotes is highly problematic. It's best to avoid single quotes all together - unless you know exactly what you are doing. Refer to systemd's documentation regarding command line and quoting for all the details. After having run mkinitcpio check the resulting command with

zstdcat /boot/initramfs-linux.img \
    | cpio --extract --to-stdout usr/lib/systemd/system/sshd@.service
  • SD_OPENSSH_AUTHORIZED_KEYS: Set this configuration variable to specify some other source of the keys file authorized_keys used for root in the initramfs (instead of `/root/.ssh/authorized_keys):

    See the sshd man page for possible options regarding authorized_keys.

    For example you may want to limit root to unlocking LUKS encrypted devices after having logged into the initramfs environment (instead of sh or any command provided by the SSH client). To achieve this make a copy of root's authorized_keys and put command="..." in front of the entry used for authentication, i.e.:

    command="systemd-tty-ask-password-agent --query" ssh-ed25519 AAAAC3Nza...
    

    Then set SD_OPENSSH_AUTHORIZED_KEYS naming the modified copy.

  • SD_OPENSSH_CONFIG: By default, sd-openssh copies a minimal configuration file to the initramfs image (ignoring any existing /etc/ssh/sshd_config). Set this configuration variable to override this behavior and reference some existing configuration file (e.g. etc/ssh/sshd_config). Files in ${SD_OPENSSH_CONFIG}.d are automatically taken into account.

Important

All lines starting with UsePAM are automatically converted to UsePAM no since there is no PAM in initramfs. This even applies for /etc/ssh/sshd_config and /etc/ssh/sshd_config.d/*.

  • SD_OPENSSH_KEYDIR: Specifies a directory with server keys ssh_host_{keytype}_key or the encrypted form ssh_host_{keytype}_key.enc to be copied into the initramfs image. Default is /etc/ssh.

  • SD_OPENSSH_PORT: With this configuration variable it is possible to change the port opened for OpenSSH. Default is 22.

    This may be used to circumvent simple firewall rules, e.g. by using port 443 instead of 22.


[1] Creating just one type of server key is usually sufficient as long as you know that all clients can deal with this type.

Clone this wiki locally