Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Building OpenSSH to work with U2F Security Keys on Debian 10 (Buster)

profile picture
Author
Erisa A
Published
2020-10-08
Time to read
13 minutes reading time

Introduction

A hardware security key is a great device for protecting your security online. They take the form of a small physical object, usually resembling a USB stick or dongle, and can be inserted in your computers USB port and tapped or pressed to authenticate to online services. The fact that the object is physical and works with advanced cryptography can dramatically increase your protection against remote attacks like phishing and MITM (Man in the middle) attacks.

FIDO2 and U2F (Universal 2 Factor) are two protocols that form the basis behind how your key authenticates with the web and other internet protocols.

In this tutorial we will be focusing on the support that OpenSSH has for these protocols, and getting it set up for you to authenticate to your server or cloud instance running Debian 10 (Stable).

To do this we'll need to compile and build the latest version (8.3 at time of writing) of OpenSSH from the next version of Debian (11/Bullseye) in a way that it will work nicely with Debian 10. It should behave fine since it is just a simple backport.

Keep in mind that building the software from source will mean that you won't receive updates for it via apt like the rest of your software. To keep up to date with security issues you should subscribe to Debian's security mailing list or an equivalent for your distribution.

Since this tutorial gets involved in the build process of Debian packages, it may seem a little complicated or involved at times. That said, these instructions are designed to be used by people of any skill level, and it's likely you'll learn something new along the way! :)

Prerequisites

To follow this tutorial you will need:

  • A server running Debian 10, either dedicated or virtual.
  • A local Linux installation or Windows installation (With Ubuntu or Debian installed through Windows Subsystem for Linux)
    • Arch Linux, Debian 10+ and Ubuntu 20.04+ are known to be compatible, anything else may work if it provides a version of OpenSSH 8.2 or later. macOS is untested.
  • A U2F compatible hardware security key, for example a YubiKey or Solo Key.

This tutorial was tested on a fresh Hetzner Cloud CPX11 instance, though it should run perfectly fine on any other server.

Step 1 - Preparing the system

If you've just provisioned the server you'll want to upgrade the installed packages and prepare the system for the package building.

Step 1.1 - Upgrading and preparing the package repositories

You can upgrade your packages with:

root@<your_host>:~# apt update
root@<your_host>:~# apt upgrade

You will also need to add the buster-backports repository if it's not already present.

Open /etc/apt/sources.list and check for the follow lines in the list. If either is not there, add them:

/etc/apt/sources.list:

deb http://deb.debian.org/debian buster-backports main
deb-src http://deb.debian.org/debian buster-backports main

You can then add the source repositories for Bullseye, the upcoming release of Debian which has the package we need. Please make sure you are only specifying deb-src here, and not deb.

/etc/apt/sources.list:

deb-src http://deb.debian.org/debian bullseye main

Optionally if you're attempting this on a Hetzner server (Dedicated or cloud!), you can speed up the process of downloading these packages by adding an entry to download from the Hetzner APT Mirror:

/etc/apt/sources.list.d/hetzner-mirror.list:

deb http://mirror.hetzner.de/debian/packages buster main contrib non-free
deb http://mirror.hetzner.de/debian/packages buster-updates main contrib non-free
deb http://mirror.hetzner.de/debian/packages buster-backports main contrib non-free
deb-src http://deb.debian.org/debian bullseye main

After all these changes you'll need to run apt update one more time to download the package information.

You can verify you did it right by making sure both buster-backports and bullseye are mentioned in the output!

Step 1.2 - Creating a sudo user

A quick note at this point is to create a user account if you haven't already, and allow them to access the root command through sudo.

For example:

root@<your_host>:~# adduser holu
Adding user `holu' ...
Adding new group `holu' (1001) ...
Adding new user `holu' (1001) with group `holu' ...
Creating home directory `/home/holu' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for holu
Enter the new value, or press ENTER for the default
        Full Name []: Hetzner Online Live User
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n]
root@<your_host>:~# usermod -aG sudo username

You can then login as that user with a fresh login shell like so:

root@<your_host>:~# sudo -i -u holu
holu@<your_host>:~$

And quickly check sudo is configured properly with the following command:

holu@<your_host>:~$ sudo whoami
[sudo] password for holu:
root
holu@<your_host>:~$

Step 2 - Installing development dependencies

First, we need to install the base Debian development dependencies:

holu@<your_host>:~$ sudo apt-get install build-essential fakeroot devscripts

There are 3 packages that we will need to build in order to successfully backport OpenSSH 8.3 to Buster. These are libfido2, dh-runit and of course openssh.

Of these, libfido2 depends on a version of cmake that is not present in Debian 10 Buster. Thankfully it is provided in the buster-backports repository, and will need to be installed manually:

holu@<your_host>:~$ sudo apt-get install -t buster-backports cmake

From here, we can now download the build dependencies for libfido2 and dh-runit. We will be fetching the openssh build dependencies later, since it relies on these two.

holu@<your_host>:~$ sudo apt-get build-dep libfido2 dh-runit

That should be all the dependencies for now, we're ready to download the source code!

First, create a directory to store the code, then enter it:

holu@<your_host>:~$ mkdir -p src/debian
holu@<your_host>:~$ cd src/debian
holu@<your_host>:~/src/debian$

And use the helpful function of apt-get to download the source code automatically!

holu@<your_host>:~/src/debian$ apt-get source libfido2 dh-runit openssh
holu@<your_host>:~/src/debian$ ls -1
dh-runit-2.8.15
dh-runit_2.8.15.dsc
dh-runit_2.8.15.tar.xz
libfido2-1.4.0
libfido2_1.4.0-2.debian.tar.xz
libfido2_1.4.0-2.dsc
libfido2_1.4.0.orig.tar.gz
libfido2_1.4.0.orig.tar.gz.asc
openssh-8.3p1
openssh_8.3p1-1.debian.tar.xz
openssh_8.3p1-1.dsc
openssh_8.3p1.orig.tar.gz
openssh_8.3p1.orig.tar.gz.asc
holu@<your_host>:~/src/debian$

That's all! Now we're ready for the build!

Step 3 - Building the packages

Of these packages, libfido2 should be installed first. Luckily it's quite a simple process.

First, enter the directory:

holu@<your_host>:~/src/debian$ cd libfido2-1.4.0/
holu@<your_host>:~/src/debian/libfido2-1.4.0$

(Make sure to replace the version number with the one you have downloaded. If you press the tab key after typing libfido2- it should autocomplete the correct folder.)

And then build!

holu@<your_host>:~/src/debian/libfido2-1.4.0$ debuild -b -uc -us

These parameters will build a binary only package (.deb file to install!), as well as instruct debuild to not sign the package, since it's only needed for personal use (and I'm assuming you trust yourself).

The build should complete relatively quickly on most systems. Once it's done, you'll want to install the needed packages:

holu@<your_host>:~/src/debian/libfido2-1.4.0$ cd ..
holu@<your_host>:~/src/debian$ sudo dpkg -i libfido2-1_1.4.0-2_amd64.deb fido2-tools_1.4.0-2_amd64.deb libfido2-dev_1.4.0-2_amd64.deb

Again, please make sure to replace the version numbers with the ones from your packages. Tab completion should help with that. Additionally, please make sure to not install packages with dbgsym in their name. These are for debugging purposes and are not needed for our purposes.

Next we can build the dh-runit package. The process should be the same as before:

holu@<your_host>:~/src/debian$ cd dh-runit-2.8.15/
holu@<your_host>:~/src/debian/dh-runit-2.8.15$ debuild -b -uc -us

And install:

holu@<your_host>:~/src/debian/dh-runit-2.8.15$ cd ..
holu@<your_host>:~/src/debian$ sudo dpkg -i dh-runit_2.8.15_all.deb runit-helper_2.8.15_all.deb

With that out of the way we have all the dependencies we need to get started with building OpenSSH!

Same as before, we'll need to install its build dependencies:

holu@<your_host>:~/src/debian$ sudo apt-get build-dep openssh

Build the source code:

holu@<your_host>:~/src/debian$ cd openssh-8.3p1/
holu@<your_host>:~/src/debian/openssh-8.3p1$ debuild -b -uc -us

(This build might take a little while if your server doesn't have enough processing resources. Don't worry though, it should proceed fine.)

And install the packages!

holu@<your_host>:~/src/debian/openssh-8.3p1$ cd ..
holu@<your_host>:~/src/debian$ sudo dpkg -i ssh_8.3p1-1_all.deb openssh-client_8.3p1-1_amd64.deb openssh-server_8.3p1-1_amd64.deb openssh-sftp-server_8.3p1-1_amd64.deb

At this point it may ask you if you want to replace your /etc/ssh/sshd_config file with a new one. This choice is up to you, I always personally keep the current configuration file since they're usually backwards and forwards compatible with each other.

Once you're done, check that it all worked!

holu@<your_host>:~/src/debian$ ssh -V
OpenSSH_8.3p1 Debian-1, OpenSSL 1.1.1d  10 Sep 2019

If the output includes a version of 8.3 or higher, congratulations! You have successfully built a newer version of OpenSSH for Debian 10 Buster!

Step 4 - Client setup

Next we will need to setup our client so that we can authenticate with our key!

Step 4.1 - Installing a supported OpenSSH

Windows

If you're running Windows 10, install the Windows Subsystem for Linux and then go through the following Linux package install instructions. There will be special instructions in Step 4.2 for setting up WSL to work with your key.

Versions of Windows older than Windows 10 do not support U2F security keys in a compatible way.

Ubuntu 20.04 or higher

Ubuntu 20.04 or higher have support for OpenSSH 8.3 out of the box, so simply sudo apt-get install openssh if you haven't already! :)

You should skip over the following Debian section if that worked, and rejoin at step 4.2!

Debian 10 Buster

If you're running Debian 10 Buster on your client side, you'll need to install a backported version of OpenSSH again!

Thankfully, you've already done that. You can copy the resulting .deb files and install them on your local machine.

First, put them in their own folder:

holu@<your_host>:~/src/debian$ mkdir debs
holu@<your_host>:~/src/debian$ mv *.deb debs/

(The * character acts as a wildcard, so in this case of *.deb it will move any file with a .deb file extension.)

Then, from your local machine, copy them locally.

If you logged into your server as root originally, you may need to use root for this operation. Alternatively, you can setup your regular user with your SSH keys. In the example I will be using root.

erisa@<your_local_machine>:~$ scp root@<203.0.113.1>:/home/holu/src/debian/debs .

(In this example, erisa is my local username on my computer. You'll also want to replace <203.0.113.1> with your server's IP address.)

In that command, -r will make it act recursively in order to download an entire directory, and . refers to the current directory on your local machine.

You can now install the packages from the deb folder you just downloaded locally:

erisa@<your_local_host>:~$ cd debs
erisa@<your_local_host>:~/debs$ sudo dpkg -i libfido2-1_1.4.0-2_amd64.deb fido2-tools_1.4.0-2_amd64.deb libfido2-dev_1.4.0-2_amd64.deb dh-runit_2.8.15_all.deb runit-helper_2.8.15_all.deb ssh_8.3p1-1_all.deb openssh-client_8.3p1-1_amd64.deb openssh-server_8.3p1-1_amd64.deb openssh-sftp-server_8.3p1-1_amd64.deb

This command is a bit of a big one. You may want to simplify it to sudo dpkg -i *.deb however be aware that will install a few undesired packages and dependencies on your local machine. They will not however cause any harm, just take up storage space for no real reason.

If you get an error like this one:

dpkg: dependency problems prevent configuration of dh-runit:
 dh-runit depends on debhelper (>= 9); however:
  Package debhelper is not installed.
 dh-runit depends on libtext-hogan-perl; however:
  Package libtext-hogan-perl is not installed.
 dh-runit depends on libfile-slurp-perl; however:
  Package libfile-slurp-perl is not installed.
 dh-runit depends on libfile-copy-recursive-perl; however:
  Package libfile-copy-recursive-perl is not installed.

dpkg: error processing package dh-runit (--install):
 dependency problems - leaving unconfigured

Don't worry! All you need to do is install some missing dependencies.

Simply run the following command:

erisa@<your_local_host>:~/debs$ sudo apt-get install -f

And aptwill find and install the required dependencies for you.

Now all that's left is to do a simple version check:

erisa@<your_local_host>:~/debs$ ssh -V
OpenSSH_8.3p1 Debian-1, OpenSSL 1.1.1d  10 Sep 2019

And you're done! Now for the fun part - getting the key to work with your new installations.

Step 4.2 - Key generation and interop

Once you're all set up with the appropriate OpenSSH versions of both server and client, you're ready to generate your SSH key pair!

If you're running Windows, skip over these instructions to the section on Windows below.

Linux

The format we'll be using here is ecdsa-sk.

Issue the following command:

erisa@<your_local_host>:~$ ssh-keygen -t ecdsa-sk

It should ask you to touch your security key, enter a PIN if one is set up and save the key. You can also add a local passphrase if you want to further protect your SSH connections!

Once you're done, you can find out the public key:

erisa@<your_local_host>:~$ cat .ssh/id_ecdsa_sk.pub
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBO5Uhm6IskKKlpu+waAlkZ79wE8hFBPpoPkEHb2V6sgCk+6UDbPCyU/siW6D4eHGMDzM4VVhvSkqrEpYa8samsQAAAAEc3NoOg== erisa@<your_local_host>

And add it to your server!

holu@<your_host>:~$ mkdir -p .ssh
holu@<your_host>:~$ echo '<your_public_key' >> .ssh/authorized_keys

At this point you should be able to successfully authenticate with your server!

If it's using an old or different SSH key, supply -i .ssh/id_ecdsa_sk on the SSH commandline to use your security key instead.

Windows

If you're running Windows 10 with WSL, you'll need to setup your WSL instance to interact with your security key over the Windows U2F API.

To achieve this we'll be using a piece of open source software called windows-fido-bridge.

You can check out the installation and usage instructions on github.

Essentially it boils down to (at the time of writing)

sudo apt install build-essential cmake g++-mingw-w64-x86-64 git libfmt-dev libgtest-dev

git clone https://github.com/mgbowen/windows-fido-bridge.git
cd windows-fido-bridge
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j $(nproc)
make test
sudo make install

On your local WSL instance.

You can then generate a keypair with:

erisa@<your_local_host>:~$ SSH_SK_PROVIDER=/usr/local/lib/libwindowsfidobridge.so ssh-keygen -t ecdsa-sk

Add it to your server with:

erisa@<your_local_host>:~$ cat .ssh/id_ecdsa_sk.pub
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBO5Uhm6IskKKlpu+waAlkZ79wE8hFBPpoPkEHb2V6sgCk+6UDbPCyU/siW6D4eHGMDzM4VVhvSkqrEpYa8samsQAAAAEc3NoOg== erisa@<your_local_host>
holu@<your_host>:~$ mkdir -p .ssh
holu@<your_host>:~$ echo '<your_public_key' >> .ssh/authorized_keys

And connect to your server with:

erisa@<your_local_host>:~$ ssh -oSecurityKeyProvider=/usr/local/lib/libwindowsfidobridge.so remote-server

If you are going to be using the Security Key for all of your authentications, you can instead add the following to .ssh/config after creating a key pair:

Host *
        SecurityKeyProvider /usr/local/lib/libwindowsfidobridge.so
        IdentityFile ~/.ssh/id_ecdsa_sk

Conclusion

Phew, that was a long ride! Hopefully you learnt something, and are now able to securely authenticate with your server using your hardware security key!

If in the future you need to update to a newer version of OpenSSH (Security patches, etc.) you can simply redownload the source code and run build/install commands by following the above instructions again!

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue
Try Hetzner Cloud

Get 20€ free credit!

Valid until: 31 December 2024 Valid for: 3 months and only for new customers
Get started
Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more