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 it 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 APM. 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 (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” and “iRule” 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.1.7KViews0likes4CommentsAPM Cookbook: Two-Factor Authentication using YubiKey OTP with iRulesLX.
Introduction It’s been a number of years since I penned my first DC article: Two-Factor Authentication using YubiKey, YubiCloud and APM. A lot has changed over the years, BIG-IP versions and features, new YubiKey models and the YubiCloud Validation API has changed significantly rendering my older article obsolete. This article is a rewrite of the original with a number of improvements, such as: No need for HTTP Auth agent No need to Reverse Proxy the HTTP connection to the YubiCloud API “yub” NPM package used with iRulesLX. This does all the hard work for us, such as signing the message, validating the response and decoding the YubiKey serial. HMAC-SHA1 signed message Signed response validation VPE improvements to protect AD Account Lockouts 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 – APM sends the YubiKey OTP to the YubiCloud validation service. If the YubiCloud API returns “Status=OK”, the signature and the nonce is verified, then we know the YubiKey OTP is valid. This is performed by the “yub” NPM package using iRulesLX. Step 4 & 5 – Check to make sure the user has been provisioned a YubiKey and the Serial number assigned to that user matches. I store the 8-digit YubiKey serial number to an Active Directory attribute: “employeeID”. Obviously you can use any attribute field you like or you can modify the policy to query a data group. Step 6 & 7 – The Username and Password is verified by Active Directory/LDAP or what ever is your preference. Step 8 - On success, grant the user access to the resource. An explanation of the validation protocol can be found here: https://developers.yubico.com/yubikey-val/Validation_Protocol_V2.0.html. The “yub” NPM module uses this API and simplifies the validation and signing process. Before we get started I have a pre-configured Active Directory 2012 R2 server which I will be using as my LDAP server with an IP address of 10.1.30.101. My BIG-IP is running TMOS 12.1.2 and the iRules Language eXtension has been licensed and provisioned. Make sure your BIG-IP has internet access to download the required Node.JS packages. This guide also assumes you have a basic level of understanding and troubleshooting at a Local Traffic Manager (LTM) level and your BIG-IP Self IP, VLANs, Routes, etc.. are all configured and working as expected. You have obtained a Client ID and API Key from: https://upgrade.yubico.com/getapikey/ to validate the YubiKey OTP. Step 1 – iRule and iRuleLX Configuration 1.1 Create a new iRulesLX workspace Local Traffic >> iRules >> LX Workspaces >> “Create” Supply the following: Name: yubikey_auth_workspace Select “Finished" to save. You will now have any empty workspace, ready to cut/paste the TCL iRule and Node.JS code. 1.2 Add the iRule Select “Add iRule” and supply the following: Name: yubikey_auth_apm_event_irulelx Select OK Cut / Paste the following iRule into the workspace editor on the right hand side. Select “Save File” to save. # Author: Brett Smith @f5 when RULE_INIT { # Debug logging control. # 0 = debug logging off, 1 = debug logging on. set static::yubikey_debug 0 } when ACCESS_POLICY_AGENT_EVENT { if { [ACCESS::policy agent_id] eq "yubikey_auth" } { # Get the YubiKey OTP from APM session data set yubiotp [ACCESS::session data get session.logon.last.yubiotp] if { $static::yubikey_debug == 1 }{ log local0. "YubiKey OTP: $yubiotp" } # Basic error handling - don't execute Node.JS if session.logon.last.yubiotp is null if { ([string trim $yubiotp] eq "") } { # The YubiKey OTP is not valid ACCESS::session data set session.yubikey.valid 0 if { $static::yubikey_debug == 1 }{ log local0. "YubiKey OTP is not valid!" } } else { # Initialise the iRulesLX extension set rpc_handle [ILX::init yubikey_auth_extension] # Need to change the default RPC timeout from 3 sec to 30 sec to # allow for the HTTPS request to the Yubico API set timeout 30000 # Pass the YubiKey OTP to Node.JS and save the iRulesLX response set rpc_response [ILX::call $rpc_handle -timeout $timeout yubikey_auth $yubiotp] if { $static::yubikey_debug == 1 }{ log local0. "rpc_response: $rpc_response" } # Loop through each key/value pair returned from "yub.verify" foreach {key value} $rpc_response { # Assign the key/value pair to an APM session variable so it # can be referenced in the Access Policy ACCESS::session data set session.yubikey.$key $value if { $static::yubikey_debug == 1 }{ log local0. "$key $value" } } } } } 1.3 Add the Extension Select “Add extenstion” and supply the following: Name: yubikey_auth_extension Select OK Cut / Paste the following Node.JS and replace the default index.js. Select “Save File” to save. Update the “client_id” and “secret_key” variables with your Yubico Client ID and API Key. // Author: Brett Smith @f5 // index.js for yubikey_auth_apm_event_lx // Includes var f5 = require('f5-nodejs'); var yub = require('yub'); // Create a new rpc server for listening to TCL iRule calls. var ilx = new f5.ILXServer(); // Start listening for ILX::call and ILX::notify events. ilx.listen(); // YubiKey Auth ilx.addMethod('yubikey_auth', function(yubiotp, response) { // Get a Yubico Client ID and API Key from here: https://upgrade.yubico.com/getapikey/ var client_id = 'XXXX'; var secret_key = 'XXXXXXXXXXXXXXX'; // Initialise the yub library yub.init(client_id, secret_key); // Attempt to verify the OTP yub.verify(yubiotp.params()[0], function(err,data) { if (err) { console.log('Error: YubiKey OTP Verify Failed!'); response.reply('valid 0'); } else { response.reply(data); } }); }); 1.4 Install the “yub” package SSH to the BIG-IP as root cd /var/ilx/workspaces/Common/yubikey_auth_workspace/extensions/yubikey_auth_extension npm install yub -save You should expect the following output from the above command: [root@big-ip1:Active:Standalone] ldap_modify_extension # npm install yub -save yub@0.11.1 node_modules/yub 1.5 Create a the iRulesLX plugin Local Traffic >> iRules >> LX Plugin >> “Create” Supply the following: Name: yubikey_auth_plugin From Workspace: yubikey_auth_workspace Select “Finished" to save. If you look in /var/log/ltm, you will see the extension start a process per TMM for the iRuleLX plugin. big-ip1 info sdmd[16339]: 018e000b:6: Extension /Common/yubikey_auth_plugin:yubikey_auth_extension started, pid:975 big-ip1 info sdmd[16339]: 018e000b:6: Extension /Common/yubikey_auth_plugin:yubikey_auth_extension started, pid:976 big-ip1 info sdmd[16339]: 018e000b:6: Extension /Common/yubikey_auth_plugin:yubikey_auth_extension started, pid:977 big-ip1 info sdmd[16339]: 018e000b:6: Extension /Common/yubikey_auth_plugin:yubikey_auth_extension started, pid:978 Step 2 – APM Configuration 2.1 Create a new Authentication Server or reuse an existing server 2.1.1 Access Policy >> AAA Servers >> Active Directory >> “Create” Supply the following: Name: f5.demo_ad_aaa (something sensible) Domain Name: f5.demo (Domain Name) Server Connection: Direct or Use Pool depending on your setup. Domain Controller: <FQDN> or (AD server) or leave blank and APM will use DNS. Admin Name and Password Select “Finished" to save. 2.2 Create an Access Profile and Policy 2.2.1 Access Policy >> Access Profiles >> Access Profiles List >> “Create” Supply the following: Name: yubikey_otp_2fa_iruleslx_ap Profile Type: All Profile Scope: Profile Languages: English (en) Use the default settings for all other settings. Select “Finished" to save. 2.2.2 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”. 2.2.3 On the “fallback” branch after the “Logon Page” object, add an “iRule Event” object. This step verifies the YubiKey OTP by passing “session.logon.last.yubiotp” from the ”Logon Page” to the iRuleLX created in Step 1. Supply the following Properties: Name: YubiKey Auth ID: yubikey_auth 2.2.3.1 Under “Branch Rules”, add a new one, by selecting “Add Branch Rule”. Update the Branch Rule settings: Name: YubiKey OTP Valid Expression (Advanced): expr { [mcget {session.yubikey.valid}] == "1" } Select “Finished”, then “Save” when your done. 2.2.4 On the “YubiKey OTP Valid” branch after the “YubiKey Auth” object, add an “AD Query” object. This step checks if the user has a YubiKey provisioned in their Active Directory account and the Serial number assigned to that user matches. 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 Serial Match Server: /Common/f5.demo_ad_aaa (select your AD Server) SearchFilter: sAMAccountName=%{session.logon.last.username} Required Attributes: employeeID 2.2.4.1 Under “Branch Rules”, delete the default and add a new one, by selecting “Add Branch Rule”. Update the Branch Rule settings: Name: Not Provisioned Expression (Advanced): expr { [mcget {session.ad.last.attr.employeeID}] == "" } Select “Finished. 2.2.4.2 Add another Branch Rule by selecting “Add Branch Rule”. Update the Branch Rule settings: Name: Match Found Expression (Advanced): expr { [mcget {session.yubikey.serial}] eq [string trim [mcget {session.ad.last.attr.employeeID}] 0] } Select “Finished”, then “Save” when your done. 2.2.5 On the “Match Found” branch after the “YubiKey Serial Match” 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/f5.demo_ad_aaa (select your AD Server) Leave the “Branch Rules” as the default. Select “Save” when your done. 2.2.6 On the “Successful” branch after the “AD Auth” object, change the branch end from “ Deny” to “Allow”. This competes the Access Policy. It should resemble something similar to this: Step 3 – Virtual Server Configuration Attach the Access Policy (yubikey_otp_2fa_iruleslx_apldap_modify_ap) to a HTTPS virtual server. Attach the iRuleLX (yubikey_auth_apm_event_irulelx) under the Resources section. 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.2.7KViews0likes6CommentsStrong Authentication
It’s authentication, people! Only, wait for it….STRONGER (yes i did use the strong html tag for that.) Strong authentication is a nebulous term. Many will interchange strong authentication with two-factor authentication (2FA), but that is only one method for strong authentication, not necessarily a requirement. You could have a series of “something you know” challenges that makes the authentication process more in depth and thus stronger, but doesn’t require one or more of the other multi-factor pillars in “something you have” or “something you are." So what strong authentication solutions does F5 support? The Access Policy Manager actually shines here, because it is vendor agnostic. You can plug and play a myriad of authentication systems into the process and off you go. Some of the different authentication methods include: LDAP/S HTTP/S RADIUS TACACS+ If your authentication services can talk one or more of these protocols, APM will support it. We’ve posted solutions on DevCentral with Google Authenticator (LTM via ldap and APM,)Yubico Yubikey(LTM and APM,) as well asRSA SecurIDover the years. There are some nuanced differences between all the solutions, but this diagram from Brett Smith’s APM and Yubikey article linked previously shows the general process for APM’s support of multi-factor authentication. This directory server could be Active Directory, LDAP, or RADIUS to a cloud service like Thales' (née SafeNet’s) Authentication Services as discussed in this solution brief. Beyond just authenticating local services, these strong authentication services can be utilized with federation as well, using APM’s SAML support for SP and iDP deployments. Thales (née SafeNet) has quality write-ups for their APM-integrated enterprise and cloudiDP strong authentication services available on their site. For more information about how these authentication services plug in to the larger SAML federation services, take a look at John’s lightboard Lesson on the topic. Additional Resources APM Authentication and Single Sign-On Agility 2015 APM 101 Supporting Infrastructure Lab Documents Thales Authentication808Views0likes0Comments