Last active 1729621908

Notes from my journey to getting Yubikey to authenticate everything on my system.

Revision eb90999a76cf0e11406dd22dd3b3916213ea5124

yubikey-all-the-things.md Raw

U2F for login and sudo access

I followed this guide: https://support.yubico.com/hc/en-us/articles/360016649099-Ubuntu-Linux-Login-Guide-U2F

For step 3, I only did the user-specific pamu2fcfg setup; I did not put it at the system level. This makes the $HOME partition portable, and allows different users on the system to require U2F and/or use different keys.

Challenge-Response auth with YubiKey

When I originally purchased my key, and for four years thereafter, I used the challenge-response feature for restricting user authentication and sudo access. I found with the update to Ubuntu 20.10, however, that challenge-respose no longer worked; even with a known, valid key, I could no longer login. I'm retaining this information here for posterity, but will also note that there's a more up-to-date article covering this now maintained by Yubico: https://support.yubico.com/hc/en-us/articles/360016649079-Ubuntu-Linux-Login-Guide-Challenge-Response

Started with https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html

  • Install yubikey-personalization
  • Install libpam-yubico
    • The instructions indicated that I'd get a question about the PAM configuration line. I did not. As such, I had to run dpkg-reconfigure libpam-yubico to do so. When I did, I used the value mode=challenge-response. Additionally, this then gives the dialog associated with pam-auth-update, and initially has Yubico selected. The instructions are unclear at this point, and make it seem like you should deselect everything; DO NOT DO THIS! Just deselect the Yubico entry.
  • Set the appropriate yubikey mode: sudo ykpersonalize -m86.
    • Mode 6 allows the yubikey 4 to act as each of OTP, U2F, and CCID.
      • OTP allows it to be used as a one-time-password device with the yubicloud as a replacement for things like google authenticator.
      • U2F allows it to be used as a universal 2 factor auth device, which we will be using with the challenge-response settings for PAM.
      • CCID allows it to be used as a smartcard for usage with GPG.
    • Mode 80 sets the MODE_FLAG_EJECT mode, which allows it to "eject" itself and then "re-insert" itself when touched for an OTP. Combine it with the above modes.
  • Set slot 2 as a challenge-response slot: ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible (This is also required for usage as a GPG smartcard)
  • Create the challenge/response for your user:
    • mkdir $HOME/.yubico
    • ykpamcfg -2 -v
    • Note: if you have an encrypted home directory, you MUST do this differently! In that case:
      • sudo mkdir /var/yubico
      • sudo chmod +t /var/yubico
      • sudo chmod 777 /var/yubico
      • ykpamcfg -2 -v -p /var/yubico
  • Update the PAM configuration.
    • Edit /etc/pam.d/common-auth
    • Check for the line: auth required pam_yubico.so mode=challenge-response; if not present, create it.
    • IF YOU HAVE AN ENCRYPTED HOME DIRECTORY, add the verbiage chalresp_path=/var/yubico to the end of the line.
    • Open a root terminal in your shell: sudo -s (This is a fail-safe, to ensure that if any configuration is wrong, you can disable the yubico PAM requirements.)
    • In a separate terminal, run sudo pam-auth-update, and enable the Yubico module.
  • Test it. Ctrl-Alt-F1 to get to the TTY.
    • Remove your yubikey
    • Attempt to login. It should fail.
    • Insert your yubikey.
    • Attempt to login. It should succeed.
    • exit.
    • Alt-F7 to get back to your GUI. In fact, at this point, your GUI should likely be at the lock screen, and you should find that you'll be unable to unlock it without the yubikey inserted!

Adding Yubikey as a sudo authentication mechanism

I followed this guide: https://defensiveinfosec.wordpress.com/2013/07/09/adding-otp-wyubikey-to-ubuntu-13-04-to-sudo-or-login/

PGP on Yubikey

I tried three different approaches:

None worked out-of-the-box.

In the first case, I never successfully got the key updated. On further reading, I discovered (a) the instructions are out-of-date, and (b) using subkeys effectively solves the problem anyways. The latter two use that latter approach.

With the latter two, I ran into issues with each.

For the blog post from Eric Severance (www.esev.com), I ran into an issue only at the very end, when I'd do my gpg --card-status: there was no "general key info" listed, which meant I did not have a valid keychain.

For the blog post from Norbert Preining (www.preining.info), I'd run into issues as soon as I issued keytocard, due to missing a secret key.

What I discovered was this: the instructions from Norber Preining did work, if I generated the subkeys using gpg2, and not gpg1. Once I did that, everything fell into place.

This means, of course, that I cannot use these for embedded systems, or when using GPG v1. I'm okay with that.

Updating GIT

Once you've got everything setup, it's time to tell Git to use your new signing key from your yubikey.

If you run gpg -K, look for your signing key (it'll have [S] at the end of the key's listing, just prior to the expiration date). Grab the key ID, and now run:

$ git config --global user.signingkey <key id>

Also, since I must use gpg2, I had to update one other configuration, to tell git to use gpg2 when signing commits:

$ git config --global gpg.program gpg2

Using touch for authentication

Instead of entering a GPG passphrase and/or the PIN associated with the yubikey, I wanted to be able to just touch the key instead. Why?

  • Simpler. I have enough passwords running through my head, and I don't want to have to remember them all.
  • More secure. The yubikey acts as an input device by default, which means that, technically, a keylogger could potentially sniff it. On top of that, a keylogger could sniff the PIN associated with it. While I may still need to enter the PIN for the first signing or encryption operation I performed, I can reduce the number of opportunities.

I followed the directions on the yubico site for enabling touch operations. It worked flawlessly immediately; I ran it using:

$ yubitouch.sh sig on
$ yubitouch.sh aut on
$ yubitouch.sh dec on

I've put the script in $HOME/bin/yubitouch.sh, and kept it around.

Updating the private (master) key and/or subkeys

You may need to update the private (master) key and/or its subkeys from time to time:

  • to reset expiration
  • to update the user identity

This turned out to be somewhate complicated, and, again, no one source I read worked for me, though this one by Abel Luck and forked from Kenny MacDermid came quite close.

Essentially, you do the following:

  • Pull the data from your USB into a new folder locally, setting the folder permissions to 0700. Export a variable, GPGHOME, pointing to this directory.
  • killall gpg-agent (just to be on the safe side)
  • Edit the key, using gpg --homedir $GPGHOME --edit-key <key>
  • Make whatever changes you need. For example, to reset expiry:
    • Type key <index> to select a key.
    • Type expire and follow the prompts.
    • Type key <index> to deselect the key.
    • Repeat the previous steps for each expired key.
    • Type save to save the changes.
  • Export the secret key: gpg --homedir $GPGHOME -a --export-secret-keys > $GPGHOME/master-secret-key.gpg
  • Export the secret keys for the subkeys: gpg --homedir $GPGHOME -a --export-secret-subkeys > $GPGHOME/sub-secret-keys.gpg
  • Export the public key: gpg --homedir $GPGHOME -a --export <key> > $GPGHOME/pub-key.gpg

If you follow along with the above link, you'll notice I'm not exporting the key stubs. I don't do this, because it did not work for me. When I would import them, they'd be imported as keys missing their secrets, but not as keys on a device.

At this point, you have one of two options:

  • Import the public key into a new, empty $HOME/.gnupg/ directory
  • Import the public key into the existing $HOME/.gnupg/ directory

I chose the first. To do this, I also had to first save my keyring, which can be in $HOME/.gnupg/pubring.gpg, but, starting in GPG 2.1, is in $HOME/.gnupg/pubring.kbx; I'll import that later.

  • killall gpg-agent (just to be on the safe side)
  • mv $HOME/.gnupg $HOME/.gnupg.$(date +%Y%m%d)
  • mkdir -p $HOME/.gnupg
  • chmod 700 $HOME/.gnupg
  • gpg --import $GPGHOME/pub-key.gpg
  • gpg --import $GPGHOME/master-secret-key.gpg
  • gpg --import $GPGHOME/sub-secret-keys.gpg
  • Import the keyring:
    • If you have a non-zero byte pubring.gpg: gpg --import $HOME/.gnupg.$(date +%Y%m%d)/pubring.gpg
    • For pubring.kbx: gpg --keyring=$HOME/.gnupg.$(date +%Y%m%d)/pubring.kbx --export | gpg --import

At this point, we also need to mark the root key as trusted. Run gpg --edit-key <key>, type trust, and mark the key as "ultimately trusted". You should also do this for any UIDs present: uid <num> + trust, and mark as "ultimately trusted".

Check to see if the yubikey has the keys properly marked, using gpg --card-status. You should see each of your keys with a "card-no: ..." entry; validate that the expiry is correct.

If not, or if card-status doesn't show any keys, you will need to move the keys onto the yubikey again.

  • Kill any running gpg-agents (killall gpg-agent)
  • Run gpg --edit-key <key>
    • toggle
    • Select the first subkey to move
    • keytocard; select the appopriate key type, agree to overwrite, and go from there.
    • Deselect the key, and select the next one; wash, rinse, repeat.
    • save

At this point, if you do gpg --list-secret-keys, you should see it correctly.

Finally: whenever you re-add keys to the card, you have to re-enable touch capabilities. Run the $HOME/bin/yubitouch.sh script to do this, running it once for each of the key types (sig, aut, dec). Remove your card, and re-insert it; run dmesg to ensure you see it, and then run ykman openpgp info to verify that the ykman tool can see it. Then run ykman openpgp keys set-touch dec on and ykman openpgp keys set-touch sig on (which enables touch for de/encryption and signature, but keeps it off for authentication; this allows you to enter your admin PIN once per SSH agent session, instead of requiring a touch each time you connect).

GitHub and signing keys

So, now it's time to update GitHub. I had already associated my GPG key with the service, but found that new releases I signed with my signing key were not showing up as verified. This bothered me.

So, I tried to export the signing key and upload it as an additional key on github. That failed; github complained that the key was already present.

Deleting the existing key indicated that I'd lose all verification on other releases I'd made.

In the end, I bit the bullet, and:

  • Deleted my existing key
  • Uploaded the contents of my pub-key.pgp

Lo and behold, all my previous verified releases were still verified, and new releases were as well! Additionally, the GPG key listing showed all my subkeys, and all email addresses associated with the primary key.

Using gpg-agent as an ssh-agent

I did the following:

  • First, remove ssh-agent from any oh-my-zsh plugin lists you're using.
  • Second, echo "enable-ssh-support" >> $HOME/.gnupg/gpg-agent.conf
  • Third, add a $HOME/.zsh/gpg-ssh-agent.zsh script with the following contents:
    #!/usr/bin/zsh
    gpg-connect-agent /bye
    export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh
    
    and, when done, source it in your .zshrc.

Open a new terminal, killall ssh-agent, and run ssh-add -l -E md5; you should see your GPG authentication key now listed as a key. You can add your default SSH keys as well, using ssh-add; they'll be added to $HOME/.gnupg/sshcontrol; I chose not to, and instead used the results of gpg --export-ssh-key <key> to seed authorized_keys entries everywhere.

The one issue I ran into was on AWS. I found I needed to ssh ANYWHERE else first, and, once I had, SSH to AWS worked fine.

Convert DSA 1024 GPG key to RSA 2048

I never got this to work; these are just my notes on what I tried.

Followed instructions at: http://atom.smasher.org/gpg/gpg-migrate.txt

First thing to remember: make sure you're using GPG v1 releases, and not v2, as it manipulates the secring.gpg and pubring.gpg. (I tried originally after first aliasing gpg to gpg2, which led me down an incorrect path.)

My second try didn't work, either; in the end, I had the new key, but the original key was not attached as a subkey properly.

Trying a third time... All goes well until I get to the step where the author notes "this last part makes no sense to me (but it doesn't seem to work otherwise)". When I get to this step, after removing the new key from the keyring, the original key disappears as well. Importing the exported secret and key imports the new key, but the original key is no longer listed as a subkey at that point; it's just completely gone.

I tried creating and verifying signatures before that step, and this worked for the new key, but not the original; at that point, the original is not considered a valid secret signing key.

One thing to note: when I listed the keys under the new key, the original, as a subkey, does not have any modes associated with it. I wonder if I need to toggle the capabilities somehow first?

Encrypted Disk Yubikey Challenge

I enabled disk encryption on two of my laptops, from 2017-2019. However, I found this was quite a bit of effort, and highly risky in the case where you have no backup key. I keep the instructions for posterity only.

I have enabled disk encryption on two of my laptops. On Linux, the encryption system is called luks, and there's a tool that will essentially allow you to use the yubikey as the primary authentication mechanism.

I followed the instructions in the blog post https://www.howtoforge.com/ubuntu-two-factor-authentication-with-yubikey-for-harddisk-encryption-with-luks The first key difference, is that you no longer need a PPA in order to install the yubikey-luks package; on 17.10, at least, it's "just there". The second difference is that it does not assume that /dev/sda5 is the encrypted partition, but instead /dev/sda3. You do not need to set any environment variables, however; the yubikey-luks-enroll script now also accepts a -d (for "device partition") argument, allowing you to specify it during invocation:

$ sudo yubikey-luks-enroll -d /dev/sda5