touch2sudo: Enable remote sudo two-factor authentication using Mac Touch ID
The Touch ID on MacBook Pro and MacBook Air is an impressive feature that enables you to use your fingerprint for authentication. In this write-up, I’m going to show how we can extend this feature to authenticate remote SSH
As a good security practice, running sudo commands require some form of authentication. If you are using password based authentication scheme, you are often prompted for passwords before running sudo commands. Typing password all the time is cumbersome and a counterproductive activity for engineers. It also forces users to keep passwords on remote servers, which is not considered secure. Hence to avoid storing passwords in servers, we need to choose a different authentication scheme for sudo. One option is to completely disable sudo authentication. This is not recommended because as a user, you may accidentally do dangerous or irrevocable actions using your root privileges — imagine you unknowingly execute some scripts that contain sudo commands. Besides that, sudo authentication puts additional security barrier against local privilege escalation attacks in case of a server compromise.
Solution in a nutshell
There are a number of PAM based solutions available that support various authentication schemes for sudo. The solution we describe here use
pam-ssh-agent-auth — a PAM module that does SSH key authentication for sudo. pam-ssh-agent-auth is based on SSH agent forwarding feature that allows the PAM module to authenticate sudo command using key cached in
ssh-agent running on your workstation (Mac).
You can enable agent forwarding by adding sudo identities (keys) to your local ssh-agent, and then connect to remote host with
ssh -A command-line flag (or by setting
AgentForward flag in your ssh config file).
ssh-add is the command line tool to add identities (SSH private keys) to ssh-agent. An interesting optional feature provided by ssh-add for adding identities is
-c option. It allows the user to force a confirmation program for the added identities before being used for authentication. For any authentication request for keys that require confirmation, ssh-agent attempts to execute
SSH_ASKPASS confirmation program. The core of this writeup is a custom SSH_ASKPASS confirmation program —
touch2sudo which authenticates your remote sudo commands using Mac Touch ID.
touch2sudo is a standalone program, if executed authenticates the user either through Touch ID or password. A successful authentication (confirmation) is signaled by a zero exit status from touch2sudo program. To authenticate sudo commands, we configure touch2sudo as SSH_ASKPASS confirmation program, invoked by ssh-agent.
You can find the touch2sudo source code here:
The end to end setup is described in the following sections.
Fingerprint authentication is done locally on Mac, but it acts as a gating mechanism for remote sudo authentication.
If you haven’t setup Touch ID, you can find the instructions from Apple here.
You may either install
touch2sudo binary using
brew OR build it from source
Install using brew
brew tap prbinu/touch2sudo
brew install touch2sudo
Build from source
$ git clone https://github.com/prbinu/touch2sudo
$ cd touch2sudo
touch2sudo.xcodeprojfile using Xcode
- Build: (Product -> Build) If the build is successful, you would see this dialog:
3. Archive: (Product -> Archive -> Distribute Content -> Build Products -> Next -> Save) Save the archive folder. The touch2sudo executable binary will be in the
4. Install: Copy
touch2sudo binary to
Configure ssh-agent with touch2sudo
Generate a new SSH key pair for sudo:
$ ssh-keygen -t rsa -b 2048 -C email@example.comGenerating public/private rsa key pair.
Enter file in which to save the key (/Users/binu/.ssh/id_rsa): id_rsa_sudo
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_sudo.
Your public key has been saved in id_rsa_sudo.pub.
The key fingerprint is: SHA256:2848NrL7O4UNaCh7kgECmt2zlphtllZjs/VwjdPxfe0 firstname.lastname@example.org
The key's randomart image is:
|o . |
|oo.. + o ..|
|o...o =.o.+ o . +|
| +oB.=o+.. ..|
| o X=..S .+ E|
| =+ . o. o |
| o . .. |
| .+= |
| o**= |
$ export SSH_ASKPASS=/usr/local/bin/touch2sudo
$ export DISPLAY=0
$ eval $(ssh-agent)
Agent pid 51863$ ssh-add -L
The agent has no identities.$ env | grep SSH
SSH_ASKPASS=/usr/local/bin/touch2sudo$ ssh-add -c id_rsa_sudo
Identity added: id_rsa_sudo (email@example.com)
The user must confirm each use of the key
Now let’s configure the remote server.
Linux (Ubuntu) server configuration
Test Environment: Ubuntu 16.04
Follow the steps below to setup
pam-ssh-agent-auth on your server:
$ wget "https://downloads.sourceforge.net/project/pamsshagentauth/pam_ssh_agent_auth/v0.10.3/pam_ssh_agent_auth-0.10.3.tar.bz2"$ bunzip2 pam_ssh_agent_auth-0.10.3.tar.bz2
$ tar -xvf pam_ssh_agent_auth-0.10.3.tar
$ cd pam_ssh_agent_auth-0.10.3/
Build and install
# install build dependencies
$ sudo apt update -y
$ sudo apt install libssl-dev libpam0g-dev -y
$ sudo apt install gcc make checkinstall -y# build
$ ./configure --libexecdir=/lib/x86_64-linux-gnu/security --with-mantype=man && make# install pam_ssh_agent_auth PAM module
$ sudo checkinstall
For demonstration purpose, I have created a user bob on remote host, and copied my SSH authentication keys to
$ sudo useradd -m -s /bin/bash -U bob
$ sudo -su bob
$ mkdir /home/bob/.ssh
$ chmod 700 /home/bob/.ssh# Copy SSH login public key (from Mac)
$ LOGIN_PUBKEY="<login public key from Mac>"
$ echo $LOGIN_PUBKEY > /home/bob/.ssh/authorized_keys
On your Mac, generate new SSH key pair for sudo purpose. Copy the public key to server as follows:
# On your Linux server
$ sudo mkdir /etc/ssh/sudo_authorized_keys# Copy sudo public key (from Mac)
$ SUDO_PUBKEY="<sudo public key from Mac>"
$ echo $SUDO_PUBKEY | sudo tee -a /etc/ssh/sudo_authorized_keys/bob
$ sudo chmod 644 /etc/ssh/sudo_authorized_keys/bob
SSH agent forwarding allows a remote host to forward authentication requests back to ssh-agent running on your workstation. This is commonly used in scenarios where users need to hop from one server to other. In such cases, without agent forwarding, the user needs to copy private keys to intermediate hosts — which is insecure (another option is to to SSH
To authenticate the sudo command, pam_ssh_agent_auth has to connect with the ssh-agent running on your Mac. The
SSH_AUTH_SOCK environment variable on your remote host exports ssh-agent’s connect path (Unix domain socket file), and has to be made available for the PAM module.
$ sudo visudo# Add the following line to the sudoers file, save and exit.
Defaults env_keep += SSH_AUTH_SOCK
pam_sudo file, and add the following line. This will force bob to authenticate sudo commands.
$ echo "bob ALL = (ALL) ALL" | sudo tee /etc/sudoers.d/pam_sudo# Apparently you can also use groups (% prefix) instead of user.
#%<unix-group-name> ALL = (ALL) ALL
The last step is to edit
/etc/pam.d/sudo file to enable PAM authentication for sudo. Add the following line to the beginning of this file. Note that the order of the line is important.
auth sufficient /lib/x86_64-linux-gnu/security/pam_ssh_agent_auth.so file=/etc/ssh/sudo_authorized_keys/%u
Here is the file after the update:
#%PAM-1.0auth sufficient /lib/x86_64-linux-gnu/security/pam_ssh_agent_auth.so file=/etc/ssh/sudo_authorized_keys/%usession required pam_env.so readenv=1 user_readenv=0
session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0@include common-auth
On your Mac:
$ export SSH_AUTH_SOCK=/var/folders/hm/x1_38yz53td1jty5xgs39dxm2lm58d/T//ssh-JssXsflTuHrC/agent.51862$ ssh -A firstname.lastname@example.org -p 2222Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-141-generic x86_64)
Last login: Sun Jan 20 03:28:18 2019 from 10.0.2.2bob@ubuntu-xenial:~$ sudo tailf /var/log/auth.log
...Jan 20 03:32:07 ubuntu-xenial sudo: pam_ssh_agent_auth: matching key found: file /etc/ssh/sudo_authorized_keys/bob, line 2Jan 20 03:32:07 ubuntu-xenial sudo: pam_ssh_agent_auth: Found matching RSA key: f9:d4:6f:91:da:ae:10:18:26:3d:93:dc:e3:52:c7:4eJan 20 03:34:30 ubuntu-xenial sudo: pam_ssh_agent_auth: Authenticated: `bob' as `bob' using /etc/ssh/sudo_authorized_keys/bobJan 20 03:34:30 ubuntu-xenial sudo: bob : TTY=pts/2 ; PWD=/home/bob ; USER=root ; COMMAND=/bin/tailf /var/log/auth.logJan 20 03:34:30 ubuntu-xenial sudo: pam_unix(sudo:session): session opened for user root by bob(uid=0)
It is well known that agent forwarding has an unintended security consequence. When you enable agent forwarding, it establishes a forwarding socket from the connected server (say host-A) back to ssh-agent running on your workstation. This means your ssh-agent is accessible from host-A, and can perform authentication when you initiate a new SSH session from host-A to host-B.
The problem with this model is, if your session to host-A is active, anyone with sufficient permissions (e.g. sudo users or an attacker who managed to compromised the host) on host-A can impersonate you and connect to host-B by performing authentication using keys from your ssh-agent.
The following steps can help mitigate agent forwarding for sudo:
- Do not use your SSH login keys for sudo authentication, instead use dedicated key for sudo authentication.
- On the server, the
authorized_keyfile that contains the sudo authentication public key should be owned by root. On Linux (Ubuntu), copy sudo public key to
- On your workstation, use dedicated ssh-agent for sudo.
$ eval $(ssh-agent)# The -c option is important.
$ ssh-add -c <sudo-auth-public-key-file> $ ssh -A -i <ssh-login-auth-public-key-file> <remote-host>
Following the above steps can stop an adversary impersonate your identity and move laterally to other servers. However certain SSH constraints in your environment may preclude you from following this method.