Using a Yubikey for SSH on macOS

SSH 8.2 introduced support for using any U2F key in place of a private key file. Using it on macOS with full support for ssh-agent is a bit more complex.

Generating the keys

  1. You must choose between ed25519-sk and ecdsa-sk. Try ed25519-sk (Options 1 or 3) first. If it does not work due to device incompatibilities, fall back on ecdsa-sk (Options 2 or 4)

  2. You must choose if you want to store the key handle as a resident key on the device. If you want to, use options 1 or 2. If not, use options 3 or 4.

    A U2F attestation requires a key handle to be sent to the device. When generating the key, ssh-keygen will create private and public key files that look similar to normal ssh key. The private key file is actually a key handle that cannot be used without the hardware token, however, the hardware token can also not be used without the key handle.

    A resident key solves this problem by storing the key handle on the device. However, your key may or may not support it and only a limited number of resident keys may be stored on a device. Additionally, it may reduce the security of your ssh key as they could use it if they steal the hardware device. For this reason, a good pin is important.

    It is your choice whether to use a resident key. If you do, you can load it directly to the ssh-agent using ssh-add -K, or write the key handle and public key to disk using ssh-keygen -K

ssh-keygen -t ed25519-sk -O resident # 1
ssh-keygen -t ecdsa-sk -O resident   # 2
ssh-keygen -t ed25519-sk             # 3
ssh-keygen -t ecdsa-sk               # 4

Updating SSH

SSH v8.2 is required to use a security key. Install it with brew.

brew install openssh

You can specifiy the path to the private key handle in your ssh config. Otherwise, you can configure the ssh-agent.

ssh-agent on macOS

To be used with a security key, the ssh-agent must be on v8.2, which the system default is not.

First, disable the macOS default ssh-agent for your user.

launchctl disable user/$UID/com.openssh.ssh-agent

Next, add a new launchd service for your ssh-agent. Add the following file to ~/Library/LaunchAgents/com.zerowidth.launched.ssh_agent.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>com.zerowidth.launched.ssh_agent</string>
	<key>ProgramArguments</key>
	<array>
		<string>sh</string>
		<string>-c</string>
		<string>/usr/local/bin/ssh-agent -D -a ~/.ssh/agent</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
</dict>
</plist>

And load it with launchctl load -w ~/Library/LaunchAgents/com.zerowidth.launched.ssh_agent.plist.

In your .bashrc or .zshrc, set SSH_AUTH_SOCK="~/.ssh/agent"

This plist was created using the launchd plist generator over at zerowidth. It runs the command /usr/local/bin/ssh-agent -D -a ~/.ssh/agent. -D prevents ssh-agent from forking, and -a ~/.ssh/agent directs the agent to create a socket file at that location that is referenced in $SSH_AUTH_SOCK.


462 Words

2020-05-18 00:00 +0000