If you get the task to load balance Exchange with NetScaler you will find a lot of whitepapers from Citrix with missing information and false configuration recommendations. I was bumping my head against the wall until I got a running configuration with all desired features. Here is the complete walkthrough guide to setup your Exchange environment with a single public ip address. Additional features like AAA, Front End Optimization and Integrated Caching will depend on your current NetScaler licence.
Change Log
2019-12-07
– Updated expressions for the Authorization Policies (AAA.USER)
– Fixxed missing binding point for Traffic Session Policy “tm_pol_exchange2016_owa_sso” on the AAA vServer. Thanks @ Christian Demmerer
2019-10-28
– AAA-default settings changed with Citrix ADC (NetScaler) 13 build 41.20
2018-05-23
– Added 401 Based Authentication for MAPI, RPC, OAB, EWS
– Added Group Filtering for OWA, Outlook Anywhere and ActiveSync
2018-05-16
– Changed Persistence for the RPC/MAPI LB vServer from RULE to SOURCEIP
– Increased timeout from 240 to 30 minutes (RPC/MAPI)
Table of contents
1. Feature Matrix
2. Network Topology
3. Requirements
3.1 Firewall Rules
4. Configuration
4.1 Features
4.2 Server
4.3 Monitors
4.4 Service Groups
4.5 Load Balancer
4.6 Content Switch
4.7 AAA (Enterprise)
4.7.1 Authentication Policies
4.7.2 SSO Traffic Policies
4.7.3 Group Filtering
4.8 Front End Optimization (Enterprise)
4.9 Integrated Caching (Platinum)
1. Feature Matrix
Licence |
Load Balancing |
Content Switching |
Responder |
AAA |
Front End Optimization |
Integrated Caching |
Standard |
x |
x |
x |
– |
– |
– |
Enterprise |
x |
x |
x |
x |
x |
– |
Platinum |
x |
x |
x |
x |
x |
x |
2. Network Topology
3. Requirements
- One public ip address
- Two private IP addresses (Content Switch and Load Balancer)
- Working DNS/NTP on NetScaler
- Wildcard SSL certificate
3.1 Firewall Rules
From |
To |
Port |
Description |
SNIP |
DNS Server |
UDP/TCP 53 |
DNS |
SNIP |
NTP Server |
UDP 123 |
NTP |
SNIP |
Domain Controller |
TCP 389 |
LDAP |
SNIP |
Domain Controller |
TCP 636 |
LDAPS |
NSIP |
Exchange Server |
TCP 25, 465, 587 |
SMTP Monitor |
SNIP |
Exchange Server |
TCP 25, 465, 587 |
SMTP |
SNIP |
Exchange Server |
TCP 143, 993 |
IMAP |
SNIP |
Exchange Server |
HTTPS – 443 |
OWA, AutoDiscover, ActiveSync, MAPI, etc. |
Internet |
SMTP LB IP |
TCP 25, 465, 587 |
SMTP |
Internet |
IMAP LB IP |
TCP 143, 993 |
IMAP |
Internet |
Content Switch VIP |
HTTP – 80 |
Web Traffic |
Internet |
Content Switch VIP |
HTTPS – 443 |
Web Traffic |
If you dont load balance DNS/LDAPS/NTP the traffic will flow from the NSIP. In my setup the servers are load balanced –> The SNIP is communicating with the backend servers.
4. Configuration
4.1 Features
#Features enable ns feature CS,RESPONDER,LB,SSL
4.2 Server
#Create serves #Replace FQDN and ip address regarding your environment add server EX01.lab.local 192.168.2.102 add server EX02.lab.local 192.168.2.103
4.3 Monitors
Name |
Type |
Standard Parameter |
Send String |
mon_smtp |
SMTP |
– |
– |
mon_owa |
HTTP-ECV |
Secure |
“GET /owa/healthcheck.htm” |
mon_activesync |
HTTP-ECV |
Secure |
“GET /Microsoft-Server-ActiveSync/healthcheck.htm” |
mon_rpc |
HTTP-ECV |
Secure |
“GET /rpc/healthcheck.htm” |
mon_ews |
HTTP-ECV |
Secure |
“GET /ews/healthcheck.htm” |
mon_autodiscover |
HTTP-ECV |
Secure |
“GET /Autodiscover/healthcheck.htm” |
mon_mapi |
HTTP-ECV |
Secure |
“GET /mapi/healthcheck.htm” |
mon_ecp |
HTTP-ECV |
Secure |
“GET /ecp/healthcheck.htm” |
#Create monitors add lb monitor mon_smtp SMTP add lb monitor mon_owa HTTP-ECV -send "GET /owa/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_activesync HTTP-ECV -send "GET /Microsoft-Server-ActiveSync/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_rpc HTTP-ECV -send "GET /rpc/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_ews HTTP-ECV -send "GET /ews/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_autodiscover HTTP-ECV -send "GET /Autodiscover/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_oab HTTP-ECV -send "GET /oab/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_mapi HTTP-ECV -send "GET /mapi/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES add lb monitor mon_ecp HTTP-ECV -send "GET /ecp/healthcheck.htm" recv 200 -LRTM DISABLED -secure YES
4.4 Service Groups
Name |
Protocol |
Monitor |
svcgrp_ex2016_smtp_25 |
TCP |
mon_smtp |
svcgrp_ex2016_smtp_465 |
TCP |
mon_smtp |
svcgrp_ex2016_smtp_587 |
TCP |
mon_smtp |
svcgrp_ex2016_imap_143 |
TCP |
TCP |
svcgrp_ex2016_imap_993 |
TCP |
TCP |
svcgrp_ex2016_owa |
SSL |
mon_owa |
svcgrp_ex2016_activesync |
SSL |
mon_activesync |
svcgrp_ex2016_rpc |
SSL |
mon_rpc |
svcgrp_ex2016_ews |
SSL |
mon_ews |
svcgrp_ex2016_autodisover |
SSL |
mon_autodiscover |
svcgrp_ex2016_oab |
SSL |
mon_oab |
svcgrp_ex2016_mapi |
SSL |
mon_mapi |
svcgrp_ex2016_epc |
SSL |
mon_epc |
#Create Service Groups add serviceGroup svcgrp_ex2016_smtp_25 TCP add serviceGroup svcgrp_ex2016_smtp_465 TCP add serviceGroup svcgrp_ex2016_smtp_587 TCP add serviceGroup svcgrp_ex2016_imap_143 TCP add serviceGroup svcgrp_ex2016_imap_993 TCP add serviceGroup svcgrp_ex2016_owa SSL add serviceGroup svcgrp_ex2016_activesync SSL add serviceGroup svcgrp_ex2016_rpc SSL add serviceGroup svcgrp_ex2016_ews SSL add serviceGroup svcgrp_ex2016_autodisover SSL add serviceGroup svcgrp_ex2016_oab SSL add serviceGroup svcgrp_ex2016_mapi SSL add serviceGroup svcgrp_ex2016_ecp SSL
#Bind Service Groups #Replace FQDN and ip address regarding your environment bind servicegroup svcgrp_ex2016_smtp_25 EX01.lab.local 25 bind servicegroup svcgrp_ex2016_smtp_25 EX02.lab.local 25 bind serviceGroup svcgrp_ex2016_smtp_25 -monitorName mon_smtp bind servicegroup svcgrp_ex2016_smtp_465 EX01.lab.local 465 bind servicegroup svcgrp_ex2016_smtp_465 EX02.lab.local 465 bind serviceGroup svcgrp_ex2016_smtp_465 -monitorName mon_smtp bind servicegroup svcgrp_ex2016_smtp_587 EX01.lab.local 587 bind servicegroup svcgrp_ex2016_smtp_587 EX02.lab.local 587 bind serviceGroup svcgrp_ex2016_smtp_587 -monitorName mon_smtp bind servicegroup svcgrp_ex2016_imap_143 EX01.lab.local 143 bind servicegroup svcgrp_ex2016_imap_143 EX02.lab.local 143 bind serviceGroup svcgrp_ex2016_imap_143 -monitorName TCP bind servicegroup svcgrp_ex2016_imap_993 EX01.lab.local 993 bind servicegroup svcgrp_ex2016_imap_993 EX02.lab.local 993 bind serviceGroup svcgrp_ex2016_imap_993 -monitorName TCP bind servicegroup svcgrp_ex2016_owa EX01.lab.local 443 bind servicegroup svcgrp_ex2016_owa EX02.lab.local 443 bind serviceGroup svcgrp_ex2016_owa -monitorName mon_owa bind servicegroup svcgrp_ex2016_activesync EX01.lab.local 443 bind servicegroup svcgrp_ex2016_activesync EX02.lab.local 443 bind servicegroup svcgrp_ex2016_activesync -monitorName mon_activesync bind servicegroup svcgrp_ex2016_rpc EX01.lab.local 443 bind servicegroup svcgrp_ex2016_rpc EX02.lab.local 443 bind servicegroup svcgrp_ex2016_rpc -monitorName mon_rpc bind servicegroup svcgrp_ex2016_ews EX01.lab.local 443 bind servicegroup svcgrp_ex2016_ews EX02.lab.local 443 bind servicegroup svcgrp_ex2016_ews -monitorName mon_ews bind servicegroup svcgrp_ex2016_autodisover EX01.lab.local 443 bind servicegroup svcgrp_ex2016_autodisover EX02.lab.local 443 bind servicegroup svcgrp_ex2016_autodisover -monitorName mon_autodiscover bind servicegroup svcgrp_ex2016_oab EX01.lab.local 443 bind servicegroup svcgrp_ex2016_oab EX02.lab.local 443 bind servicegroup svcgrp_ex2016_oab -monitorName mon_oab bind servicegroup svcgrp_ex2016_mapi EX01.lab.local 443 bind servicegroup svcgrp_ex2016_mapi EX02.lab.local 443 bind servicegroup svcgrp_ex2016_mapi -monitorName mon_mapi bind servicegroup svcgrp_ex2016_ecp EX01.lab.local 443 bind servicegroup svcgrp_ex2016_ecp EX02.lab.local 443 bind servicegroup svcgrp_ex2016_ecp -monitorName mon_ecp
4.5 Load Balancer
vServer |
IP address |
Method |
Persistence |
Timeout |
Protocol |
Authentication (AAA) |
SMTP |
192.168.2.248 |
Least Connection |
NONE |
Default |
TCP |
|
IMAP |
192.168.2.248 |
Least Connection |
NONE |
Default |
TCP |
|
OWA |
0.0.0.0 |
Least Connection |
NONE |
Default |
SSL |
FBA |
ECP |
0.0.0.0 |
Least Connection |
NONE |
Default |
SSL |
FBA |
ActiveSync |
0.0.0.0 |
SRCIPDESTIP |
NONE |
Default |
SSL |
401 |
AutoDiscover |
0.0.0.0 |
SourceIP |
NONE |
30 |
SSL |
401 |
RPC |
0.0.0.0 |
Least Connection |
SOURCEIP |
30 |
SSL |
401 |
EWS |
0.0.0.0 |
Least Connection |
NONE |
Default |
SSL |
401 |
OAB |
0.0.0.0 |
Least Connection |
NONE |
Default |
SSL |
401 |
MAPI |
0.0.0.0 |
Least Connection |
SOURCEIP |
30 |
SSL |
401 |
#Create Load Balancer add lb vserver lb_vsrv_ex2016_smtp_25 TCP 192.168.2.248 25 add lb vserver lb_vsrv_ex2016_smtp_465 TCP 192.168.2.248 465 add lb vserver lb_vsrv_ex2016_smtp_587 TCP 192.168.2.248 587 add lb vserver lb_vsrv_ex2016_imap_143 TCP 192.168.2.248 143 add lb vserver lb_vsrv_ex2016_imap_993 TCP 192.168.2.248 993 add lb vserver lb_vsrv_ex2016_owa SSL 0.0.0.0 0 -persistenceType NONE add lb vserver lb_vsrv_ex2016_activesync SSL 0.0.0.0 0 -persistenceType SRCIPDESTIP add lb vserver lb_vsrv_ex2016_rpc SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 add lb vserver lb_vsrv_ex2016_ews SSL 0.0.0.0 0 -persistenceType NONE add lb vserver lb_vsrv_ex2016_autodiscover SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 add lb vserver lb_vsrv_ex2016_oab SSL 0.0.0.0 0 -persistenceType NONE add lb vserver lb_vsrv_ex2016_mapi SSL 0.0.0.0 0 -persistenceType SOURCEIP -timeout 30 add lb vserver lb_vsrv_ex2016_ecp SSL 0.0.0.0 0 -persistenceType NONE
#Bind Service Groups to vServer bind lb vserver lb_vsrv_ex2016_smtp_25 svcgrp_ex2016_smtp_25 bind lb vserver lb_vsrv_ex2016_smtp_465 svcgrp_ex2016_smtp_465 bind lb vserver lb_vsrv_ex2016_smtp_587 svcgrp_ex2016_smtp_587 bind lb vserver lb_vsrv_ex2016_imap_143 svcgrp_ex2016_imap_143 bind lb vserver lb_vsrv_ex2016_imap_993 svcgrp_ex2016_imap_993 bind lb vserver lb_vsrv_ex2016_owa svcgrp_ex2016_owa bind lb vserver lb_vsrv_ex2016_activesync svcgrp_ex2016_activesync bind lb vserver lb_vsrv_ex2016_rpc svcgrp_ex2016_rpc bind lb vserver lb_vsrv_ex2016_ews svcgrp_ex2016_ews bind lb vserver lb_vsrv_ex2016_autodiscover svcgrp_ex2016_autodisover bind lb vserver lb_vsrv_ex2016_oab svcgrp_ex2016_oab bind lb vserver lb_vsrv_ex2016_mapi svcgrp_ex2016_mapi bind lb vserver lb_vsrv_ex2016_ecp svcgrp_ex2016_ecp
#Bind SSL certificate #Replace certificate name bind ssl vserver lb_vsrv_ex2016_owa -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_activesync -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_rpc -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_ews -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_autodiscover -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_oab -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_mapi -certkeyName 'Wildcard-Flashmob' bind ssl vserver lb_vsrv_ex2016_ecp -certkeyName 'Wildcard-Flashmob'
4.6 Content Switch
Name |
Action |
Target |
Expression |
cs_pol_owa |
cs_act_owa |
lb_vsrv_ex2016_owa |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/owa”) |
cs_pol_ews |
cs_act_ews |
lb_vsrv_ex2016_ews |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/ews”) |
cs_pol_activesync |
cs_act_activesync |
lb_vsrv_ex2016_activesync |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“Microsoft”) |
cs_pol_autodiscover |
cs_act_autodiscover |
lb_vsrv_ex2016_autodiscover |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/autodiscover”) |
cs_pol_rpc |
cs_act_rpc |
lb_vsrv_ex2016_rpc |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/rpc”) |
cs_pol_ews |
cs_act_ews |
lb_vsrv_ex2016_ews |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/ews”) |
cs_pol_oab |
cs_act_oab |
lb_vsrv_ex2016_oab |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/oab”) |
cs_pol_mapi |
cs_act_mapi |
lb_vsrv_ex2016_mapi |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/mapi”) |
cs_pol_cgi |
cs_act_owa |
lb_vsrv_ex2016_owa |
HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS(“/cgi”) |
cs_pol_owa_redirect |
cs_act_owa |
lb_vsrv_ex2016_owa |
HTTP.REQ.HOSTNAME.EQ(“mail.flashmob-saulgau.de”) |
#Create Content Switch #Replace IP address of Content Switch add cs vserver cs_exchange2016_http HTTP 192.168.1.20 80 add cs vserver cs_exchange2016_ssl SSL 192.168.1.20 443 #Replace certificate name bind ssl vserver cs_exchange2016_ssl -certkeyName 'Wildcard-Flashmob'
#Create Content Switch Policies add cs action cs_act_owa -targetLBVserver lb_vsrv_ex2016_owa add cs policy cs_pol_owa -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/owa")' -action cs_act_owa add cs action cs_act_ews -targetLBVserver lb_vsrv_ex2016_ews add cs policy cs_pol_ews -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/ews")' -action cs_act_ews add cs action cs_act_autodiscover -targetLBVserver lb_vsrv_ex2016_autodiscover add cs policy cs_pol_autodiscover -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/autodiscover")' -action cs_act_autodiscover add cs action cs_act_activesync -targetLBVserver lb_vsrv_ex2016_activesync add cs policy cs_pol_activesync -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("Microsoft")' -action cs_act_activesync add cs action cs_act_oab -targetLBVserver lb_vsrv_ex2016_oab add cs policy cs_pol_oab -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/oab")' -action cs_act_oab add cs action cs_act_mapi -targetLBVserver lb_vsrv_ex2016_mapi add cs policy cs_pol_mapi -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/mapi")' -action cs_act_mapi add cs action cs_act_rpc -targetLBVserver lb_vsrv_ex2016_rpc add cs policy cs_pol_rpc -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/rpc")' -action cs_act_rpc add cs action cs_act_ecp -targetLBVserver lb_vsrv_ex2016_ecp add cs policy cs_pol_ecp -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/ecp")' -action cs_act_ecp #OWA Fix https://support.citrix.com/article/CTX209060 add cs policy cs_pol_cgi -rule 'HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).CONTAINS("/cgi")' -action cs_act_owa #Redirect to OWA if only https://mail.flashmob-saulgau.de will be entered #Replace the mail FQDN add cs policy cs_pol_owa_redirect -rule 'HTTP.REQ.HOSTNAME.EQ("mail.flashmob-saulgau.de")' -action cs_act_owa
#Bind Content Switch Policies bind cs vserver cs_exchange2016_ssl -policyName cs_pol_owa -priority 100 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_ews -priority 110 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_autodiscover -priority 120 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_activesync -priority 130 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_oab -priority 140 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_mapi -priority 150 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_rpc -priority 160 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_ecp -priority 170 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_cgi -priority 180 bind cs vserver cs_exchange2016_ssl -policyName cs_pol_owa_redirect -priority 190
#OWA HTTP Redirect (Responder) #Replace the mail FQDN add responder action resp_act_owa redirect '"https://"+HTTP.REQ.HOSTNAME+"/owa/"' add responder policy resp_pol_owa 'HTTP.REQ.HOSTNAME.CONTAINS("mail.flashmob-saulgau.de")' resp_act_owa bind cs vserver cs_exchange2016_http -policyName resp_pol_owa -priority 100
4.7 AAA (Enterprise)
If you have an enterprise licence you can let take the authenication on the AAA server and redirect the credentials to OWA. This feature offers improved security integration and n-factor authentication like RADIUS, SAML and certificate authentication is possible. If you enter the mail domain you will be redirected to the AAA login page.
If you want to you use “401 Based Authentication” on the Autodiscover service you need to set the following registry key in the user profile.
HKCU\SOFTWARE\Microsoft\Office\16.0\Common\Identity\EnableADAL (REG_DWORD 0)
Otherwise Outlook 2016 is crashing while setting up the profile (ADAL is enabled by default)
NetScaler is not supporting ADAL at the moment.
https://discussions.citrix.com/topic/376086-outlook-2016-and-autodiscover/
Update (2018-05-23): When using NetScaler >= 12.0 there is no need to create the “EnableADAL” key. https://support.citrix.com/article/CTX216539
Update (2019-10-28): Johannes Norz reached out to me that there is a problem with the authentication when running the Citrix ADC on firmware 13.41.20 and higher. You will receive the message “Error: Not an privileged user” after the succesfull authentication. Citrix changed the default authorization action to “DENY” which is causing the issue. You can change it back to “ALLOW” or create an authorization policy. For more information check his blog post.
#Enable AAA Feature enable ns feature AAA #Create AAA Server add authentication vserver AAA_Exchange_2016 SSL 0.0.0.0 #Replace certificate name bind ssl vserver AAA_Exchange_2016 -certkeyName 'Wildcard-Flashmob' bind authentication vserver AAA_Exchange_2016 -portaltheme X1 #Replace AAA FQDN set authentication vserver AAA_Exchange_2016 -authenticationDomain 'flashmob-saulgau.de'
4.7.1 Authentication Policies
In this setup we will authenticate with LDAP only. For this we need create two policies.
1.) LDAP with sAMAccountName
2.) LDAP with userPrincipalName
The user can login with “test” or “test@lab.local”.
#LDAP SAM + UPN Policy #Replace server ip, ldapbase, bind user and password add authentication ldapaction LDAP_UPN -serverip 192.168.2.1 -secType SSL -serverPort 636 -ldapBase "DC=lab,DC=local" -ldapBindDn ns-ldap@lab.local -ldapBindDnPassword ##password## -ldapLoginName userPrincipalName -groupAttrName "memberOf" -subAttributeName "cn" -ssoNameAttribute userPrincipalName -passwdChange ENABLED -followReferrals ON add authentication ldapaction LDAP_SAM -serverip 192.168.2.1 -secType SSL -serverPort 636 -ldapBase "DC=lab,DC=local" -ldapBindDn ns-ldap@lab.local -ldapBindDnPassword ##password## -ldapLoginName sAMAccountName -groupAttrName "memberOf" -subAttributeName "cn" -ssoNameAttribute userPrincipalName -passwdChange ENABLED -followReferrals ON add authentication ldappolicy LDAP_UPN ns_true LDAP_UPN add authentication ldappolicy LDAP_SAM ns_true LDAP_SAM
#Bind LDAP Policies bind authentication vserver AAA_Exchange_2016 -policy LDAP_UPN -priority 100 bind authentication vserver AAA_Exchange_2016 -policy LDAP_SAM -priority 110
4.7.2 SSO Traffic Policies
#Create AAA Session Policy #Replace "ssoDomain" add tm sessionAction tm_act_exchange2016_owa_sso -defaultAuthorization ALLOW -SSO ON -ssoDomain 'lab.local' add tm sessionPolicy tm_pol_exchange2016_owa_sso 'HTTP.REQ.URL.CONTAINS("/owa/auth/logon.aspx")' tm_act_exchange2016_owa_sso
#Create SSO Form and Policies add tm formSSOAction sso_profile_exchange_2016_owa -actionURL "/owa/auth.owa" -userField "username" -passwdField "password" -responsesize "60000" -ssoSuccessRule 'HTTP.RES.SET_COOKIE.COOKIE("cadata").VALUE("cadata").LENGTH.GT(70)' -nvtype DYNAMIC -submitMethod POST add tm trafficAction traffic_prof_exchange_2016_owa -SSO ON -appTimeout 1 -formSSOAction sso_profile_exchange_2016_owa add tm trafficAction traffic_prof_exchange_2016_owa_logout -InitiateLogout ON add tm trafficPolicy traffic_pol_exchange_2016_owa 'HTTP.REQ.URL.CONTAINS("/owa/auth/logon.aspx")' traffic_prof_exchange_2016_owa add tm trafficPolicy traffic_pol_exchange_2016_owa_logout 'HTTP.REQ.URL.CONTAINS("/owa/logoff.owa")' traffic_prof_exchange_2016_owa_logout
#Bind Traffic Session Policy to the AAA vServer bind authentication vserver AAA_Exchange_2016 -policy tm_pol_exchange2016_owa_sso -priority 100 -gotoPriorityExpression NEXT
#Bind SSO Policies to the OWA vServer bind lb vserver lb_vsrv_ex2016_owa -policyName traffic_pol_exchange_2016_owa -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_owa -policyName traffic_pol_exchange_2016_owa_logout -priority 110 -gotoPriorityExpression END -type REQUEST
#Set FBA and 401 authentication #Replace AuthenticationHost FQDN set lb vserver lb_vsrv_ex2016_owa -Authentication ON -authnVsName AAA_Exchange_2016 -AuthenticationHost aaa.flashmob-saulgau.de set lb vserver lb_vsrv_ex2016_ecp -Authentication ON -authnVsName AAA_Exchange_2016 -AuthenticationHost aaa.flashmob-saulgau.de set lb vserver lb_vsrv_ex2016_activesync -authn401 ON -authnVsName AAA_Exchange_2016 set lb vserver lb_vsrv_ex2016_autodiscover -authn401 ON -authnVsName AAA_Exchange_2016 set lb vserver lb_vsrv_ex2016_rpc -authn401 ON -authnVsName AAA_Exchange_2016 set lb vserver lb_vsrv_ex2016_ews -authn401 ON -authnVsName AAA_Exchange_2016 set lb vserver lb_vsrv_ex2016_oab -authn401 ON -authnVsName AAA_Exchange_2016 set lb vserver lb_vsrv_ex2016_mapi -authn401 ON -authnVsName AAA_Exchange_2016
#Content Switch AAA #Replace AuthenticationHost FQDN add cs action cs_act_aaa -targetVserver AAA_Exchange_2016 add cs policy cs_pol_aaa -rule 'HTTP.REQ.HOSTNAME.EQ("aaa.flashmob-saulgau.de")' -action cs_act_aaa bind cs vserver cs_exchange2016_ssl -policyName cs_pol_aaa -priority 90
The priorty of the AAA content switch policy must be the one with the lowest priority.
4.7.3 Group Filtering
If you need to restrict the external access to security groups in Active Directory, create the following authorization policies.
#Group Filtering #Replace AD Groups add authorization policy pol_auth_owa "AAA.USER.IS_MEMBER_OF(\"External-OWA\").NOT" DENY add authorization policy pol_auth_outlook "AAA.USER.IS_MEMBER_OF(\"External-Outlook\").NOT" DENY add authorization policy pol_auth_activesync "AAA.USER.IS_MEMBER_OF(\"External-ActiveSync\").NOT" DENY
Bind the authorization policies to the vServers.
bind lb vserver lb_vsrv_ex2016_owa -policyName pol_auth_owa -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_ecp -policyName pol_auth_owa -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_rpc -policyName pol_auth_outlook -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_mapi -policyName pol_auth_outlook -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_ews -policyName pol_auth_outlook -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_oab -policyName pol_auth_outlook -priority 100 -gotoPriorityExpression END -type REQUEST bind lb vserver lb_vsrv_ex2016_activesync -policyName pol_auth_activesync -priority 100 -gotoPriorityExpression END -type REQUEST
When your user is not in the “External-OWA” group and the authentication against the AAA server was succesfull you will get the following notification:
“Error: Not a privileged User.”
I created a responder html page, to present a more user friendly message. This can’t be done via CLI. Go to AppExpert –> Responder –> HTML Page Imports
You are not authorized to use the OWA service. Please contact the Helpdesk. 0049 - 0000 - 0000 helpdesk@flashmob-saulgau.de
Now you can create the responder policy & action.
#OWA Deny Responder add responder action resp_act_owa_deny respondwithhtmlpage html_owa -responseStatusCode 200 add responder policy resp_pol_owa_deny "AAA.USER.IS_MEMBER_OF(\"External-OWA\").NOT" resp_act_owa_deny<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span> bind lb vserver lb_vsrv_ex2016_owa -policyName resp_pol_owa_deny -priority 100 -gotoPriorityExpression END -type REQUEST
4.8 Front End Optimization (Enterprise)
I couldnt test this feature too much so I configured like descriped in the Citrix whitepaper. If the optimization action “Moderate” is not suiting your expectations you can try the “Aggresive” mode. You can verify if FOE is working within the GUI. Go to Optimization –> Front End Optimization –> Statics (“stat feo” in CLI)
#Enable Front End Optimization enable ns feature FEO #Front End Optimization #Replace FQDN add feo policy feo_pol_exchange2016 'HTTP.REQ.HOSTNAME.CONTAINS("mail.flashmob-saulgau.de")' MODERATE bind cs vserver cs_exchange2016_ssl -policyName feo_pol_exchange2016 -priority 100
4.9 Integrated Caching (Platinum)
In the Citrix docs its recommended to allocate less than half of the NetScalers memory for integrated caching. Set the parameter “memLimit” fitting to your appliance. I’m using 500MB. You can verify if the cache is working within the GUI. Go to Optimization –> Integrated Caching –> View Cache Objects (“show cache object” in CLI)
#Enable Integrated Caching enable ns feature IC #Integrated Caching set cache parameter -memLimit 500 set serviceGroup svcgrp_ex2016_owa -CMP YES set serviceGroup svcgrp_ex2016_activesync -CMP YES set serviceGroup svcgrp_ex2016_rpc -CMP YES set serviceGroup svcgrp_ex2016_ews -CMP YES set serviceGroup svcgrp_ex2016_autodisover -CMP YES set serviceGroup svcgrp_ex2016_oab -CMP YES set serviceGroup svcgrp_ex2016_mapi -CMP YES set serviceGroup svcgrp_ex2016_ecp -CMP YES add cache contentGroup cache_group_exchange2016 -type HTTP -weakNegRelExpiry 233 -weakPosRelExpiry 233 add cache policy cache_pol_exchange2016 -rule 'TRUE' -action CACHE -storeInGroup cache_group_exchange2016 bind cs vserver cs_exchange2016_ssl -policyName cache_pol_exchange2016 -priority 100 -type REQUEST
I hope this guide helped you to load balance Exchange with Citrix NetScaler.
If you find any misconfigurations or have improvments please contact me.
Hi Julian,
Awesome blog. I am experiencing problem in configuring a email connector for our mail box to configure IMAP, so that this cloud based application will fetch the email from this email box and raise a ticket against each email.
Exchange is currently load balanced using Netscaler. Now I need to configure IMAP over SSL (993). I have done below steps.
On our firewall, I have created a NAT rule to send traffic to Netscaler LBVS VIP.
Created service group in Netscaler and binded exchange servers using port 993TCP and assigned the TCP monitor.
Created LBVS VIP and bind the service group to the LBVS.
Now the netscaler is listening to 993 from exchange boxed as I can see all service is UP and GREEN.
Note : Since it is TCP port i couldn’t bind any SSL certs at netscaler level.
Testing that I have done – Telnet the hostname with 993 failed externally.
Telnet to the local netscaler LBVS VIP from exchange box on 993 port connecting.
Telnet to the local netscaler LBVS VIP from netscaler appliance SSH on 993 port is connecting.
I am not sure what I am missing.Should we require the MAPI content switching config also to be in place to make this work?
Your help is much appreciated.!
Thanks
LikeLike
Hi Abdul,
from LAN the MAPI LB vServer is accessible and working as desired? You can not content switch the MAPI protocol > DNAT directly to the LB VIP
LikeLike
HI Julian,
From LAN IMAP LBVS is accessible, however apart from exchange box and netscaler I can’t telnet 993 even from lan machines.. I am not sure what I am missing. here.
Already I have the direct NAT of public IP to LB VIP.
As of now, I don’t have any MAPI configurations in place. This is my earler question whether I need MAPI configuration to make this work. you have cleared it now in your reply saying not required.
Thanks
Abdul
LikeLike
Firewall inside the LAN? Using the same setup (TCP-993) at a customer. No problem with IMAP connections. Can you take a trace with WireShark?
LikeLike
Firewall is at DMZ. Can you please confirm the below configuration made is as per standards?
Created service group in Netscaler and binded exchange servers using port 993-TCP and assigned the TCP monitor.
Created LBVS VIP and bind the service group to the LBVS.
Sorry for the confusion. I have checked in other LAN machines. I can telnet to port 993 internally to the LBVS VIP.
It is only not working from externally. I have made necessary changes in the Firewall, still not working.
Any ideas
Thanks
LikeLike
Yes but I would create a more specific monitor. You can poll for the receive string. It’s like „IMAP Exchange Service is running..“ You can see the exact message via telnet. If it’s not working from external I suggest there is a problem with your NAT. Can you create the DNAT directly to your Exchange for testing?
LikeLike
Hello, thank you for this awesome guide!
We have a problem that the user is prompted to enter his password after he switches from one network to another. (LAN to WiFi)
Is this made by design (session persistence sourceip) or is there any way to avoid those outlook prompts.
Thanks in advance.
Best regards,
Christian
LikeLike
Hi,
Thanks for a great article, stumbled upon a small problem. I am not able to catch the /owa/logoff.owa since its never sent. (verified with sniffer). Using Exchange 2016.
Do you know if there is a solution to this
Thanks,
LikeLike
@SLR – I am dealing with the same issue. I can’t get the force logoff or the cookie cleared and am not finding any solutions or alternatives. I am wondering if Exchange is sending a http://owa.domain.com/owa/logoff.aspx URL back to the client but it never reaches the Netscaler because we don’t have 80 allowed on the firewall. Traces ahead….
LikeLike
hey Citrix guy!
I noticed that you set up an LB server for SMTP services. was this set up using SourceIP? I’m just curious if you have set up SMTP sourceIP LB using different subnets (NS in one subnet, exchange servers in another subnet). I’ve been asked to do this and although I have set up sourceIP LB for exchange on the same subnet as the NS, I have not done so when there are 2 subnets. I’m thinking it may not be possible but thought I would check anyway.
LikeLike
Hello Paul the thing with SourceIP persistence is that its working without any problems for an internal client. If we take a look at the external client access which is happening over a DNAT (in most of the cases) the backend server load could be very high because its always coming from the same source IP. I would go with NONE persistence for SMTP. But I am not an Exchange/SMTP guru 🙂
LikeLike
Hello,
Thank you for the guide. Do you have a blog on restricting access to SMTP Traffic. Currently in the configuration you have and I have implemented, any device that knows the relay VIP can now send traffic through the relay. I would like to restrict by IP as you can in the Exchange Relay setting in Exchange ECP.
LikeLike
Luke E. – If you want to restrict allowed relays / senders then you need to add ACLs. What I have done previously is add ACLs for individual IP addresses or networks with a priority of 1-100 or whatever number you need and then add a blanket DENY ACL and give it a very high priority number like 5000. If you have a lot of IPs to add you can use CONCANTENATE in excel to help build your commands and use Get-ReceiveConnector -Identity “Mail Relay” | select-object Identity,RemoteIPRanges to dump the allowed relays on your existing relay connector in Exchange. The only alternative would be to route your mail relays direct to a secondary IP bound on an Exchange Server NIC – but no HA. You can view your configured ACLs at any time by running show ns acl.
LikeLike
If you do not want to work with ACLs this should be possible with a listen policy on the LB vServer as well.
Example: CLIENT.IP.SRC.EQ(192.168.2.2) or CLIENT.IP.SRC.IN_SUBNET(192.168.2.0/24)
LikeLike
I can´t see avalible any https VS, looking in the owa VS, service groups members monitors; I get the Failure – Time out during SSL handshake stage error
LikeLike
Hello
Could you clarify why do you use SSL-based LB vServers (0.0.0.0) behind CS vServer instead of HTTP ones ? At the first look HTTP will be enough because these servers are not directly accessible outside ADC and all communications are only between CS and LB one. So additional ADC resources will be used for encrypting/decrypting the stream between them if SSL is used.
Or I miss something?
Thanks in Advance
LikeLike
Hello Alexey, it should work with a HTTP as well. Give it a try.
LikeLike
Julian, I just upgraded to Citrix ADC 13 built 41.20. This broke my exchange deployment as default authorization changed from allow to deny. You’ll need an additional authorization policy in future (see https://blog.norz.at/aaa-default-settings-changed-with-citrix-adc-netscaler-13-built-41-20/)
LikeLike
Hello Johannes, thanks for the information. I am going to update the blog in the next days 👍🏽
LikeLike
Hey Julian,
Perfect Blog articel, Thank you alot.
I have one question.
You configure a session action and policy for the SSO.
Ist it just me and i havent found it, or ist this policy not bound to the AAA Server?
BR Christian
LikeLike
Thanks for this good guide 🙂
LikeLike