Restart hanging gpg-agent automatically using swatch on MacOSX

gpg-agentWith a smart card reader, you can store your gpg keys on a card to sign, encrypt and even authenticate. What if you try to sign an email but your email client is not responding while signing the email? Or  ssh authentication gets stuck in the login process? The reason might be a stuck gpg-agent.

Even when authenticating via ssh and starting in verbose mode, it does not tell you why it can’t continue. In verbose mode, ssh will seem stuck around the following line.

$ ssh user@host.example.com -v
...
debug1: Found key in /Users/user/.ssh/known_hosts:21
debug1: ssh_rsa_verify: signature correct
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: Roaming not allowed by server
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received

Under Mac OS X the gpg-agent seems to hang from time to time (see discussion on gpgtools.org). This causes applications trying to use the agent (like ssh) to wait for the agent which never responds. To resolve a stuck gpg-agent, the smart card reader needs to be disconnected, the gpg-agent restarted and the smart card reader reconnected. Doing this manually is annoying. With ‘swatch’ (Simple Log Watcher) this can be automated.

Installing swatch

The swatch(1) utility is written in Perl. At the time of writing ‘swatch’ is not available via homebrew or cpan. To install it, swatch needs to be downloaded from sourceforge.net.

$ wget http://vorboss.dl.sourceforge.net/project/swatch/swatch/3.2.3/swatch-3.2.3.tar.gz

The download link points to the current latest version of swatch. Swatch requires some perl modules to be installed. This required modules can be installed via cpan. Installing the required modules can be done with the following command and may take some time to complete.

$ sudo cpan install Date::Calc Date::Format Date::Manip File::Tail Time::HiRes Term::ANSIColor

To install, swatch uncompress the downloaded archive and change into the directory that has been created while uncompressing. Within this directory execute the following commands to install swatch in the system.

$ tar -xzf swatch-3.2.3.tar.gz
$ cd swatch-3.2.3
$ perl Makefile.PL
$ make
$ make test
$ sudo make install

Swatch installs into /usr/local/bin/ which might be different if you use perlbrew. The man page of swatch is available via the following command.

$ perldoc swatch

Configure swatch to restart gpg-agent

To set swatch up you need to give it a config file telling it which files to watch and what to do based on them. The configuration file can be defined by command line argument. If none is specified, swatch tries to find it in the user’s home directory.

$ vim ~/.swatchrc

In this example swatch will check for the event when the smart card reader is connected and disconnected. In both cases a line is written to a logfile called ~/swatch.log. Additionally, a command is executed to restart the gpg-agent.

watchfor /^(.{15}) .*com.apple.SecurityServer.*Token reader (.*) removed from system/
exec 'echo "\033[1;31m$1 - SCR\040 - DISCON - Disconnected '$2'\033[0m" >>~/swatch.log'
exec pkill -9 gpg-agent ; gpg-agent --daemon --write-env-file >/dev/null
watchfor /^(.{15}) .*com.apple.SecurityServer.*Token reader (.*) inserted into system/
exec 'echo "\033[1;32m$1 - SCR\040 - CONN \040 - Connected '$2'\033[0m" >>~/swatch.log'
exec pkill -9 gpg-agent ; gpg-agent --daemon --write-env-file >/dev/null

When swatch is started with the following argument, it will watch the system.log file for messages indicating a smart card reader has been connected or disconnected. When such a line is found in the log, a message is written into the ~/swatch.log file and the gpg-agent is restarted.

$ swatch --tail-file "/private/var/log/system.log"

To start swatch automatically, create a script (similar to init-scripts) with the parameters to start and stop swatch. In this example the script is called ‘swatch.sh’. Its main purpose is to start swatch but will also make sure that there will be only one instance started.

#!/bin/sh
# Simple Log Watcher script
case "$1" in
'start')
if ps -p $(cat /usr/local/var/run/swatch.pid) > /dev/null 2>&1
then
echo "swatch is already running"
else
/usr/local/bin/swatch --daemon --pid-file=/usr/local/var/run/swatch.pid --tail-file="/private/var/log/system.log"
fi
;;
'stop')
PID=`cat /usr/local/var/run/swatch.pid`
kill $PID
;;
*)
echo "Usage: $0 { start | stop }"
;;
esac
exit 0

Create a so-called plist file, which is actually an xml file with the file extension .plist, for launchd to start swatch automatically when you login.

vim ~/Library/LaunchAgents/net.sourceforge.swatch.plist

The plist file will contain the setting “RunAtLoad” to instruct launchd to start swatch at login. The script created earlier and the argument ‘start’ is shown as separate elements under the “ProgramArguments” item.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<false/>
<key>Label</key>
<string>net.sourceforge.swatch</string>
<key>ProgramArguments</key>
<array>
<string>/Volumes/Daten/00_Environment_Mac/20_Applications/_Own_/swatch.sh</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

To activate this new launchd configuration, you either need to logout and login again or you can use the following command to immediately load the plist file.

launchctl load ~/Library/LaunchAgents/net.sourceforge.swatch.plist

While launchd reads the new configuration, the script is immediately executed. With swatch running, each time the smart card reader is disconnected or connected, gpg-agent is restarted and a log line like those below are written to the logfile. If your shell supports it, the disconnect lines are shown in red while the connect lines are shown in green.

Mar 12 22:32:40 - SCR  - DISCON - Disconnected 'SCM Microsystems Inc. SCR 355'
Mar 12 22:32:46 - SCR  - CONN   - Connected 'SCM Microsystems Inc. SCR 355'

From now on, a hanging gpg-agent can be restarted by disconnecting and reconnecting the card reader. No need to open the shell or remember any commands.

I hope this workaround is obsolete soon and gpg-agent is working as it should, until then, this workaround will help to keep everything working.


Read more of my posts on my blog at http://blog.tinned-software.net/.

This entry was posted in MacOSX, Security and tagged , . Bookmark the permalink.