For some time I've been using a hand-rolled solution for touchID over ssh which
I previously blogged about. Up until recently it's been a somewhat
loosely-compiled scattering of config that wasn't really in a releasable form
but with a pending security talk on the horizon I thought it would be worth
tidying it up and making it releasable so I could mention it in my talk.
That code can be found here: https://github.com/m4rkw/touchid-remote
However when I was compiling it I was under the impression that one of the tools
it relies on - touch2sudo - was merely a standalone binary for authenticating
sudo commands locally. I didn't realise that the author had also noted in the
README that it's possible to use it over SSH. The solution presented there
involves forwarding the local ssh agent over the SSH connection and then
configuring touch2sudo as the askpass agent.
This is cool and has some advantages over my solution - there are no static
secrets that need to be configured and no local server daemon that needs to be
running locally.
It got me thinking though, for a while I've been using Secretive for ssh keys -
https://github.com/maxgoedjen/secretive. This lets you create ssh keys in the
mac's secure enclave so the private key cannot be extracted. When you create
such a key you can optionally choose to require touchID authentication with it.
I wondered if I could perhaps use this instead of touch2sudo for a simpler
solution that required less config and no local server daemon.
The pam_ssh_auth_agent PAM module is available for Debian distributions but the
version that came with my machines was 0.10.3 which segfaults when used with
keys from the secure enclave. 0.10.4 also seems to do this but the latest code
on github works.
Here's the setup process.
1) Compile and install pam_ssh_agent_auth on your target machines:
$ git clone https://github.com/jbeverly/pam_ssh_agent_auth
$ cd pam_ssh_agent_auth
$ git submodule init
$ git submodule update
$ ./configure --without-openssl-header-check # this flag was necessary for me
$ make
$ sudo make install
2) Add this line to your sudoers file:
Defaults env_keep += SSH_AUTH_SOCK
3) Enable the module in /etc/pam.d/sudo:
auth sufficient /usr/local/libexec/pam_ssh_agent_auth.so file=/etc/ssh/sudo_authorized_keys/%u
4) Create the authorized_keys file with the public key from Secretive, this
should be set to require touchID and the file should be root-owned and mode
0600.
5) Configure the local ssh connection in ~/.ssh/config:
Host myserver
Hostname myserver.mydomain.com
IdentityAgent ~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh
ForwardAgent yes
Now connect to the machine and execute sudo, you should get a touchID prompt
from Secretive asking you to authenticate. This is very cool and doesn't require
touch2sudo at all, everything is done by the Secretive agent.
It's important to understand the security implications of forwarding your local
agent to the remote server. An adversary that manages to gain access to the
forwarded socket on the remote machine can attempt to make use of any keys that
are held on the agent which could potentially be used to access other machines
that accept those keys. To mitigate this it's worth making sure that all of your
keys in Secretive are set to require touchID so they can't be silently used
without your awareness.
Another really cool thing you can do with this approach is to delegate a key on
your iOS device as having sudo access as well. For example if you create a new
ssh key in your SSH client on iOS, add it to the authorized_keys file specified
above and enable agent forwarding on the connection you can then invoke sudo and
it will use the key from the agent to authenticate sudo - passwordless and
without any prompt. Because iOS devices are inherently very secure this is a
reasonable tradeoff - my SSH client (Shelly) requires faceID when it loads.
Prevously I was using the Duo PAM module to authenticate sudo over SSH when
connecting from my iOS device but this new method is much quicker and easier as
there's no confirmation step.