How I remember my YubiKey, take three

By | June 5, 2019

[This is obsolete. My improved code is now in Github.]

[The technique in this article supersedes my earlier “How I remember my YubiKey, take two” how-to; I explain at the bottom of this article what was wrong with my earlier technique and why this new technique is better.]

I’ve recently started using a YubiKey NEO for two-factor authentication for sites that support it.1

Because I am using my YubiKey for more and more sites, I tend to leave it plugged in whenever I am in front of a computer for an extended period of time. The first day I was using the key it became clear that this was going to be a problem, when I left it plugged into my computer at work and didn’t realize it until I’d gotten home. Yes, new technology leads to new problems, but problems that are created by technology can be solved by technology too. Here’s how I solved the “Don’t forget your YubiKey at work” problem.2

In a nutshell, I am generating push notifications from my computer to my phone every time I plug in or unplug my YubiKey, reading those notifications in the Android Tasker app to to track whether it’s plugged in, and using Tasker to alert me if I walk away from my computer when it is. I explain in detail below how each of these pieces works.

Set up push notifications from my computer to my phone

I registered for an IFTTT account and installed the IFTTT Android app so that I can generate push notifications from IFTTT to my phone.

On the IFTTT web site, I set up a new applet as follows:

  1. For “this”, select “Webhooks” and then “Receive a web request”.
  2. Enter “yubikey_plugged_in” as the event name.
  3. For “that”, select “Notifications” and then “Send a notification from the IFTTT app”.
  4. For the message, enter “YubiKey is plugged in”.

Then I repeated the above steps but this time I used “yubikey_unplugged” as the event name and “YubiKey is not plugged in” as the message.

Next, I went to https://ifttt.com/services/maker_webhooks/settings and copied down my Maker key, which is the last component of the URL shown on that page, and saved it in the file /root/.ifttt_maker_key.

Finally, I tested:

curl https://maker.ifttt.com/trigger/yubikey_plugged_in/with/key/$(sudo cat /root/.ifttt_maker_key)
curl https://maker.ifttt.com/trigger/yubikey_unplugged/with/key/$(sudo cat /root/.ifttt_maker_key)

Once I followed the steps above properly, the two curl commands above generated two notifications on my phone, one of which said “YubiKey is plugged in” and the other “YubiKey is not plugged in”.

Set up my computer to notify my phone whenever my YubiKey is plugged in or unplugged

This has four pieces:

  1. a shell script that does the notifying;
  2. a systemd service that invokes the shell script;
  3. a udev rule that triggers the service; and
  4. a systemd timer that also triggers the service once per minute, just in case the udev rule doesn’t work.

The shell script

This is installed as /usr/local/bin/yubikey-monitor.sh:

#!/bin/bash -e

export HOME=$(eval echo "~$(whoami)")
FLAG=/var/run/yubikey-watcher
MIN_NOTIF_GAP=30 # seconds
KEY_FILE=/root/.ifttt_maker_key

trap "rm -f \"$FLAG.lock\"" EXIT
lockfile -1 -l 15 "$FLAG.lock"

check_yubikey() {
    usb-devices 2>/dev/null | grep -q -s -i -w yubikey
}

next_state() {
    if [ "$ACTION" = "add" ]; then
        if [ "$STATE" = "just-plugged" -o "$STATE" = "plugged" ]; then
            echo "plugged"
        else
            echo "just-plugged"
        fi
    elif [ "$STATE" = "just-unplugged" -o "$STATE" = "unplugged" ]; then
        echo "unplugged"
    else
        echo "just-unplugged"
    fi
}

notify_message() {
    if [ "$ACTION" = "add" ]; then
        echo "yubikey_plugged_in"
    else
        echo "yubikey_unplugged"
    fi
}

doit() {
    if check_yubikey; then
        ACTION="add"
    else
        ACTION="remove"
    fi

    set $(cat $FLAG 2>/dev/null || : )
    if [ "$1" ]; then
        STATE=$1; shift
        if [ "$1" ]; then
            LAST_TIME=$1; shift
        fi
    fi

    NEXT_STATE=$(next_state)

    if [ "$NEXT_STATE" = "$STATE" ]; then
        exit
    fi

    if [ "$LAST_TIME" ]; then
        DELTA=$(($(date +%s) - LAST_TIME))
        REMAINING=$((MIN_NOTIF_GAP - DELTA))
        if [ $REMAINING -gt 0 ]; then
            echo Delaying notification for $REMAINING seconds
            sleep $REMAINING
            doit
            return
        fi
    fi

    if ! ifttt_key=$(cat $KEY_FILE); then
        echo IFTTT Maker key not installed in $KEY_FILE 1>&2
    else
        url=https://maker.ifttt.com/trigger/$(notify_message)/with/key
        echo $(curl --silent "$url/$ifttt_key")
        echo Triggered "$url/[key-elided]"
    fi
    echo "$NEXT_STATE $(date +%s)" >| $FLAG
}

doit

The script is written to generate two notifications every time the YubiKey is plugged in or unplugged, just in case one of the notifications gets lost. That’s probably overkill, but shrug.

The systemd service

This is installed as /etc/systemd/system/yubikey-monitor.service, after which systemctl daemon-reload is run to tell systemd to load it:

[Unit]
Description=Check for inserted Yubikey
StartLimitIntervalSec=0

[Service]
Type=exec
ExecStart=/usr/local/bin/yubikey-monitor.sh

The udev rule

This is installed as /etc/udev/rules.d/50-yubikey.rules, after which udevadm control --reload-rules is run to tell udev to reread all of its rules.

ATTRS{idVendor}=="1050", ACTION=="add|remove", RUN+="/bin/systemctl start yubikey-monitor.service"

The systemd timer

This is installed as /etc/systemd/system/yubikey-monitor.timer, after which systemctl daemon-reload is run to tell systemdto load it:

[Unit]
Description=Check for inserted Yubikey every minute

[Timer]
OnStartupSec=60
OnUnitActiveSec=60

[Install]
WantedBy=timers.target 

Process the notifications in Tasker

Once you’ve got the computer generating notifications to your phone, you need to configure the phone to process the notifications. I’m using the AutoNotification app, which integrates with Tasker, to do that. Here are the relevant Tasker configs:

    Profile: YubiKey Plugged In (37)
Event: AutoNotification Intercept [ Configuration:Event Behaviour: true
Notification Type: Only Created Notifications
Notification App: IFTTT
Notification Text: YubiKey is plugged in ]
Enter: Set YubiKey Variable (34)

Profile: YubiKey Unplugged (38)
Event: AutoNotification Intercept [ Configuration:Event Behaviour: true
Notification Type: Only Created Notifications
Notification App: IFTTT
Notification Text: YubiKey is not plugged in ]
Enter: Clear YubiKey Variable (36)
Exit: Remove YubiKey Reminder (33)

Task: Set YubiKey Variable (34)
A1: Variable Set [ Name:%YUBIKEY To:1 Recurse Variables:Off Do Maths:Off Append:Off ]
A2: AutoNotification Cancel [ Configuration:Other Id: %anid
Package: %anpackage
Tag: %antag Timeout (Seconds):20 ]

Task: Clear YubiKey Variable (36)
A1: Variable Clear [ Name:%YUBIKEY Pattern Matching:Off Local Variables Only:Off ]
A2: AutoNotification Cancel [ Configuration:Other Id: %anid
Package: %anpackage
Tag: %antag Timeout (Seconds):20 ]

Task: Remove YubiKey Reminder (33)
A1: Notify Cancel [ Title:Don't Forget Your YubiKey Warn Not Exist:Off ]

Notify me when I walk away from my computer

Finally, these additional Tasker configs generate a notification (which goes to my Fitbit) and a silent vibration when I walk ten steps away from my computer, or a really loud alert when I walk fifty steps away:

    Profile: YubiKey Soft Reminder (39)
Event: Steps Taken [ Number:10 ]
State: Variable Value [ %YUBIKEY ~ 1 ]
Enter: Quiet YubiKey Reminder (30)

Profile: YubiKey Loud Reminder (41)
Event: Steps Taken [ Number:50 ]
State: Variable Value [ %YUBIKEY ~ 2 ]
Enter: Loud YubiKey Reminder (40)

Task: Quiet YubiKey Reminder (30)
Run Both Together
A1: Notify [ Title:Don't Forget Your YubiKey Text:Don't Forget Your YubiKey Icon:null Number:0 Permanent:Off Priority:5 ]
A2: Vibrate Pattern [ Pattern:0,1000,250,1000,250,1000 ]
A3: Variable Set [ Name:%YUBIKEY To:2 Recurse Variables:Off Do Maths:Off Append:Off ]

Task: Loud YubiKey Reminder (40)
Run Both Together
A1: Morse [ Text:oo Frequency:4000 Speed:80 Amplitude:50 Stream:4 ]
A2: Variable Set [ Name:%YUBIKEY To:3 Recurse Variables:Off Do Maths:Off Append:Off ]

Conclusion

So, there you have it. My phone is now alerting me whenever I walk away from my computer and leave my YubiKey behind, first quietly and then loudly if I don’t pay attention, You can do it too, assuming that you’re as ridiculously obsessive about stuff like this as I am and willing to take the time to set it up.

Please email me or comment if you found this useful!

P.S. Don’t forget to back up your Tasker configuration (“Data” -> “Backup” from the home screen menu) and save the backup file somewhere off your phone, so you don’t have to rebuild everything if you lose or break it!

P.P.S. My first attempt at solving this problem involved having Tasker alert me if I strayed so far from my computer that it was no longer visible to my phone via Bluetooth. This proved to be quite unreliable; I was unable to get Tasker to reliably detect a nearby Bluetooth device, causing both false positives (i.e., my phone alerting me when I was still standing at my desk) and false negatives (i.e., my phone failing to alert me when I walked away from my desk without my YubiKey). The notification-based solution described here appears, at least so, far to be much more reliable.

P.P.P.S. My second attempt at solving this problem, which more closely resembled this one, worked fine until the Notify app and disappeared from the app store and the NodeJS notify command-line interface stopped working, or more accurately, it said it was sending notifications but my phone never received them. Other people were having the same problem, so I decided to switch to using IFTTT. Also, in addition to modifying yubikey-monitor.sh to use IFTTT instead of Notify, I’ve refactored it in some other ways to make it more robust.


1A quick primer, for those of you who are unfamiliar… The YubiKey sends two-factor authentication information to web sites when I either tap the button on the key, when it is plugged into my computer, or tap it on my phone’s NFC sensor, if I’m logging into somewhere on my phone. Furthermore, for sites that support Universal 2nd Factor (U2F) authentication, the YubiKey adds an additional layer of security, confirming the identity of the web site I’m logging into to ensure that I’m not being phished.

2If someone else has already solved this problem and I’m a fool to have wasted my time on it ;-), please feel free to let me know in a comment below. I’m always happy to throw away my own hacks and use somebody else’s instead, if it means one less hack for me to have to maintain.

Share

4 thoughts on “How I remember my YubiKey, take three

  1. Pingback: How I remember my YubiKey, take four – Something better to do

  2. jik Post author

    Note: I’ve just made two minor improvements to the shell script above; the new, improved version is the one currently embedded in the article.

    1) I’ve adjusted the logic so that if you plug in or unplug your YubiKey while the script is running but delaying its notification, then it’ll re-check the status of the YubiKey after the delay but before sending the notification, so that it won’t accidentally send the wrong notification.

    2) I’ve increased the minimum gap between the first and second (backup, redundant) notification from 15 seconds to 30 seconds, because it appears that sometimes 15 seconds isn’t enough for the notification to work its way through channels and make it to the phone.

    Reply
  3. Pingback: How I remember my YubiKey, take two – Something better to do

Leave a Reply

Your email address will not be published. Required fields are marked *