.. SPDX-FileCopyrightText: 2023 Matteo Settenvini SPDX-License-Identifier: CC-BY-SA-4.0 .. post:: 15 Mar, 2023 :tags: yubikey, security :category: tech :author: Matteo Settenvini :location: Berlin, DE ####################### My Yubikey Debian Setup ####################### .. contents:: Table of Contents :local: ======== 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 ================= .. code-block:: bash 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): .. code-block:: none 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``: .. code-block:: none (9) ECC and ECC (1) Curve 25519 0 = key does not expire ... pub ed25519 2023-03-15 [SC] DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C uid Temporary sub cv25519 2023-03-15 [E] Then ``gpg --expert --edit-key DF3D5AD589481FC3448BB3D33AA9819FF4CB5B7C``: .. code-block:: none 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 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. .. code-block:: bash 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. .. code-block:: none 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. .. code-block:: bash 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: .. code-block:: bash 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``: .. code-block:: none :caption: /etc/dracut.conf.d/99-yubikey.conf 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: .. code-block:: bash 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: .. code-block:: none # 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: .. code-block:: bash 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: .. code-block:: none :caption: /usr/share/pam-configs/yubico-sufficient 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 ================================== .. code-block:: none :caption: ~/.ssh/config 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. .. code-block:: none :caption: ~/.gnupg/scdaemon.conf disable-ccid pcsc-shared pcsc-driver /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 Configuring ``git`` with the Subkey =================================== .. code-block:: ini :caption: ~/.gitconfig ... [user] signingkey = 8576CC1AD97D42DF! [commit] gpgsign = true Note the exclamation mark (``!``) after the subkey id to force its selection.