Citrix ADC – Securing Microsoft Exchange Hybrid Deployments

Reading Time: 4 minutes

Overview

As most MS-Exchange-Online migrations are ending up with one Exchange-part OnPrem – it’s called Exchange Hybrid Deployment – I was thinking about the hardening of the two commonly used exchange virtual directories, Autodiscover and Exchange Web Services (EWS), mostly for MS-Teams calendar access for OnPremises mailboxes.

Exchange-Online is using Open-Authorization (OAuth), so the first intension was to use OAuth at ADC for pre-authentication with AAA. Then I checked the following article from the MS Exchange Team

Configuring Teams calendar access for Exchange on-premises mailboxes – Microsoft Tech Community

Containing the following statement (which I commented on the thread, hoping the MS-Team will ever dig into it…)

“Autodiscover and EWS URLs should be available from the Internet. Pre-Auth is not supported. If you use some sort of publishing system, you will need to configure pass-through.”

And there’s also the following article from the F5 BigIP Support engineers, which is listed to use passthrough-auth instead of pre-auth for the selected used virtual directories Exchange Outlook Calender not visible with hybrid setup across on-prem and cloud users (f5.com)

So, what’s better getting full access to /Autodiscover and /EWS without pre-authentication AND staying in the Microsoft’s supported line? Filtering using Pattern Sets on ADC. The goal is to difference between M365 servers requesting /Autodiscover or /EWS (No-AAA-Pre-Auth) and random clients from the internet requesting /Autodiscover and /EWS for their Outlook Anywhere, too (With enabled AAA-Pre-Auth) – if you’re not allowing Outlook Anywhere directly from the internet, you can bind a default LoadBalancer to your Content Switch containing a responder blocklist. (Jump directly to part three)

Configuration

I’ve used the latest JSON (07/29/2021) (https://endpoints.office.com/endpoints/worldwide?clientrequestid=b10c5ed1-bad1-445f-b386-b919946339a7) from Microsoft containing all listed IP ranges of Office 365 and I’ve filtered the file, so it’s only containing:

  • IPv4 ranges (as all my ADC customers don’t using IPv6 yet)
  • Only service areas for Exchange-Online, Teams (containing S4B), SharePoint, OneDrive and the M365 common and office online ones
  • Only TCP Port 443

ATM the filtered list is containing about 38 IP ranges from Microsoft. A future goal is to automate the JSON updates to the patset on ADC, so you don’t have to maintain the entries by manual.

Part one, creating the Pattern Set and associated policy expression

#########################################################################################

add policy patset PatSet_Allow_M365

# Exchange Online:

bind policy patset PatSet_Allow_M365       	"13.107.6.152/31" 	-index 1
bind policy patset PatSet_Allow_M365      	"13.107.18.10/31"	-index 2
bind policy patset PatSet_Allow_M365     	"13.107.128.0/22"	-index 3
bind policy patset PatSet_Allow_M365      	"23.103.160.0/20"	-index 4
bind policy patset PatSet_Allow_M365      	"40.96.0.0/13"		-index 5
bind policy patset PatSet_Allow_M365      	"40.104.0.0/15"		-index 6
bind policy patset PatSet_Allow_M365      	"52.96.0.0/14"		-index 7
bind policy patset PatSet_Allow_M365      	"131.253.33.215/32"	-index 8
bind policy patset PatSet_Allow_M365      	"132.245.0.0/16"	-index 9
bind policy patset PatSet_Allow_M365     	"150.171.32.0/22"	-index 10
bind policy patset PatSet_Allow_M365     	"204.79.197.215/32"	-index 11
bind policy patset PatSet_Allow_M365	  	"40.92.0.0/15"		-index 12
bind policy patset PatSet_Allow_M365      	"40.107.0.0/16"		-index 13
bind policy patset PatSet_Allow_M365      	"52.100.0.0/14"		-index 14
bind policy patset PatSet_Allow_M365      	"52.238.78.88/32"	-index 15
bind policy patset PatSet_Allow_M365      	"104.47.0.0/17"		-index 16

	  
# Skype for Business and MS-Teams:

bind policy patset PatSet_Allow_M365      	"13.107.64.0/18"	-index 17
bind policy patset PatSet_Allow_M365      	"52.112.0.0/14"		-index 18
bind policy patset PatSet_Allow_M365      	"52.120.0.0/14"		-index 19
bind policy patset PatSet_Allow_M365      	"52.238.119.141/32"	-index 20
bind policy patset PatSet_Allow_M365      	"52.244.160.207/32"	-index 21

	  
# SharePoint Online and OneDrive for Business:

bind policy patset PatSet_Allow_M365      	"13.107.136.0/22"	-index 22
bind policy patset PatSet_Allow_M365      	"40.108.128.0/17"	-index 23
bind policy patset PatSet_Allow_M365     	"52.104.0.0/14"		-index 24
bind policy patset PatSet_Allow_M365      	"104.146.128.0/17"	-index 25
bind policy patset PatSet_Allow_M365      	"150.171.40.0/22"	-index 26
	  
# Microsoft 365 Common and Office Online:

bind policy patset PatSet_Allow_M365      	"13.107.6.171/32"	-index 27
bind policy patset PatSet_Allow_M365      	"13.107.18.15/32"	-index 28
bind policy patset PatSet_Allow_M365      	"13.107.140.6/32"	-index 29
bind policy patset PatSet_Allow_M365      	"52.108.0.0/14"		-index 30
bind policy patset PatSet_Allow_M365      	"52.238.106.116/32"	-index 31
bind policy patset PatSet_Allow_M365      	"52.244.37.168/32"	-index 32
bind policy patset PatSet_Allow_M365      	"52.244.203.72/32"	-index 33
bind policy patset PatSet_Allow_M365      	"52.244.207.172/32"	-index 34
bind policy patset PatSet_Allow_M365      	"52.244.223.198/32"	-index 35
bind policy patset PatSet_Allow_M365      	"52.247.150.191/32"	-index 36
bind policy patset PatSet_Allow_M365	  	"20.190.128.0/18"	-index 37
bind policy patset PatSet_Allow_M365      	"40.126.0.0/18"		-index 38



add policy expression PolExpr_Allow_M365 "(CLIENT.IP.SRC + \"/32\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(31) + \"/31\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(30) + \"/30\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(29) + \"/29\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(28) + \"/28\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(27) + \"/27\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(26) + \"/26\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(25) + \"/25\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(24) + \"/24\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(23) + \"/23\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(22) + \"/22\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(21) + \"/21\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(20) + \"/20\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(19) + \"/19\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(18) + \"/18\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(17) + \"/17\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(16) + \"/16\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(15) + \"/15\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(14) + \"/14\").EQUALS_ANY(\"PatSet_Allow_M365\") || (CLIENT.IP.SRC.SUBNET(13) + \"/13\").EQUALS_ANY(\"PatSet_Allow_M365\")"

Part two is about creating the content switch policies for the correct filtering. You have to create 2x Autodiscover and 2x EWS Exchange load balancing vServer on your ADC – the difference is w / wo AAA Pre-Auth and w / wo filtering on the Pattern Sets. Checkout Julian’s Exchange ADC post if you’re needing some technical details

add cs policy CS_Policy_Exchange-AUT-NoAAA -rule HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ("autodiscover.customer.com")&&HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH("/autodiscover")&&PolExpr_Allow_M365 -action CS_Action_Exchange-AUT-NoAAA
add cs policy CS_Policy_Exchange-EWS-NoAAA -rule HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ("exchange.customer.com")&&HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH("/ews")&&PolExpr_Allow_M365 -action CS_Action_Exchange-EWS-NoAAA

add cs policy CS_Policy_Exchange-AUT -rule HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ("autodiscover.customer.com")&&HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH("/autodiscover") -action CS_Action_Exchange-AUT
add cs policy CS_Policy_Exchange-EWS -rule HTTP.REQ.HOSTNAME.SET_TEXT_MODE(IGNORECASE).EQ("exchange.customer.com")&&HTTP.REQ.URL.SET_TEXT_MODE(IGNORECASE).STARTSWITH("/ews") -action CS_Action_Exchange-EWS

Very important is the priority of the policy bindings to your exchange content switching vServer, the „NoAAA“ policies have to hit first!

bind cs vserver CS_vServer_Exchange -policyName CS_Policy_Exchange-AUT-NoAAA -priority 10
bind cs vserver CS_vServer_Exchange -policyName CS_Policy_Exchange-EWS-NoAAA -priority 11
bind cs vserver CS_vServer_Exchange -policyName CS_Policy_Exchange-AUT -priority 20
bind cs vserver CS_vServer_Exchange -policyName CS_Policy_Exchange-EWS -priority 21

Part three is containing to create a blocking responder page, which is hitting as the default load balancer bound to the exchange content switch OR if you’re not allowing outlook anywhere service and your IP is not listed in the PatSet, the responder action is giving you this output:

add responder action RespAction_Blocklist respondwith "This IP address ("+ CLIENT.IP.SRC +") is blocked to connect to this service."
add responder policy RespPol_Blocklist TRUE RespAction_Blocklist

add service Service_AlwaysUp 127.0.0.1 HTTP 80
add lb vserver LB_vServer_BlockedPage_NA HTTP 0.0.0.0 0 -persistenceType NONE -cltTimeout 180
bind lb vserver LB_vServer_BlockedPage_NA Service_AlwaysUp
bind lb vserver LB_vServer_BlockedPage_NA -policyName RespPol_Blocklist -priority 100 -gotoPriorityExpression END -type REQUEST

bind cs vserver CS_vServer_Exchange -lbvserver LB_vServer_BlockedPage_NA

Summary

A positive side effect is to compare M365 server access with the direct internet client access via the different content switching policies. In my opinion it’s not the most secure way, but way better than putting the OnPrem exchange-host directly to the internet. I’m curious if Microsoft will change the possibilities for ADC’s regarding pre-authentication, soon.

Big thanks to Jeroen Tielen as his blog post about IP Black and Whitelisting with Citrix ADC was the missing impulse for creating this post.

Hints

If you’re having problems with free / busy query in your hybrid deployment, check your WSSecurityAuthentication, see Nod’s Blog: Office 365 – Exchange Online – Free/Busy Query from Exchange Online Mailbox to On Premises Exchange Fails (johnliew.net)

Leave a Reply

Your email address will not be published.