03-Mar-2021 08:35 - edited 08-Feb-2023 14:40
BIG-IP APM has supported Duo as an MFA provider for a long time with RADIUS-based integration. Recently, Duo has added support for Universal Prompt that uses Open ID Connect (OIDC) protocol to provide two-factor authentication. To integrate APM as an OIDC client and resource server, and Duo as an Identity Provider (IdP), Duo requires the user’s logon name and custom parameters to be sent for Authentication and Token request.
This guide describes the configuration required on APM to enable Duo MFA integration using an iRule. iRules addresses the custom parameter challenges by generating the needed custom values and saving them in session variables, which the OAuth Client agent then uses to perform MFA with Duo. This integration procedure is supported on BIG-IP versions 13.1, 14.1x, 15.1x, and 16.x.
To integrate Duo MFA with APM, complete the following tasks:
1. Choose deployment type: Per-request or Per-session
2. Configure credentials and policies for MFA on the DUO web portal
3. Create OAuth objects on the BIG-IP system
4. Configure the iRule
5. Create the appropriate access policy/policies on the BIG-IP system
6. Apply policy/policies and iRule to the APM virtual server
APM supports two different types of policies for performing authentication functions.
This guide contains information about setting up both policy types.
Ensure the BIG-IP system has DNS and internet connectivity to contact Duo directly for validating the user's OAuth tokens.
Before you can protect your F5 BIG-IP APM Web application with Duo, you will first need to sign up for a Duo account.
1. Log in to the Duo Admin Panel and navigate to Applications.
2. Click Protect an application.
Figure 1: Duo Admin Panel – Protect an Application
3. Locate the entry for F5 BIG-IP APM Web in the applications list and click Protect to get the Client ID, Client secret, and API hostname. You will need this information to configure objects on APM.
Figure 2: Duo Admin Panel – F5 BIG-IP APM Web
4. As DUO is used as a secondary authentication factor, the user’s logon name is sent along with the authentication request. Depending on your security policy, you may want to pre-provision users in Duo, or you may allow them to self-provision to set their preferred authentication type when they first log on.
To add users to the Duo system, navigate to the Dashboard page and click the Add New... -> Add User button. A Duo username should match the user's primary authentication username. Refer to the https://duo.com/docs/enrolling-users link for the different methods of user enrollment.
Refer to Duo Universal Prompt for additional information on Duo’s two-factor authentication.
When APM is configured to act as an OAuth client or resource server, it uses JSON web keys (JWKs) to validate the JSON web tokens it receives from Duo.
To create a JSON web key:
1. On the Main tab, select Access > Federation > JSON Web Token > Key Configuration.
The Key Configuration screen opens.
2. To add a new key configuration, click Create.
3. In the ID and Shared Secret fields, enter the Client ID and Client Secret values respectively obtained from Duo when protecting the application.
4. In the Type list, select the cryptographic algorithm used to sign the JSON web key.
Figure 3: Key Configuration screen
5. Click Save.
As an OAuth client or resource server, APM validates the JSON web tokens (JWT) it receives from Duo.
To create a JSON web token:
1. On the Main tab, select Access > Federation > JSON Web Token > Token Configuration.
The Token Configuration screen opens.
2. To add a new token configuration, click Create.
3. In the Issuer field, enter the API hostname value obtained from Duo when protecting the application.
4. In the Signing Algorithms area, select from the Available list and populate the Allowed and Blocked lists.
5. In the Keys (JWK) area, select the previously configured JSON web key in the allowed list of keys.
Figure 4: Token Configuration screen
6. Click Save.
APM uses the OAuth provider settings to get URIs on the external OAuth authorization server for JWT web tokens.
To configure an OAuth provider:
1. On the Main tab, select Access > Federation > OAuth Client / Resource Server > Provider.
The Provider screen opens.
2. To add a provider, click Create.
3. In the Name field, type a name for the provider.
4. From the Type list, select Custom.
5. For Token Configuration (JWT), select a configuration from the list.
6. In the Authentication URI field, type the URI on the provider where APM should redirect the user for authentication. The hostname is the same as the API hostname in the Duo application.
7. In the Token URI field, type the URI on the provider where APM can get a token. The hostname is the same as the API hostname in the Duo application.
Figure 5: OAuth Provider screen
8. Click Finished.
The OAuth Server settings specify the OAuth provider and role that Access Policy Manager (APM) plays with that provider. It also sets the Client ID, Client Secret, and Client’s SSL certificates that APM uses to communicate with the provider.
To configure a Duo server:
1. On the Main tab, select Access > Federation > OAuth Client / Resource Server > OAuth Server.
The OAuth Server screen opens.
2. To add a server, click Create.
3. In the Name field, type a name for the Duo server.
4. From the Mode list, select how you want the APM to be configured.
5. From the Type list, select Custom.
6. From the OAuth Provider list, select the Duo provider.
7. From the DNS Resolver list, select a DNS resolver (or click the plus (+) icon, create a DNS resolver, and then select it).
8. In the Token Validation Interval field, type a number.
In a per-request policy subroutine configured to validate the token, the subroutine repeats at this interval or the expiry time of the access token, whichever is shorter.
9. In the Client Settings area, paste the Client ID and Client secret you obtained from Duo when protecting the application.
10. From the Client's ServerSSL Profile Name, select a server SSL profile.
Figure 6: OAuth Server screen
11. Click Finished.
Requests specify the HTTP method, parameters, and headers to use for the specific type of request. An auth-redirect-request tells Duo where to redirect the end-user, and a token-request accesses the authorization server for obtaining an access token.
To configure an auth-redirect-request:
1. On the Main tab, select Access > Federation > OAuth Client / Resource Server > Request.
The Request screen opens.
2. To add a request, click Create.
3. In the Name field, type a name for the request.
4. For the HTTP Method, select GET.
5. For the Type, select auth-redirect-request.
6. As shown in Figure 7, specify the list of GET parameters to be sent:
Figure 7: Request screen with auth-redirect-request (Use “subsession.custom…” for Per-request or “session.custom…” for Per-session)
7. Click Finished.
1. On the Main tab, select Access > Federation > OAuth Client / Resource Server > Request.
The Request screen opens.
2. To add a request, click Create.
3. In the Name field, type a name for the request.
4. For the HTTP Method, select POST.
5. For the Type, select token-request.
6. As shown in Figure 8, specify the list of POST parameters to be sent:
Figure 8: Request screen with token-request (Use “subsession.custom…” for Per-request or “session.custom…” for Per-session)
7. Click Finished.
iRules gives you the ability to customize and manage your network traffic. Configure an iRule that creates the required sub-session variables and usernames for Duo integration.
Note: This iRule has sections for both per-request and per-session policies and can be used for either type of deployment.
To configure an iRule:
1. On the Main tab, click Local Traffic > iRules.
2. To create an iRules, click Create.
3. In the Name field, type a name for the iRule.
4. Copy the sample code given below and paste it in the Definition field.
Replace the following variables with values specific to the Duo application:
Note: The iRule ID here is set as JWT_CREATE. You can rename the ID as desired. You specify this ID in the iRule Event agent in Visual Policy Editor.
Note: The variables used in the below example are global, which may affect your performance. Refer to the K95240202: Understanding iRule variable scope article for further information on global variables, and determine if you use a local variable for your implementation.
proc randAZazStr {len} { return [subst [string repeat {[format %c [expr {int(rand() * 26) + (rand() > .5 ? 97 : 65)}]]} $len]] } proc getClientId { return <Duo Client ID> } proc getExpiryTime { set exp [clock seconds] set exp [expr $exp + 900] return $exp } proc getJwtHeader { return "{\"alg\":\"HS512\",\"typ\":\"JWT\"}" } proc getJwkName { return <JSON Web Key> #e.g. return "/Common/duo_jwk" } proc createJwt {duo_uname} { set header [call getJwtHeader] set exp [call getExpiryTime] set client_id [call getClientId] set redirect_uri "https://" set redirect [ACCESS::session data get "session.server.network.name"] append redirect_uri $redirect append redirect_uri "/oauth/client/redirect" set payload "{\"response_type\": \"code\",\"scope\":\"openid\",\"exp\":${exp},\"client_id\":\"${client_id}\",\"redirect_uri\":\"${redirect_uri}\",\"duo_uname\":\"${duo_uname}\"}" set jwt_duo [ ACCESS::oauth sign -header $header -payload $payload -alg HS512 -key [call getJwkName] ] return $jwt_duo } proc createJwtToken { set header [call getJwtHeader] set exp [call getExpiryTime] set client_id [call getClientId] set aud "<Duo API Hostname>/oauth/v1/token" #Example: set aud https://api-duohostname.com/oauth/v1/token set jti [call randAZazStr 32] set payload "{\"sub\": \"${client_id}\",\"iss\":\"${client_id}\",\"aud\":\"${aud}\",\"exp\":${exp},\"jti\":\"${jti}\"}" set jwt_duo [ ACCESS::oauth sign -header $header -payload $payload -alg HS512 -key [call getJwkName] ] return $jwt_duo } when ACCESS_POLICY_AGENT_EVENT { set irname [ACCESS::policy agent_id] if { $irname eq "JWT_CREATE" } { set ::duo_uname [ACCESS::session data get "session.logon.last.username"] ACCESS::session data set session.custom.jwt_duo [call createJwt $::duo_uname] ACCESS::session data set session.custom.jwt_duo_token [call createJwtToken] } } when ACCESS_PER_REQUEST_AGENT_EVENT { set irname [ACCESS::perflow get perflow.irule_agent_id] if { $irname eq "JWT_CREATE" } { set ::duo_uname [ACCESS::session data get "session.logon.last.username"] ACCESS::perflow set perflow.custom [call createJwt $::duo_uname] ACCESS::perflow set perflow.scratchpad [call createJwtToken] } }
Figure 9: iRule screen
5. Click Finished.
Skip this section for a per-session type deployment
The per-request policy is used to perform secondary authentication with Duo. Configure the access policies through the access menu, using the Visual Policy Editor. The per-request access policy must have a subroutine with an iRule Event, Variable Assign, and an OAuth Client agent that requests authorization and tokens from an OAuth server. You may use other per-request policy items such as URL branching or Client Type to call Duo only for certain target URIs.
Figure 10 shows a subroutine named duosubroutine in the per-request policy that handles Duo MFA authentication.
Figure 10: Per-request policy in Visual Policy Editor
The iRule Event agent specifies the iRule ID to be executed for Duo integration. In the ID field, type the iRule ID as configured in the iRule.
Figure 11: iRule Event agent in Visual Policy Editor
The Variable Assign agent specifies the variables for token and redirect requests and assigns a value for Duo MFA in a subroutine. This is required only for per-request type deployment. Add sub-session variables as custom variables and assign their custom Tcl expressions as shown in Figure 12.
Figure 12: Variable Assign agent in Visual Policy Editor
An OAuth Client agent requests authorization and tokens from the Duo server. Specify OAuth parameters as shown in Figure 13.
Figure 13: OAuth Client agent in Visual Policy Editor
Configure the Per Session policy as appropriate for your chosen deployment type.
Figures 14a and 14b show a per-session policy that runs when a client initiates a session. Depending on the actions you include in the access policy, it can authenticate the user and perform actions that populate session variables with data for use throughout the session.
Figure 14a: Per-session policy in Visual Policy Editor performs both primary authentication and Duo authentication (for per-session use case)
Figure 14b: Per-session policy in Visual Policy Editor performs primary authentication only (for per-request use case)
Finally, apply the per-request policy, per-session policy, and iRule to the APM virtual server. You assign iRules as a resource to the virtual server that users connect. Configure the virtual server’s default pool to the protected local web resource.
Per-request policy
To attach policies to the virtual server:
1. On the Main tab, click Local Traffic > Virtual Servers.
2. Select the Virtual Server.
3. In the Access Policy section, select the policy you created.
4. Click Finished.
Figure 15: Access Policy section in Virtual Server (per-request policy)
Per-session policy
Figure 16 shows the Access Policy section in Virtual Server when the per-session policy is deployed.
Figure 16: Access Policy section in Virtual Server (per-session policy)
To attach the iRule to the virtual server:
1. On the Main tab, click Local Traffic > Virtual Servers.
2. Select the Virtual Server.
3. Select the Resources tab.
4. Click Manage in the iRules section.
5. Select an iRule from the Available list and add it to the Enabled list.
6. Click Finished.
Hi -
I have this setup on our F5 APM and using it for a MS Sharepoint Website. I've configured it as per-request policy and I can authenticate via DUO MFA using the Oauth Client in the policy and it gets me into the site. The problem I am encountering comes when an authenticated user tries to open a document in the "native", local Office application (Word, Excel Powerpoint). It doesn't work for that use case, Do you have any ideas on that?
We have been using the F5 APM DUO RADIUS integration and we have the same issue with that. That is why I am trying this new Oauth/iRule integration that you have published here.
By the way. Nice job on this config and write up!
Thanks
-Steve
Hi ,
Thank you for your feedback!
You can refer to this article https://support.f5.com/csp/article/K94430306 that lists two options for getting this to work.
Let me know if it helps.
Thanks,
Hardeep
Hi-
I tried the option 1 in the article to add a MSOFBA client type check in the APM Access Policy. When I try the "open in word" option in SharePoint it still takes me back to the F5 APM login page. When I login again, and pass MFA with DUO prompt, it repeats the process again and again and never opens the word document in Word.
Thanks
Steve
Also we are not using IE browser so i cannot use Option 2. I am testing with Chrome and Edge on MacOS.
-Steve
Hi ,
I suggest creating a support ticket to resolve your issue.
https://support.f5.com/csp/article/K2633
Thanks,
Hardeep
Hi- I just wanted to mention that I have this working for F5 APM /DUO for auth/access to SharePoint on Windows Clients for opening documents in "native", local Microsoft Applications (Word/PP/Excel). The use case for MAC users trying to open in native applications still will not work. As a workaround, MAC users will have to use a Windows client to get that functionality. The bottom line is that this Oauth/OIDC method that you have developed here improves the user experience for our SharePoint users. Thanks. - Steve
Hi
I am trying to set this up for a webtop. I am on version 16.0.11 Build 0.9.6. I authenticate using LDAP first then use the irule event and oauth client before assigning the resources. After successfully authenticating to LDAP, I get redirected to the Duo portal with the error " {"error": "invalid_client", "error_description": "The supplied client_assertion is not a valid JWT"} " I have double-checked my configuration and looks ok. so not sure what I am missing. can you help?
Thanks
Varun
Hi ,
Are you using an IP address or host name? If IP address is used it does not work as Duo rejects it.
Hi ,
Thanks for your response. My requirement is to have this working on an internal as well as an internet-facing web page. Here I am testing this on a lab, where the VIP is a private IP and I have a host file entry to the VIP and I am accessing the VIP using the hostname. will this work of should I have a website whose hostname is resolvable on the internet?
Hi ,
The errors you see is from Duo; we would need to see their config and logs to find the reason of error. I suggest creating a support ticket to resolve your issue.
https://support.f5.com/csp/article/K2633
Thanks,
Hardeep
Hi
Found the issue,
under "Configure an auth-redirect-request and a token-request" section,
For per-session policy: %[session.custom.jwt_duo] needs to be replaced with %{session.custom.jwt_duo}
The brackets were incorrect, this document needs to be updated.
Wanted to add that I found during my testing if the floating self IP cannot make a connection to the Duo cloud on port 443, the OAuth connection will fail with an error message like the following:
/Common/DuoOAuthAccessPolicy:Common:xxxxxxxx:/Common/duo_web_act_oauth_client_ag: OAuth Client: failed for server '/Common/Duo-Oauth-Server' using 'authorization_code' grant type (client_id=ABCDEF01234568790), error: HTTP error 503, Connect failed
Otherwise, seems to work really well!
Has anyone else had problems with the iPhone F5 Access client after switching over? We are seeing the following message when the app tries to connect to Duo:
403 ERROR
The request could not be satisfied.
This distribution is not configured to allow the HTTP request method that was used for this request. The distribution supports only cachable requests. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
The URL is https://duo.com/my.policy.
The android app doesn't have this problem.
Can you elaborate on the use case to understand this issue in a better way? it will help our engineers to reproduce this issue locally and also, good to have if any supporting F5Acces logs collected from the iOS device where we see this issue.
You can email me directly p.campbell@f5.com. Thanks
Is there a specific reason to use global variables to store the Duo username? I see that attaching the iRule demotes the virtual from CMP.
Hi is there a way to troubleshoot this instead of just debugging the session it the APM level?
thanks
Manuel
A follow up on my issue with the iPhone F5 Access app not handling the redirect properly. It turns out you need to include the redirect_uri parameter in the auth-redirect-request.
Several tools that I found helpful were browser developer tools as well as https://jwt.io/ for decoding the JWT tokens to ensure their data was correct. Other tools include dig (to verify good DNS lookups to get the Duo address), curl (to verify connectivity), and tcpdump (to verify connectivity). One way to validate connection would be to build a simple vip and pool with the Duo server in the pool, then see if you can make connections to that VIP. I would probably keep a basic pool with a health check handy to be able to log if there is a connection issue to the Duo cloud.
I know this is an old post but was curious if anyone has run into this issue. My APM policy was failing at the OAuth branch rule expression. To fix it I had to change the OAuth branch rule
Expression: expr {[mcget {session.oauth.client.last.authresult}] == 1} <-- Changed to 0
Is this a valid fix or im I bypassing any security controls by chaging this branch Expression
Thank You
Enzo
Hi All,
After completing Primary Authentication, I got this error
{"error": "invalid_request", "error_description": "request: ['Length must be between 1 and 4096.'], "}
Please tell me why and how to resolve it!
Thanks!
When I first set it up I used my own naming convention . So I started over following the steps and naming convention above to configure a Per-session policy since the iRule references the names used in the config. I also found my DNS Resolver was not setup correctly so that might be something to check as well
I was with @JoshBecigneul in wondering if we could change the iRule so that it didn't demote the VS from CMP.
I came up with the following change to the last two sections:
when ACCESS_POLICY_AGENT_EVENT {
set irname [ACCESS::policy agent_id]
if { $irname eq "JWT_CREATE" } {
table set duo_uname [ACCESS::session data get "session.logon.last.username"] 604800
ACCESS::session data set session.custom.jwt_duo [call createJwt [table lookup duo_uname] ]
ACCESS::session data set session.custom.jwt_duo_token [call createJwtToken]
}
}
when ACCESS_PER_REQUEST_AGENT_EVENT {
set irname [ACCESS::perflow get perflow.irule_agent_id]
if { $irname eq "JWT_CREATE" } {
table set duo_uname [ACCESS::session data get "session.logon.last.username"] 604800
ACCESS::perflow set perflow.custom [call createJwt [table lookup duo_uname] ]
ACCESS::perflow set perflow.scratchpad [call createJwtToken]
}
}
It seems to be working as expected for me and I no longer receive the CMP warnings. I have not done a lot of work with the table command or global tables in iRules so if someone with more knowledge sees a flaw in this code please let me know.
I did not want the records left in the table forever so I chose a timeout (604800) that works for me and my usual session lengths.
Thanks
Scott
Hi @ScottE,
I found that simply switching the variables from global to local seemed to work. So far I have only tested this integration with the per-session APM setup and not per-flow, so I'm not 100% clear if there could be a limitation there.
Thanks,
Josh
Hi! I need help on my issue, after doing the Primary Authentication, I got this error
{"error": "invalid_request", "error_description": "request: ['Length must be between 1 and 4096.'], "}
Hope you have answers!
Do you have APM error logging enabled while testing? If so, can you see your ldapsearch in the logs?
Go to
Access ›› Overview : Event Logs : Settings.
Clcik "Create" for new log profile or click the check box next to "default-log-setting" and click "Edit"
In the "Access Profiles" section use the arrow to add the policy your troubleshooting to the "Selected" field
Login to the VIP associated with your DUO access profile
Go to "Access ›› Overview : Active Sessions"
Find your login and click the session ID
Hope this helps
{"error": "invalid_request", "error_description": "request: ['Length must be between 1 and 4096.'], "}
When I've seen this error it's typically because I've forgotten to do this final step:
Have spent way too much time troubleshooting when it's a 1-minute fix to enable the iRule.
Perhaps someone can figure out where this is failing.
I've gone through this setup on several of our F5s and even have automation developed to do most of it.
Depsite this, one of the F5s seems to be failing to resolve its own URL:
https://{ VIP URL }/oauth/client/redirect
I'm getting this error message in the Access log:
Agent_Type=Client;
OAuth_Config_Type=server;
OAuth_Config_Object=/{ partition }/duo_server;
Grant_Type_Msg= using 'authorization_code' grant type;
Credential_Type=(client_id=;Credential_ID={ Duo application ID });
Error_Message=HTTP error 503, DNS lookup failed;"
This is occurring when the Duo site is attempting to redirect a user back to the F5 device after 2FA succeeds.
The policy fails out after a few seconds since the page could not be loaded.
The DNS resolver setting is identical across all our F5s.
All of the configurations look identical.
Thanks in advance.
Hi @JacobV
Please double check that you have a DNS resolver profile set in the "Network" section, and with the Forward Zone ( either a dot "." or the domain name of your Duo server) pointed to some DNS resolver.
Thanks, @JoshBecigneul
Bringing up the forward zone helped me find the immediate issue.
It seems that this one F5 device can't reach the Duo API endpoint.
I'll have to work on getting that resolved.
I had assumed there weren't any connectivity issues as the Duo RADIUS login method relied on a user's browser to load a script from Duo instead of the F5 device communicating directly with Duo. Its communications with the API endpoint were also being handled by a Duo Authentication Proxy that had connectivity.
I'll try to report back when that's cleared up.
In this case I believe the F5 acts as an OAuth client talking directly to the API server at Duo, which is unlike SAML authentication.
I recall running into a similar issue with one of my deployments of this setup.