My Yubikey Debian Setup¶
My own Yubikey Debian setup.
My Goals¶
Warning
Disclaimer
As with everything related to security: do not follow these instructions blindly. Take time to understand exactly why and if you need to follow some step. I take no responsibility for you locking yourself out of your data, or leaking everything to a third-party.
You have been warned.
I am documenting my goals to make you understand why I did something and why it is good enough in my scenario. Check if you really need the same provisions.
You might need to be more strict or more lax, depending on your use case. I wanted to achieve reasonable security and ergonomics, without sacrificing usability.
In particular, I try to use different passwords almost everywhere. This means having one hardware token which enables me to perform passwordless logins and acts as a 2FA token is a big improvement. If it gets stolen, I expect to realize that very fast, and be able to revoke everything linked to it, without losing full access to the associated services.
This is why:
I want to be able to unlock my disk volumes with the hardware key without a password or PIN, but just with the Yubikey presence.
I want to be able to login into my account and become root without checking for user presence or inputting a password if the Yubikey is inserted.
I want to use the Yubikey to securely prove my identity on different computers / laptops under my full control.
I want to be able to sign emails and
git
commits with my GPG key without going around with the master key.I want to be able to get 2FA codes for a range of different Internet services to improve security over the usual username-and-password authentication flow. I also have my smartphone setup with the same codes.
I want to be able to still get access to all services and quickly react if the Yubikey gets lost or stolen (or forgotten at home!).
Other applications¶
Note that the Yubikey is also not my only 2FA provider. I tend to duplicate all TOTP account data using Authenticator Pro on my phone. I don’t always have my Yubikey with me, and I might need to access a 2FA-protected account! Doing it like this, has the nice property that I can export and backup all secrets to my Nextcloud instance. This might or might not be a good idea in your scenario. I still recommend checking all recovery keys for accounts with 2FA enabled inside a password manager.
As for my password manager: I use pass along with the corresponding Android application. I export a copy of the password store via git
to my server, and sync with that.
You might want to simply rely on Firefox to store your password, and use the Firefox Sync account to keep them actual on all your devices. Remember to use 2FA with your Firefox account, then!
Preparing the Key¶
apt install yubikey-manager flatpak
flatpak remote-add --if-not-exists \
flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install com.yubico.yubioath
Now you should proceed to enable a PIN for critical apps on the key if it is not already on.
Importing the GPG subkeys¶
GPG interface is… byzantine. While you can generate multiple subkeys on a per-device basis (and this is the most secure setup), it has the drawback that data encrypted with one encryption key cannot be decrypted by the master (well, this is not really a GPG problem, it’s a design choice).
Since I want to be able to sign and decrypt data using always the same keys, I decided to manually export the subkeys to the Yubikey while retaining them on a desktop PC I own. This is normally not permitted (and for good reason!) by GPG. In case the Yubikey is lost or stolen, it would be my job to immediately revoke and rotate keys on the desktop PC!
But from an egonomic perspective, it’s a really good thing. The desktop PC is unlikely to get stolen. The Yubikey acts as a backup.
Start by setting the Yubikey PIN with gpg --card-edit
(factory defaults are 123456
and 12345678
for admin):
gpg/card> admin
gpg/card> passwd
1 - change PIN
3 - change Admin PIN
Q - quit
gpg/card> name
Name Lastname
gpg/card> lang
e.g. en
gpg/card> login
email@email.org
Now to generate the key with gpg --expert --full-gen-key
:
(9) ECC and ECC
(1) Curve 25519
0 = key does not expire
...
pub ed25519 2023-03-15 [SC]
DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C
uid Temporary <ciccio@pasticcio.com>
sub cv25519 2023-03-15 [E]
Then gpg --expert --edit-key DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C
:
uid
# (optional) if you have other email addresses to add
addkey
(10) ECC (sign only)
(1) Curve 25519
0 = key does not expire
addkey
(11) ECC (set your own capabilities)
(A) Toggle the authenticate capability
(S) Toggle the sign capability
(Q) Finished
(1) Curve 25519
0 = key does not expire
sec ed25519/3AA9819FF4CB5B7C
created: 2023-03-15 expires: never usage: SC
trust: ultimate validity: ultimate
ssb cv25519/5811300B293014C8
created: 2023-03-15 expires: never usage: E
ssb ed25519/92167215DD1721AD
created: 2023-03-15 expires: never usage: S
ssb ed25519/1D988BD1FB3879E0
created: 2023-03-15 expires: never usage: A
[ultimate] (1). Temporary <ciccio@pasticcio.com>
save
Now we have all we need: an encryption, a signing, and an auth subkey. I strongly recommend that you also generate a revocation certificate for the whole master key at this point, print it in a font that makes it easy to use OCR later on (look for good differentiation of O
and 0
, e.g. via Fira Code), and store it offline. This is left as an exercise to the reader.
At this point, we want to export the full key, including subkeys, so that we can re-import it later.
gpg --export-secret-keys DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C > temp.priv
gpg --export DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C > temp.pub
gpg --expert --edit-key DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C
Now select the subkeys in turn via key
and then issue a keytocard
to move them to the Yubikey.
gpg> key 1
gpg> keytocard
gpg> key 2
gpg> keytocard
gpg> key 3
gpg> keytocard
gpg> save
The subkeys will be marked as “moved”. This is not what we want: we want a local copy. Hence it is time to throw away our key and restore it from backup.
gpg --delete-secret-keys DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C
gpg --import temp.priv
This should be enough. After checking everything is back to normal, you can delete the temp.priv
file.
SSH¶
See directly Setting up OpenSSH for FIDO2 Authentication » Discoverable Key Instructions on the Yubico website.
System Integration¶
Automated LUKS disk unlocking¶
The default initramfs-tools
which generates the initrd in Debian seems lacking of many security-related features, including proper FIDO2 cryptsetup support. Hence, let’s switch to dracut
instead:
apt install dracut
At the time of this writing, some files were missing from the dracut-generated images. I forced these to be included by adding a corresponding configuration file and then running dpkg-reconfigure dracut
:
install_optional_items+=" /lib/x86_64-linux-gnu/libfido2.so* "
install_items+=" /usr/bin/fido2-token "
install_items+=" /usr/lib/udev/rules.d/60-fido-id.rules /usr/lib/udev/fido_id "
I expect the above configuration file to be or become obsolete quite soon. For me it was still necessary to ensure all needed files were present in the initrd image. But it should really be fixed upstream in Debian.
Now it’s finally time to enroll our keys. After a quick check at blkid | grep crypto_LUKS
, we can enroll our Yubikey against volumes we want to unlock through it:
systemd-cryptenroll \
--fido2-device=auto \
--fido2-with-user-presence=false \
--fido2-with-client-pin=false \
/dev/sda1
At the moment, --fido2-with-user-presence=false
does not seem to work. I don’t know if this is by design or if it is a bug. You will need to touch your key during boot when it starts blinking.
We now need to tell the system using fido2 at boot for unlocking is an option. For instance, for me:
# <target name> <source device> <key file> <options>
rootfs UUID="13bd3015-55c4-40d3-a805-4d5f2b90ac1f" - fido2-device=auto
If you do not have your key, after 30 seconds you will be prompted for the password. You can lower this timeout in /etc/crypttab
, check its man page.
Configuring PAM¶
Let’s start by installing the needed module:
apt install libpam-yubico yubikey-personalization
Unfortunately the default installed PAM module goes through the Internet and to the Yubico server. This requires an account and an Internet connection. Since I often travel with my laptop and I don’t want to depend on an external service, I set up the key to perform a challenge-response authentication locally.
Let’s create a custom configuration file:
Name: Yubico authentication with YubiKey (challenge-response)
Default: no
Priority: 704
Auth-Type: Primary
Auth:
sufficient pam_yubico.so mode=challenge-response chalresp_path=/var/yubico
Auth-Initial:
sufficient pam_yubico.so mode=challenge-response chalresp_path=/var/yubico
Now run pam-auth-update
and enable the corresponding module we just created. Disable the default “Yubico authentication with Yubikey” module if it is enabled.
The rest of the guide about generating the challenge-response slot on the Yubikey can be read here. Please follow instructions there.
Teaching SSH to Prefer the Yubikey¶
Host *
IdentityFile ~/.ssh/id_ed25519_sk
ControlMaster auto
ControlPath ~/.ssh/S.%r@%h:%p
ControlPersist 5m
Making GPG Slightly More Happy¶
pcscd
, the smartcard daemon, seems to take an exclusive lock on the Yubikey if used for unlocking the drive or SSH access. We can instruct GnuPG to also use PC/SC in a shared move, which should ameliorate the problem and avoid the need for restarting it (systemctl restart pcscd
) every time e.g. we want to sign an email with GnuPG.
disable-ccid
pcsc-shared
pcsc-driver /usr/lib/x86_64-linux-gnu/libpcsclite.so.1
Configuring git
with the Subkey¶
...
[user]
signingkey = 92167215DD1721AD!
[commit]
gpgsign = true
Note the exclamation mark (!
) after the subkey id to force its selection.