08.SSH FIDO2 - Jubijub/arch-config GitHub Wiki

Introduction

Table of Contents

The goal is to explain the generation and usage of SSH authentication / SSH signing using FIDO2 Yubikeys.

Note
historically I would follow the SSH+PGP keys stored on the Yubikey approach (cf DrDuh Yubikey Guide). But FIDO2 makes it much simpler, and since I don’t use GPG for anything else, it was too much hassle.

Reference

Install the required packages

paru -S yubikey-manager libfido2
sudo systemctl enable pcscd.service
sudo systemctl start pcscd.service

(for new keys) Configure FIDO Pin

ykman info (1)
ykman fido access change-pin
  1. Verify that FIDO2 is enabled.

Obtain a key

Important
Each key has its own private/public key pair
  • the tutorial below will advise to save each key as id_ed25519_sk-<last 3 digits of the serial number>

  • while git supports multiple allowed_signers, we can only configure one user.signingkey at a time.

  • we will thus use a udev script to listen to Yubikey insertions, and copy the relevant id_ed25519_sk-XXX into id_ed25519_sk, and git will be configured to always look for id_ed25519_sk.

  • Target situation for n yubikeys :

    • Your ~/.ssh/ folder contains n pairs of private/public keys, with the last 3 digits of the serial number as a suffix.

    • Your ~/.ssh/allowed_signers contains n lines, with each line containing the <email><public key>.

    • Git is configured as if there was only one key named ~/.ssh/id_ed25519_sk

    • A udev script in the background monitors which key is inserted, and copies the right keys into ~/.ssh/id_ed25519_sk.

    • All n keys have been added to Github as signing and SSH keys, and to the other services.

Generate a new key on a Yubikey and add it to this computer

ykman info (1)
ssh-keygen -t ed25519-sk -O resident -O verify-required -C “<key form factor + last 3 digit serial number>” (2)
  1. note the last 3 digits of the serial number.

  2. when prompted, directly specify the proper name : ~/.ssh/id_ed25519_sk-XXX

You can add the -O application=ssh:YourText if you need more than one SSH key per YubiKey.

Warning
don’t set any passphrase, press enter both times.

Add the existing key on a Yubikey to this computer

Copy the key to the new computer

ykman info (1)
mkdir ~/.ssh
cd ~/.ssh (2)
ssh-keygen -K
mv id_ed25519_sk_rk id_ed25519_sk-XXX (3)
mv id_ed25519_sk_rk.pub id_ed25519_sk-XXX.pub (3)
  1. note the last 3 digits of the serial number.

  2. important as the -K command downloads the key in the current directory.

  3. where XXX are the last 3 digits of the serial number. Do note that the copied key will be called *_sk_rk, you need to remove that _rk

Edit the public key comment

nvim ~/.ssh/id_ed25519-XXX_sk

After the = sign, use the <key form factor + last 3 digit serial number>, eg: USB-C 123. This will make it clearer which SSH key is associated with which Yubikey.

Configure the computer to use the key

Add the key to ~/.ssh/allowed_signers

nvim ~/.ssh/allowed_signers
cat ~/.ssh/id_ed25519_sk-XXX.pub (1)
  1. copy the public key

The format is

<email> <the public key>

At the end, this file should contain one entry per Yubikey in your possession.

Configure services to use the new keys

udev script to ensure multiple keys can be used

Important
see the explanation above, as git can only use one user.signingkey at a time, we will use a script to dynamically copy the right key into the ~/.ssh/id_ed25519_sk

Create a udev rule

Create the file /etc/udev/rules.d/99-yubikey-renaming.rules

SUBSYSTEM=="usb", ATTR{idVendor}=="1050", ATTR{idProduct}=="0407", ACTION=="add", RUN+="/home/jubi/scripts/yubikey.sh"

Create the script

#!/bin/bash
# detect-yubikey-and-set-signing-key.sh
#
#!/bin/bash
exec >> /tmp/yubikey.log 2>&1
export PATH="/usr/local/bin:/usr/bin:/bin"
export HOME="/home/jubi"

# Get YubiKey serial number
SERIAL_FULL=$(ykman info 2>/dev/null | grep "Serial number" | awk '{print $3}')

if [ -z "$SERIAL_FULL" ]; then
    echo "$(date): No YubiKey detected or ykman failed"
    exit 1
fi

# Get last 3 digits
SERIAL_LAST3=${SERIAL_FULL: -3}
echo "$(date): YubiKey serial: $SERIAL_FULL, last 3 digits: $SERIAL_LAST3"

SOURCE_KEY="$HOME/.ssh/id_ed25519_sk-$SERIAL_LAST3"
SOURCE_PUB="$HOME/.ssh/id_ed25519_sk-$SERIAL_LAST3.pub"
TARGET_KEY="$HOME/.ssh/id_ed25519_sk"
TARGET_PUB="$HOME/.ssh/id_ed25519_sk.pub"

if [ ! -f "$SOURCE_KEY" ]; then
    echo "$(date): Source key not found: $SOURCE_KEY"
    exit 1
fi

# Copy the keys
cp -f "$SOURCE_KEY" "$TARGET_KEY"
cp -f "$SOURCE_PUB" "$TARGET_PUB"

# Set proper permissions
chown jubi:users "$TARGET_KEY"
chown jubi:users "$TARGET_PUB"
chmod 600 "$TARGET_KEY"
chmod 644 "$TARGET_PUB"

echo "$TARGET_KEY and $TARGET_PUB successfully updated"

You can monitor the logs with tail -f /tmp/yubikey.log.

Git

git config --global gpg.format ssh (1)
git config --global user.signingkey ~/.ssh/id_ed25519_sk (2)
git config --global commit.gpgsign true
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
  1. enables SSH key signing instead of GPG.

  2. we will use id_ed25519_sk no matter what. The script will ensure that it contains the right key.

GitHub

  • Login to Github

  • Click on top right profile icon > Settings

  • In Password and authentication, verify that the key has been added as a security key, and add it if not

  • In SSH and GPG keys add the key twice:

    • Add it as a SSH Authentication key

    • Add it as a SSH Signing key

⚠️ **GitHub.com Fallback** ⚠️