Two-Factor Authentication – Remote Desktop Gateway

Technical Challenge

Recently I touched on a problem that F5 IT was facing with Two-Factor Authentication (TFA) and VPN clients that didn’t support it (Two-Factor Authentication - Captive Portal). I figured this would be a great opportunity to talk about another interesting challenge IT is experiencing, end users have expressed a desire to access internal resources without the need to establish a Full-VPN tunnel.

This problem could be solved with BIG-IP APM using either Portal Access or an App Tunnel but I wanted to approach it from a different angle. Reading through the release notes for 11.6 I noticed a feature that would enable remote employees to connect directly to either a workstation or our internal Remote Desktop Services (RDS) cluster.

Native MSRDP Support - This feature provides native support for Microsoft RDP clients to connect to backed resources without establishing a VPN Tunnel. In other words the BIG-IP APM is now able to act as an Remote Desktop Gateway.

Okay here comes the curve ball, to keep the security-team happy the only way they would bless this service is if it required TFA. No big deal right, TFA has been around since 1984……

Looking at the Remote Desktop Connection application answered my TFA question real quick.

The supported login methods are NTLM or Smart Cards (Oh Joy, my liquor cabinet is going to need a refill after this one). So how do you add TFA to NTLM?

The Solution

The answer is YOU DON’T, my initial thought process was maybe some type of Password+Token concatenation solution but then I remembered how NTLM worked. And at that point I realized that if the employee concatenated their password and token it would break the NTLM Authentication process.

  1. (Interactive authentication only) A user accesses a client computer and provides a domain name, user name, and password. The client computes a cryptographic hash of the password and discards the actual password.
  2. The client sends the user name to the server (in plaintext).
  3. The server generates a 16-byte random number, called a challenge or nonce, and sends it to the client.
  4. The client encrypts this challenge with the hash of the user's password and returns the result to the server. This is called the response.
  5. The server sends the following three items to the domain controller:
  6. The domain controller uses the user name to retrieve the hash of the user's password from the Security Account Manager database. It uses this password hash to encrypt the challenge.
  7. The domain controller compares the encrypted challenge it computed (in step 6) to the response computed by the client (in step 4). If they are identical, authentication is successful.

So now that we have determined that modifying the credentials on the client is a non-starter what’s left? Well what if the employee validated their Username, Password & TFA Token against a website and that session was used to satisfy the TFA requirement for RDG?

That just might work, here is what it would look like.



Putting Everything Together


Step 1 – Update the “rdg-apm-access” Access Policy

At this point you should have a VPE policy that looks similar to the image below

Now we need to add a second logic branch to the fallback section of the policy to handle web based authentication for employees. This branch will validate their TFA token.

Step 1.1 – Adding a Logon Page

Under the fallback option for “Client Type” resource and a new “Logon Page” resource and configure field 3 to collect your OTP information

*NOTE – I configured field 3 to store the entered value as otp, this id will be required later when we validate the provided token against the radius server

Step 1.2 – Validating the Username & Password

Next add the AAA resource that will handle Username and Password validation.

Step 1.3 – Validating the TFA Token

After the Username and Password are validated you will need to update the password session variable so that it contains the OTP value. The Radius AAA resource will expect the OTP value to be stored in the password variable

Next add the AAA resource that will handle OTP validation and update the successful result to Allow.

*NOTE – You can add a second Logon Page and Radius Auth to the fallback path of the “Radius Auth” resource to allow employees to retry just their OTP token on Radius Failure.

*NOTE – To keep the TFA session active you should add an Advanced Resource Assign object to the successful branch of the Radius Auth path. The advanced resource object should contain a Webtop object and one other resource (a webtop link would work). This way as long as the TFA webtop is open the session will remain active, communicate to your employees that in order to use the RDG feature the TFA webtop must be open. You could use a custom session table to store this information but I wanted to keep this section simple. For more information on session tables take a look at => https://devcentral.f5.com/s/wiki/iRules.session.ashx

Step 2 – Two-Factor Validation for RDG

This iRule should be applied to the virtual that employees will use as their Remote Desktop Gateway

when ACCESS_POLICY_COMPLETED {
## Checks to see if the employee has succesfully authenticated to the RDG using NTLM
if {[ACCESS::policy result] eq "allow"}
{

## Stores the username associated to this session as a variable
set s_username [ACCESS::session data get session.logon.last.username]
## Stores the Access Profile name as a variable
set s_profile [PROFILE::access name]

## Enumerate all Session ID's active on this profile for the provided username
set a_uuid [ACCESS::uuid getsid "$s_profile.$s_username"]

## Set the variable that will be used to identify completed TFA to fail
set b_tfa 0

## Loop through each Session ID to see if it contains a valid TFA
foreach s_uuid $a_uuid {

## Store the Client IP associated to the current loop as a variable
set s_clientip [ACCESS::session data get -sid $s_uuid session.user.clientip]

## Store the Radius Result associated to the current loop as a variable
set b_radius [ACCESS::session data get -sid $s_uuid session.radius.last.result]

## Check to see if the Client-IP associated to the session UUID matches the current request ip
## Check to see if the session has passed Radius Authentication
if {$s_clientip eq [IP::client_addr] && $b_radius}
{
## Since we have found an active session that has completed TFA
## we need to update the variable created earlier
set b_tfa 1

## Exit the ForEach Loop
break
}
}

## Check to see if the employee has passed TFA
if {!($b_tfa)}
{
## If the employee has not passed TFA prevent them from connecting to the RDG and return a 500 error message
## Note the RDG failure may not be obvious to the employee
ACCESS::respond 500 content "Please complete Two-Factor Authentication prior to establishing an RDG Connection"
}
}
}
Published Oct 09, 2015
Version 1.0

Was this article helpful?

1 Comment

  • Robert, Great article on how to get 2FA working on RDP however I would like to know when F5 are looking to support Pluggable Authentication for RDS which allows custom access using Tokens and signed RDP files from a webtop type environment. I raised this question on here a few months back https://devcentral.f5.com/s/feed/0D51T00006i7bNGSAY and would like to know if this is something that maybe coming up in the future at all?