I recently bought a U2F Zero, an $8.00 open source U2F (Universal 2nd Factor) hardware key.
After the initial setup of the U2F Zero (hint: there was none), I immediately wondered how I could enable it as a required second factor when logging into my Mac.
After a bit of browsing on Yubico's site and a couple of blogs, I didn't find a solid step-by-step tutorial on how to set this up that fully described the process and the different configurations. So here we are.
Note: These instructions apply for any U2F token, not just the U2F Zero or YubiKey devices.
If you're feeling dangerous (and I really do mean that), you can skip to the very end and just see which commands to run.
U2F Zero
The U2F Zero is an open source DIY (or purchase pre-built from Amazon) hardware token that supports U2F (Univeral 2nd Factor) authentication. At $8.00, it's hard to beat the price, and similar products like the various YubiKeys aren't open source, which is a minor inconvenience for some and a dealbreaker for others.
You have the option of either purchasing it pre-assembled from Amazon or building it yourself. While I always enjoy soldering, the programmer alone is around $35, so unless you're going to build quite a few, you might be best off just buying them pre-built.
Enabling 2FA for OS X
Note: I'm doing this on macOS Sierra (10.12), but this should work on any recent version of OS X.
Basically I am trying to achieve the following: when logging in either after a restart or upon waking from sleep, I would like to require a U2F token as a second authentication factor, in addition to my password.
The process is actually extremely simple, and boils down to:
- Install the
pam-u2f
module - Generate the Authorization Mapping File for your key(s)
- Edit the system configuration files to enable U2F authentication
1. pam-u2f
Module
Installing the pam-u2f
module is easiest with Homebrew, in which case a simple
brew install pam-u2f
will suffice.
If you're not using Homebrew on OS X (seriously?), you can build it from source following the instructions here. You will also need autoconf
and automake
and friends, and also libu2f-host
and libu2f-server
.
In either case, you will end up with a shared library called pam_u2f.so
. If you installed via Homebrew (be sure to check the version since the path will change) it will be located at /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so
.
To make the configuration step easier, you can symlink the Homebrew library or copy the library you built into /usr/lib/pam/
.
cd /usr/lib/pam
sudo ln -s /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so
# Or copy if you built it yourself
Note: You will get an
Operation not permitted
message on OS X El Capitan or macOS Sierra, since you must disable System Integrity Protection to have write access to anything in/usr
other than/usr/local
. You can disable SIP by booting into recovery mode (⌘ + R) and then opening a Terminal and executingcsrutil disable
.
If you decide not to disable SIP or just not to link or copy into /usr/lib/pam
, you can also just provide the full path to the pam_u2f.so
file in the configuration files later.
2. Authorization Mapping Files
In order for the module to know which U2F token to accept the challenge response from and authenticate with, you must store some information about your token. Specifically, you must create what is called an Authorization Mapping File, which contains a list of token pairs consisting of the key handles and the public keys.
More information on these files can be found here.
To generate this file, you can use the pamu2fcfg
utility which is installed with the pam-u2f
recipe on Homebrew or can also be built manually.
Insert your U2F token and then open a Terminal and run
pamu2fcfg
Note: If you want to be verbose about the username for the token you can add the
-u <username>
option.
If the device is inserted correctly, the colour of the light on the U2F Zero will change, prompting you to press the button to complete the challenge response. If everything worked you will see some output on the screen consisting of
<username>:<key handle>,<public key>\n
I'm going to place this in a file called ~/.config/u2f/keys
, but you can place it anywhere. To directly output to the file:
# Create the directory if it doesn't exist
mkdir -p ~/.config/u2f
# Use >> if you want to append, or a single > if you want to rewrite the file
pamu2fcfg > ~/.config/u2f/keys
You'll need to specify this location in the PAM configuration files, unless you place it in the file ~/.config/Yubico/u2f_keys
, which is the default search location.
Multiple U2F Tokens
To use multiple tokens under the same user on the same machine, you just need to concatenate the second token's key information to the mapping file.
With one user and two tokens, the mapping file should look like:
<username>:<key handle 1>,<public key 1>:<key handle 2>,<public key 2>
Thankfully, the PAM U2F config tool includes an option (-n
) to leave off the username, so in the simple case of one user and two tokens you can just do
pamu2fcfg -n >> ~/.config/u2f/keys
with the second token.
If, however, you wanted to add a second token but for a different user, you would just add a separate line with the output right from pamu2fcfg
, no modification necessary.
For multiple users and/or tokens the mapping file could look like:
<username 1>:<key handle 1>,<public key 1>:<key handle 2>,<public key 2>
<username 2>:<key handle 3>,<public key 3>:<key handle 2>,<public key 2>
In that example, both users have a unique token (keys 1
and 3
) and also share a token (key 2
).
3. PAM Configuration Files
The PAM configuration files are located at /etc/pam.d
.
You will see a list of configuration files for various authentication modules here. Of these files, the screensaver
and authorization
files are of particular interest to us.
Note: In these steps it is extremely important to use the
sufficient
keyword and not therequired
keyword when first configuring these. This gives you the opportunity to test it out and verify that you can correctly use your U2F token to log in.
Open up the screensaver
file (with sudo
, of course) and add a line consisting of:
auth sufficient <path to pam_u2f.so> authfile=<path to mapping file>
Using the full Homebrew path and placing my authorization mapping file under ~/.config/u2f/keys
, my line looks like:
auth sufficient /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so authfile=/Users/joe/.config/u2f/keys
Note: You cannot perform shell expansions (e.g.
~
) here so give the full path.
If you symlinked or installed directly into /usr/lib/pam
, and if you created your mapping file under ~/.config/Yubico/u2f_keys
, you can simply use:
auth sufficient pam_u2f.so
Then save and exit. Make sure you're configured to require a password immediately after going to sleep, and then tell your Mac to go to sleep.
Upon waking, insert your U2F token and then type your password. After entering the password, it should pause and the light on your U2F Zero should change colours. Et voilà! Press the button to complete the challenge response and you should be successfully logged in with your U2F as a second factor!
You can now perform the next step by adding the same line to the authorization
file, and then log out and back in to test.
Since we chose sufficient
for the test, if you don't have your token inserted when you type your password, it will log in without asking for it. To require your token in addition to your password for true 2FA, simply change the sufficient
keyword to required
in the authorization
and screensaver
files.
Note: It is a very good idea to first try the
required
configuration with thescreensaver
file only, since if something goes wrong, you can still authenticate via the normal login screen without your token.
As mentioned, be careful with requiring the token. If you lose your token, you may be totally locked out of your Mac. One solution to this is to buy two tokens and add both of them to your authorization mapping file. Then store that second token away for safe keeping.
Commands
If you really truly don't think you need to read about what it is that you're doing, you can achieve all of this by just running the following commands:
# Install PAM U2F Module
brew install pam-u2f
# Generate a new authorization mapping file
mkdir -p ~/.config/u2f
# >>> Insert U2F Token
pamu2fcfg > ~/.config/u2f/keys
# >>> Press button on U2F token
# >>> Remove token
# >>> Insert second U2F token (if you have one)
pamu2fcfg -n >> ~/.config/u2f/keys
# >>> Press button on second U2F token
# >>> Remove token
# ... Repeat for each additional token
# Edit PAM configuration files
sudo $EDITOR /etc/pam.d/screensaver
### Add a line with:
# auth required <path to pam_u2f.so> authfile=<path to mapping file>
sudo $EDITOR /etc/pam.d/authorization
### Add a line with:
# auth required <path to pam_u2f.so> authfile=<path to mapping file>