Two-Factor Authentication using YubiKey, YubiCloud and APM

 

Introduction

What is a YubiKey?

The YubiKey is an innovative USB-key that simplifies the process of logging in with strong two factor authentication. With a simple touch on the device, it generates a One-Time Password (OTP) on any computer and platform without any client software needed.

How the YubiKey works

By touching the integrated button, the YubiKey sends a time-variant, secure login code as if it was typed in from a keyboard. The unique passcode is verified by a YubiKey compliant web service or software application, in our case the BIG-IP Access Policy Manager.

For more details see: http://www.yubico.com/yubikey

What is YubiCloud?

The YubiCloud is a free cloud based YubiKey (OTP) validation service, for easy integration of strong two-factor authentication with your web site or online service. (i.e APM)

For more details see: http://www.yubico.com/yubicloud

 

YubiKey 2-Factor Authentication Process with APM

The authentication process can be broken down into a few simple steps which is illustrated below and explained in more detail.

 

Step 1 – The user is presented with a login page. The login page in my example asks for a Username, Password and YubiKey OTP. After entering your username and password, you simply plug in the YubiKey to the USB port and press the button. The YubiKey will generate the unique OTP followed by the enter key.

 

Step 2 & 3 – The Username and Password is verified by Active Directory. On success move to Step 4.

Step 4 & 5 – Check to make sure the user has been assigned a YubiKey. I store the 8-digit YubiKey serial number to an Active Directory attribute: “employeeID”. Obviously you can use any attribute field you like or a data group.

Step 6 – Make sure the OTP generated matches the YubiKey serial number for that user. YubiKey OTP uses Modified Hexadecimal output, known as MODHEX. The MODHEX alphabet is explained on the Yubico forum: http://forum.yubico.com/viewtopic.php?f=6&t=96. I created an iRule which takes a number (YubiKey Serial) and converts it to MODHEX and saves this to an APM session variable. Next compare the OTP value from step 1 to the MODHEX serial number, if the string exists, then we are good to go.

Step 7, 8 & 9 – Send the YubiKey OTP to the YubiCloud validation service. If the YubiCloud API returns “Status=OK” then we can allow the user to access to the resource.

An explanation of the validation protocol can be found here: http://code.google.com/p/yubikey-val-server-php/wiki/ValidationProtocolV20. In my example setup, I’m not signing or validating the response, this could be done with an iRule, something for my next post.

 

BIG-IP APM Configuration

The APM configuration is straight forward and assumes:

  • You have an existing directory server with users configured and you can add the YubiKey serial number to an LDAP/AD attribute.
  • Your YubiKey is registered with the YubiCloud service.
  • You have obtained an Auth ID and API key from: https://upgrade.yubico.com/getapikey/ to validate the YubiKey.
  • You have use a BIG-IP before and have suitable LTM knowledge.

This configuration was created using BIG-IP v11.2.0. Lets get into the APM configuration.

Authentication Servers

Active Directory

Access Policy >> AAA Servers >> Active Directory >> “Create”

Supply the following:

  • Name: dc1.test.lab (something sensible)
  • Domain Name: test.lab (Domain Name)
  • Domain Controller: <FQDN> or <IP Address> (AD server)
  • Admin Name and Password

Select “Finished" to save.

YubiCloud API

The HTTP AAA Server object doesn’t support HTTPS connections, so we need to create a Virtual Server to proxy the traffic from the BIG-IP to the YubiCloud API. The Virtual Server will listen on HTTP, but we will apply a Server SSL profile creating a HTTP –> HTTPS proxy.

Local Traffic >> Nodes >> Node List >> “Create”

Supply the following:

  • Name: api.yubico.com
  • Address: api.yubico.com
  • Health Monitor

Select “Finished" to save.

Local Traffic >> Pools >> Pool List >> “Create”

Supply the following:

  • Name: pool_yubicloud
  • Description: YubiCloud Proxy Pool (optional)
  • Health Monitor (optional)
  • Address: api.yubico.com
  • Service Port: HTTPS

Select “Finished" to save.

 

Local Traffic >> Virtual Servers >> Virtual Server List >> “Create”

Supply the following:

  • Name: vs_yubicloud
  • Description: YubiCloud Proxy VS (optional)
  • Destination: <IP Address>
  • Service Port: HTTP
  • HTTP Profile: http
  • SSL Profile (Server): clientssl
  • SNAT Pool: Auto Map
  • Default Pool: pool_yubicloud

Select “Finished" to save.

Access Policy >> AAA Servers >> HTTP

Supply the following:

  • Name: aaa_yubicloud
  • Form Method: GET
  • Form Action: http://<IP_address_of_vs_yubicloud>/wsapi/verify
  • Form Parameter For User Name: id
  • Form Parameter For Password: otp
  • Successful Logon Detection Match Type: By Specific String in Response
  • Successful Logon Detection Match Value: status=OK

The Form Action uses the Virtual Server IP configured above instead of the YubiCloud server FQDN. You need to supply at least the id and otp parameters for an unsigned response. The id parameter is your Auth ID for the YubiCloud service and otp is the token code generated by the YubiKey.

Select “Finished" to save.

 

 

 

MODHEX Encode iRule

Local Traffic >> iRules >> iRules List >> “Create”

Copy and paste the following iRule which will convert the YubiKey serial number to Modified Hexadecimal (MODHEX) format. In my example, I’m retrieving the YubiKey serial associated with the user from Active Directory using the “employeeID” attribute. This iRule will need to be modified if the serial number has been stored in alternate source, such as a data group or a custom LDAP/AD attribute.

when ACCESS_POLICY_AGENT_EVENT {

    if { [ACCESS::policy agent_id] eq "irule_apm_yubikey_modhex_encode" } {

        # yubikey serial number
        set yubikey_serial [ACCESS::session data get session.ad.last.attr.employeeID]

        # modhex alphabet
        array set modhex_alphabet { 0 c 1 b 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k A l B n C r D t E u F v }

        # remove leading zeros from serial number
        set yubikey_serial [string trimleft $yubikey_serial 0]

        # convert serial to hex and pad with zeros
        set yubikey_serial [format %012X $yubikey_serial]

        # split the string
        set yubikey_serial [split $yubikey_serial ""]

        # for each HEX change to MODHEX using alphabet
        set yubikey_modhex ""
        foreach index $yubikey_serial {
            append yubikey_modhex $modhex_alphabet($index)
        }

        ACCESS::session data set session.custom.yubikey.modhex $yubikey_modhex
    }

}

Access Profile  & Policy

Access Policy >> Access Profiles >> Access Profiles List >> “Create”

Supply the following:

  • Name: profile_yubicloud
  • Language: English (en)
  • Use the default settings for all other settings.

Select “Finished" to save.

 

 

 

Access Policy >> Access Profiles >> Access Profiles List >> “Edit”

On the “fallback” branch after the “Start” object, add a “Logon Page” object.

Add a third field:

  • Type: text
  • Post Variable Name: yubiotp
  • Session Variable Name: yubiotp
  • Read Only: No

In the “Customization” section further down the page, set the “Form Header Text” to what ever you like and change “Logon Page Input Field #3” to something meaningful, see my example below for inspiration. Leave the “Branch Rules” as the default. Don’t forget to “Save”.

On the “fallback” branch after the “Logon Page” object, add an “AD Auth” object.

This step verifies the username and password is correct against Active Directory.

Supply the following Properties:

  • Name: AD Auth
  • AAA Server: /Common/dc1.test.lab (select your AD Server)

Leave the “Branch Rules” as the default. Select “Save” when your done.

 

On the “Successful” branch after the “AD Auth” object, add an “AD Query” object.

This step checks if the user has a YubiKey provisioned in their Active Directory account. I’ve added the serial number of the YubiKey to the “employeeID” attribute in Active Directory for each user. I used the “employeeID” attribute for simplicity, but I would recommend creating a custom AD attribute for the YubiKey serial number.

Supply the following Properties:

  • Name: YubiKey Provisioned
  • SearchFilter: sAMAccountName=%{session.logon.last.username}

Under “Branch Rules”, delete the default and add a new one, by selecting “Add Branch Rule”.

Update the Branch Rule settings:

Name: Yes

Expression (Advanced): expr { [mcget {session.ad.last.attr.employeeID}] != "" }

Select “Finished”, then “Save” when your done.

On the “Yes” branch after the “YubiKey Provisioned” object, add an “iRule Event” object.

This step passes “session.ad.last.attr.employeeID” (YubiKey serial) from the successful AD Query to the iRule called “irule_apm_yubikey_modhex_encode”. The iRule returns a Modified Hexadecimal (MODHEX) encoded string and stores this to “session.custom.yubikey.modhex”. We need the YubiKey serial in the MODHEX format so we can compare this to the original YubiKey OTP entered on the Logon Page.  To learn more about how/why Yubico uses this format, visit the Yubico Forum: http://forum.yubico.com/viewtopic.php?f=6&t=96

Supply the following Properties:

  • Name: YubiKey MODHEX Encode
  • ID: irule_apm_yubikey_modhex_encode

Now we compare the MODHEX formatted YubiKey serial to the YubiKey OTP entered on the Logon Page. This step verifies the user is using their YubiKey and not someone else's. The first 12 characters of the YubiKey OTP is the serial of that YubiKey, this makes is very easy to verify using the “string first” TCL command.

Under “Branch Rules” add a new one, by selecting “Add Branch Rule”.

Update the Branch Rule settings:

  • Name: YubiKey Serial Match
  • Expression (Advanced): expr { [string first [mcget {session.custom.yubikey.modhex}] [mcget {session.logon.last.yubiotp}]] == 0 }

Select “Finished”, then “Save” when your done.

On the “YubiKey Serial Match” branch after the “YubiKey MODHEX Encode” object, add a “Variable Assign” object.

This step assigns your YubiCloud Auth ID to to “session.logon.last.username” and the YubiKey OTP entered on the Logon Page to “session.logon.last.password”. These session variables are then assigned to the id and otp HTTP parameters used in the HTTP Auth AAA object “aaa_yubicloud” created earlier. If you don’t quite follow how this works, just believe me and just do it!

Supply the following Properties:

  • Name: Assign YubiCloud Variables

Add the Variable assignments by selecting “Add new entry” >> “change”.

Variable Assign 1:

  • Custom Variable (Unsecure): session.logon.last.username
  • Custom Expression: expr {"1111"} (change “1111” to your YubiCloud Auth ID)

Variable Assign 2:

  • Custom Variable (Secure): session.logon.last.password
  • Custom Expression: expr { [mcget {session.logon.last.yubiotp}] }

Select “Finished”, then “Save” when your done.Leave the “Branch Rules” as the default.

On the “fallback” branch after the “Assign YubiCloud Variables” object, add a “HTTP Auth” object.

This step sends a HTTP Get to the YubiCloud API (https://api.yubico.com/wsapi/verify) to verify the YubiKey OTP entered on the Logon Page.

Supply the following Properties:

  • Name: YubiCloud Auth
  • AAA Server: /Common/aaa_yubicloud

Leave the “Branch Rules” as the default. Select “Save” when you’re done.

This competes the Access Policy. It should resemble something similar to this:

 

 

Next attached the “Access Profile” to a Virtual Server to test.

 

Conclusion

This is another great example how you can easily add a 2nd factor of authentication to any application using the power of Access Policy Manager (APM). F5 provides a 10 Concurrent User trial version of APM with every BIG-IP licensed with LTM. APM is one of my favourite pieces of technology, it amazes me every day what I can create with this flexible tool. Why not give it a try today.

Published Dec 04, 2012
Version 1.0
  • Smithy's avatar
    Smithy
    Icon for Cirrostratus rankCirrostratus
    Don't forget to apply the iRule to the virtual server