Guest Blog from Julian Jakob (@jakob_davidson)
Table of Contents
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
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 https://citrixguyblog.com/2017/07/22/citrix-netscaler-loadbalancing-exchange-20132016-walkthrough-guide/
1 2 3 4 5 |
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!
1 2 3 4 |
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:
1 2 3 4 5 6 7 8 9 |
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)
I am looking to set this up, but no where in in the this article do you actually setup the actions the NoAAA policies point to or the lb virtual servers that I assume those actions point to. I see the actions refered to as CS_Action_Exchange-AUT-NoAAA and CS_Action_Exchange-EWS-NoAAA. What is special about these items that I am missing?
NoAAA links to Exchange-LB where there’s no AAA vServer (preauthentication) bound to. So the Exchange Online is able to point directly to the EWS and AUT Service, only with the active Patternset as a filter.
The remaining access of Clients is pointing to Exchange-LB with bound AAA vServer for preauthentication.
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
The cs_action are missing , can you please share them as well
Hell Rickard. Please read the following article:
https://citrixguyblog.com/2017/07/22/citrix-netscaler-loadbalancing-exchange-20132016-walkthrough-guide/
This should explain the EWS and AAA configuration.
But what exactly is included in the action
CS_Action_Exchange-EWS
CS_Action_Exchange-EWS-NoAAA
CS_Action_Exchange-AUT-NoAAA
CS_Action_Exchange-AUT