Table of Contents
Overview
Not all customers are able to use modern authentication methods regarding SAML / OAuth with Identity Providers like Azure Active Directory, Okta and so on – there is still a small but not uninteresting market for legacy authentications with OnPrem MFA.
That’s where Citrix NetScaler Native OTP and Push OTP to play.
Both features are based on Active Directory attributes, the auth protocol is LDAP(S). Native OTP comes with Advanced license and Push OTP with Premium license.
The Combination
Because the Push Service is based on Citrix Cloud, you have to use the Citrix SSO App, as no other App is supporting the Push notification. This requirement isn’t fitting in most environments, mostly because of BYOD / external service providers / …
That’s why there was the idea to built a combination of both solutions. For more details about the requirements and configuration hints, see my post here.
The goal is to provide both methods and giving the user the possibility to choose their preferred method. Push OTP with Citrix SSO App or Native OTP with any TOTP-supported App / Software. There are also options to extend the nFactor flow with some RADIUS methods, for example when doing a gentle migration from deprecated RADIUS MFA provider’s to NetScaler OTP.
Configuration
Let’s collect some of my experienced limitations and requirements regarding the design:
- The Sub-URL /manageotp is accessing the Inbuilt LoginSchema SingleAuthManageOTP.xml
- This is happening as there a some hidden fields which create the NSC_TASS Cookie and enables the OTP-Registration page
- Use a dedicated Token-Selfservice URL (=Push OTP), the User isn’t able to differ between the Gateway Loginpage and the Sub-URL /manageotp
- The Token-Selfservice URL has to be published to the Internet
- To register for Push OTP, you have to use the Citrix SSO App, which isn’t available for all Smartphone Operating System
- When trying to scan the QR-Code with other OTP Apps, the registration of the OTP fails, as the Push Service cannot connect
- Create a separate Token-Selfservice-Legacy URL (=Native OTP) – which is basically the configuration of Native OTP – and use both in a row via nFactor flow
- The Token-Selfservice-Legacy URL has not to be published to the Internet
- For hardening (so Users are only able to register and manage their Token from internal) use a dedicated Gateway vServer with an AAA profile and some SRC IP filters set into the authentication LoginSchema policy (via Pattern Sets)
- Use the Inbuilt “DualAuthPushOrOTP” LoginSchema as your template, if there’s no internet-connection for receiving the Push-Notification, Users are able to insert the OTP (Both TOTP from Citrix SSO and any Native OTPs) by manual as there is the option to enable a third loginbox
Templates & Script
Here’s my script to configure the whole setup so you’re getting a working MFA-flow with both Push and Native OTP. This includes also options for dedicated Token-Selfservice addresses and a Group Extraction at the beginning for having an “Emergency Fallback” to only Username + Password if a User is member of a defined group.
Download my LoginSchema templates and upload to /nsconfig/loginschema/customer on your NetScaler:
########## Config for gaining Citrix NetScaler Push OTP with Native OTP in nFactor Flow with "Emergency 1FA" Group Extraction, using only one AAA vServer ########## Replace customer with your Organization ########## Replace contoso.local with your internal ADS FQDN ########## Replace dc=contoso,dc=local with your DC ADS FQDN ########## Replace ACL-1FA-Emergency with your ADS Groupname for Disabling MFA for all Groupmembers ########## Replace customer.com with your public FQDN ########## Replace 192.168.1.0/24 with your internal Subnets for Filtering Access to the MFA Selfservice Page ########## Replace OTPAttribute with your AD Attribute name for the OTP storage location ########## Replace service.citrixcag with your LDAPS Service Account ########## Replace 10.10.10.2 with your LDAPS VIP ########## Replace OTPEncryption with your SSL certificate name installed on NetScaler which is used for encrypted OTP (highly recommended!) ############################################################################################################################# bind vpn global -userDataEncryptionKey OTPEncryption set aaa otpparameter -encryption ON -maxOTPDevices 3 add authentication vserver AAA_vServer_nFactor_NA SSL 0.0.0.0 add authentication authnProfile AAA_Prof_nFactor -authnVsName AAA_vServer_nFactor_NA add authentication pushService CitrixADC-MFAService -namespace "https://mfa.cloud.com/" -clientID 12345 -clientSecret 12345 -CustomerID 123id456 -trustService "https://trust.citrixworkspacesapi.net/" add authentication ldapAction Auth_LDAPS_No_Filter -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -searchFilter "memberOf:1.2.840.113556.1.4.1941:=CN=ACL-ExternalAccess,OU=Test,dc=contoso,dc=local" -groupAttrName memberOf -subAttributeName CN -secType SSL -passwdChange ENABLED -nestedGroupExtraction ON -maxNestingLevel 5 -groupNameIdentifier cn -groupSearchAttribute memberOf -groupSearchSubAttribute cn add authentication ldapAction Auth_LDAPS_OTP_set_no-auth -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -passwdChange ENABLED -pushService CitrixADC-MFAService -OTPSecret OTPAttribute add authentication ldapAction Auth_LDAPS_OTP_verify_no-auth -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -searchFilter "OTPAttribute>={\"otpdata\"" -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -passwdChange ENABLED -pushService CitrixADC-MFAService -OTPSecret OTPAttribute add authentication ldapAction Auth_LDAPS_NoAuth -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -passwdChange ENABLED -nestedGroupExtraction ON -maxNestingLevel 5 -groupNameIdentifier cn -groupSearchAttribute memberOf -groupSearchSubAttribute cn add authentication ldapAction Auth_LDAPS_No_GroupFiltering -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName cn -secType SSL -passwdChange ENABLED -nestedGroupExtraction ON -maxNestingLevel 5 -groupNameIdentifier cn -groupSearchAttribute memberOf -groupSearchSubAttribute cn add authentication ldapAction Auth_LDAPS_OTP_set_no-auth-LegacyOTP -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -passwdChange ENABLED -OTPSecret OTPAttribute add authentication ldapAction Auth_LDAPS_OTP_verify_no-auth-LegacyOTP -serverIP 10.10.10.2 -serverPort 636 -ldapBase "dc=contoso,dc=local" -ldapBindDn service.citrixcag@contoso.local -ldapBindDnPassword Start#123 -ldapLoginName sAMAccountName -searchFilter "OTPAttribute>={\"otpdata\"" -groupAttrName memberOf -subAttributeName cn -secType SSL -authentication DISABLED -passwdChange ENABLED -OTPSecret OTPAttribute add authentication loginSchema Single_Manage_OTP-lschema -authenticationSchema "/nsconfig/loginschema/customer/SingleAuthManageOTP.xml" add authentication loginSchema Schema_NoLoginSchema -authenticationSchema noschema add authentication loginSchema Schema_OnlyUsername -authenticationSchema "/nsconfig/loginschema/customer/Only_Username.xml" add authentication loginSchema Schema_OnlyPassword -authenticationSchema "/nsconfig/loginschema/customer/PrefillEmergency1FA.xml" add authentication loginSchema Dual_Auth_UserExtract -authenticationSchema "/nsconfig/loginschema/customer/DualAuthPushOrOTP_UserExtract.xml" -passwordCredentialIndex 1 -SSOCredentials YES add authentication loginSchemaPolicy Single_Manage_OTP-lschemapol -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice.customer.com\") && (client.IP.SRC.IN_SUBNET(192.168.1.0/24))||HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice-legacy.customer.com\") && (client.IP.SRC.IN_SUBNET(192.168.1.0/24))" -action Single_Manage_OTP-lschema add authentication loginSchemaPolicy Schema_NoLoginSchema -rule true -action Schema_NoLoginSchema add authentication loginSchemaPolicy Schema_EnterUsername -rule true -action Schema_OnlyUsername add authentication loginSchemaPolicy Schema_OnlyPassword -rule true -action Schema_OnlyPassword add authentication Policy Adv_Pol_LDAPS_Selfservice -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice.customer.com\") && (client.IP.SRC.IN_SUBNET(192.168.1.0/24))||HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice-legacy.customer.com\") && (client.IP.SRC.IN_SUBNET(192.168.1.0/24))" -action Auth_LDAPS_No_GroupFiltering add authentication Policy Adv_Pol_Manage_OTP -rule true -action Auth_LDAPS_OTP_set_no-auth add authentication Policy Adv_Pol_Verify_OTP -rule true -action Auth_LDAPS_OTP_verify_no-auth add authentication Policy Adv_Pol_LDAPS_EmergencyGroup -rule "AAA.USER.IS_MEMBER_OF(\"ACL-1FA-Emergency\")" -action NO_AUTHN add authentication Policy Adv_Pol_2FA -rule true -action NO_AUTHN add authentication Policy Adv_Pol_LDAPS_GroupExtraction -rule true -action Auth_LDAPS_NoAuth add authentication Policy Adv_Pol_LDAPS -rule true -action Auth_LDAPS_No_Filter add authentication Policy Adv_Pol_Manage_OTP_Legacy -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice-legacy.customer.com\")" -action Auth_LDAPS_OTP_set_no-auth-LegacyOTP add authentication Policy Adv_Pol_Verify_OTP_Legacy -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice-legacy.customer.com\")" -action Auth_LDAPS_OTP_verify_no-auth-LegacyOTP add authentication Policy Adv_Pol_LDAPS_Selfservice_Legacy -rule "HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ(\"token-selfservice-legacy.customer.com\") && (client.IP.SRC.IN_SUBNET(192.168.1.0/24))" -action Auth_LDAPS_No_GroupFiltering add authentication Policy Adv_Pol_Verify_OTP_Auth_Legacy -rule true -action Auth_LDAPS_OTP_verify_no-auth-LegacyOTP add authentication policylabel OTP_pollabel -loginSchema LSCHEMA_INT add authentication policylabel NoSchema-2FA -loginSchema Schema_NoLoginSchema add authentication policylabel LDAP_PasswordOnly_Emergency -loginSchema Schema_OnlyPassword add authentication policylabel NoCert_GroupExtraction_2FA -loginSchema LSCHEMA_INT add authentication policylabel Dual_OTP_2FA -loginSchema Dual_Auth_UserExtract add authentication policylabel OTP_pollabel_Legacy -loginSchema LSCHEMA_INT bind authentication policylabel OTP_pollabel -policyName Adv_Pol_Manage_OTP -priority 110 -gotoPriorityExpression END bind authentication policylabel OTP_pollabel -policyName Adv_Pol_Verify_OTP -priority 120 -gotoPriorityExpression END bind authentication policylabel NoSchema-2FA -policyName Adv_Pol_Verify_OTP_Auth_Legacy -priority 90 -gotoPriorityExpression NEXT bind authentication policylabel NoSchema-2FA -policyName Adv_Pol_Verify_OTP -priority 100 -gotoPriorityExpression END bind authentication policylabel LDAP_PasswordOnly_Emergency -policyName Adv_Pol_LDAPS -priority 100 -gotoPriorityExpression END bind authentication policylabel NoCert_GroupExtraction_2FA -policyName Adv_Pol_LDAPS_EmergencyGroup -priority 100 -gotoPriorityExpression NEXT -nextFactor LDAP_PasswordOnly_Emergency bind authentication policylabel NoCert_GroupExtraction_2FA -policyName Adv_Pol_2FA -priority 110 -gotoPriorityExpression NEXT -nextFactor Dual_OTP_2FA bind authentication policylabel Dual_OTP_2FA -policyName Adv_Pol_LDAPS -priority 100 -gotoPriorityExpression NEXT -nextFactor NoSchema-2FA bind authentication policylabel OTP_pollabel_Legacy -policyName Adv_Pol_Manage_OTP_Legacy -priority 100 -gotoPriorityExpression END bind authentication policylabel OTP_pollabel_Legacy -policyName Adv_Pol_Verify_OTP_Legacy -priority 110 -gotoPriorityExpression END bind authentication vserver AAA_vServer_nFactor_NA -policy Single_Manage_OTP-lschemapol -priority 90 -gotoPriorityExpression END bind authentication vserver AAA_vServer_nFactor_NA -policy Schema_EnterUsername -priority 100 -gotoPriorityExpression END bind authentication vserver AAA_vServer_nFactor_NA -policy Adv_Pol_LDAPS_Selfservice -priority 90 -nextFactor OTP_pollabel -gotoPriorityExpression NEXT bind authentication vserver AAA_vServer_nFactor_NA -policy Adv_Pol_LDAPS_Selfservice_Legacy -priority 95 -nextFactor OTP_pollabel_Legacy -gotoPriorityExpression NEXT bind authentication vserver AAA_vServer_nFactor_NA -policy Adv_Pol_LDAPS_GroupExtraction -priority 110 -nextFactor NoCert_GroupExtraction_2FA -gotoPriorityExpression NEXT ########## Recommended vpn traffic actions for working sso scenarios when using sslvpn and or native otp and or webapps with authorization-bearer headers add vpn trafficAction traf_act_StoreFront_SSO http -SSO ON add vpn trafficAction traf_act_SSLVPN_SSO http -SSO ON add vpn trafficAction traf_act_SSLVPN_AuthHeader_Bearer http -SSO OFF add vpn trafficPolicy traf_pol_StoreFront_SSO true traf_act_StoreFront_SSO add vpn trafficPolicy traf_pol_SSLVPN_SSO "http.req.method.eq(post)||http.req.method.eq(get) && false" traf_act_SSLVPN_SSO add vpn trafficPolicy traf_pol_SSLVPN_AuthHeader_Bearer "HTTP.REQ.HEADER(\"Authorization\").SET_TEXT_MODE(IGNORECASE).CONTAINS(\"Bearer\")" traf_act_SSLVPN_AuthHeader_Bearer
Summary
If you’re having NetScaler Premium license and you’re thinking about to use Push OTP, I can highly recommend to use this method to provide both ways and to be prepared for supporting all kind of TOTP Apps and Operating Systems.
2 comments