Posted in : ADFS, NetScaler, Office 365

3 years ago

A common issue in organizations moving to Office 365 is the different URLs the users have to remember. This can be made easier by for example smart links, where the users only have to remember something like ”office.example.com” or ”onedrive.example.com”.
This is something we can easily do with NetScaler and ADFS. See below for a few examples.
First, create a few pattern sets and expressions to reflect the different host headers:

add policy patset PATSET_HOSTHEADER_O365_ADMINPORTAL
bind policy patset PATSET_HOSTHEADER_O365_ADMINPORTAL adminportal.example.com -index 1
add policy patset PATSET_HOSTHEADER_O365_PORTAL
bind policy patset PATSET_HOSTHEADER_O365_PORTAL portal.example.com -index 1
add policy patset PATSET_HOSTHEADER_O365_ONEDRIVE
bind policy patset PATSET_HOSTHEADER_O365_ONEDRIVE onedrive.example.com -index 1
add policy patset PATSET_HOSTHEADER_O365_SHAREPOINT
bind policy patset PATSET_HOSTHEADER_O365_SHAREPOINT sharepoint.example.com -index 1
add policy expression EXP_ADV_HOSTHEADER_O365_ADMINPORTAL "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_O365_ADMINPORTAL\")"
add policy expression EXP_ADV_HOSTHEADER_O365_PORTAL "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_O365_PORTAL\")"
add policy expression EXP_ADV_HOSTHEADER_O365_ONEDRIVE "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_O365_ONEDRIVE\")"
add policy expression EXP_ADV_HOSTHEADER_O365_SHAREPOINT "HTTP.REQ.HEADER(\"Host\").SET_TEXT_MODE(IGNORECASE).EQUALS_ANY(\"PATSET_HOSTHEADER_O365_SHAREPOINT\")"

In this case, they will do the following:

  • adminportal.example.com will point to portal.office.com but not use SSO in ADFS (internally), but rather allow the user to enter username and password.
  • portal.example.com will point to portal.office.com and use SSO in ADFS (internally)
  • onedrive.example.com will point to OFFICE365TEANANT-my.sharepoint.com
  • sharepoint.example.com will point to OFFICE365TENANT.sharepoint.com

Next step, create the responder actions. Different for internal and external usage:

add responder action REA-INT_O365_ADMINPORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D3\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\" + \"&wauth=urn:oasis:names:tc:SAML:1.0:am:password\""
add responder action REA-INT_O365_PORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D1\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\""
add responder action REA-INT_O365_ONEDRIVE-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D1\" + \"%26wreply%3Dhttps:%252F%252F\" + \"OFFICE365TENANT-my.sharepoint.com\""
add responder action REA-INT_O365_SHAREPOINT-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D1\" + \"%26wreply%3Dhttps:%252F%252F\" + \"OFFICE365TENANT.sharepoint.com\""
add responder action REA-EXT_O365_ADMINPORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D3\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\""
add responder action REA-EXT_O365_PORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D3\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\""
add responder action REA-EXT_O365_ONEDRIVE-REDIRECT redirect "\"https://adfs.example.com/adfs/ls?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D1\" + \"%26wreply%3Dhttps:%252F%252F\" + \"OFFICE365TENANT-my.sharepoint.com\" + \"&username=\" + HTTP.REQ.URL.QUERY.VALUE(\"username\").TO_LOWER"
add responder action REA-EXT_O365_SHAREPOINT-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D3\" + \"%26wreply%3Dhttps:%252F%252F\" + \"OFFICE365TENANT.sharepoint.com\""

In my case, I’ve added the ability to enter add a query string to onedrive, which forwards it to ADFS. For example, you’ll be able to go to https://onedrive.example.com/?username=simon and it will be entered in ADFS for you. Great to use in combination with other tools that already knows you username. Don’t forget to change adfs.example.com and OFFICE365TENANT to your own.
Now we’ll create the responder policies and policy labels:

add responder policy REP-INT_O365_REDIRECT-NOOP "EXP_ADV_HOSTHEADER_O365_ADMINPORTAL || EXP_ADV_HOSTHEADER_O365_PORTAL || EXP_ADV_HOSTHEADER_O365_ONEDRIVE || EXP_ADV_HOSTHEADER_O365_SHAREPOINT" NOOP
add responder policy REP-INT_O365_ADMINPORTAL-REDIRECT "EXP_ADV_HOSTHEADER_O365_ADMINPORTAL" REA-INT_O365_ADMINPORTAL-REDIRECT
add responder policy REP-INT_O365_PORTAL-REDIRECT "EXP_ADV_HOSTHEADER_O365_PORTAL" REA-INT_O365_PORTAL-REDIRECT
add responder policy REP-INT_O365_ONEDRIVE-REDIRECT "EXP_ADV_HOSTHEADER_O365_ONEDRIVE" REA-INT_O365_ONEDRIVE-REDIRECT
add responder policy REP-INT_O365_SHAREPOINT-REDIRECT "EXP_ADV_HOSTHEADER_O365_SHAREPOINT" REA-INT_O365_SHAREPOINT-REDIRECT
add responder policy REP-EXT_O365_REDIRECT-NOOP "EXP_ADV_HOSTHEADER_O365_ADMINPORTAL || EXP_ADV_HOSTHEADER_O365_PORTAL || EXP_ADV_HOSTHEADER_O365_ONEDRIVE || EXP_ADV_HOSTHEADER_O365_SHAREPOINT" NOOP
add responder policy REP-EXT_O365_ADMINPORTAL-REDIRECT "EXP_ADV_HOSTHEADER_O365_ADMINPORTAL" REA-EXT_O365_ADMINPORTAL-REDIRECT
add responder policy REP-EXT_O365_PORTAL-REDIRECT "EXP_ADV_HOSTHEADER_O365_PORTAL" REA-EXT_O365_PORTAL-REDIRECT
add responder policy REP-EXT_O365_ONEDRIVE-REDIRECT "EXP_ADV_HOSTHEADER_O365_ONEDRIVE" REA-EXT_O365_ONEDRIVE-REDIRECT
add responder policy REP-EXT_O365_SHAREPOINT-REDIRECT "EXP_ADV_HOSTHEADER_O365_SHAREPOINT" REA-EXT_O365_SHAREPOINT-REDIRECT
add responder policylabel REPL-INT_O365_REDIRECT
bind responder policylabel REPL-INT_O365_REDIRECT REP-INT_O365_ADMINPORTAL-REDIRECT 100 END
bind responder policylabel REPL-INT_O365_REDIRECT REP-INT_O365_PORTAL-REDIRECT 110 END
bind responder policylabel REPL-INT_O365_REDIRECT REP-INT_O365_ONEDRIVE-REDIRECT 120 END
bind responder policylabel REPL-INT_O365_REDIRECT REP-INT_O365_SHAREPOINT-REDIRECT 130 END
add responder policylabel REPL-EXT_O365_REDIRECT
bind responder policylabel REPL-EXT_O365_REDIRECT REP-EXT_O365_ADMINPORTAL-REDIRECT 100 END
bind responder policylabel REPL-EXT_O365_REDIRECT REP-EXT_O365_PORTAL-REDIRECT 110 END
bind responder policylabel REPL-EXT_O365_REDIRECT REP-EXT_O365_ONEDRIVE-REDIRECT 120 END
bind responder policylabel REPL-EXT_O365_REDIRECT REP-EXT_O365_SHAREPOINT-REDIRECT 130 END

 
And as a last step, bind these policy labels to vserver, in my example to content switching vservers:

add cs vserver CS-INTERNAL-TCP80 HTTP <INTERNAL IP> 80 -cltTimeout 180
bind cs vserver CS-INTERNAL-TCP80 -policyName REP-INT_O365_REDIRECT-NOOP -priority 90 -gotoPriorityExpression END -type REQUEST -invoke policylabel REPL-INT_O365_REDIRECT
add cs vserver CS-INTERNAL-TCP443 SSL <INTERNAL IP> 443 -cltTimeout 18000
bind ssl vserver CS-INTERNAL-TCP443 -certkeyName star.example.com
bind cs vserver CS-INTERNAL-TCP443 -policyName REP-INT_O365_REDIRECT-NOOP -priority 90 -gotoPriorityExpression END -type REQUEST -invoke policylabel REPL-INT_O365_REDIRECT
add cs vserver CS-EXTERNAL-TCP80 HTTP <EXTERNAL IP> 80 -cltTimeout 180
bind cs vserver CS-EXTERNAL-TCP80 -policyName REP-EXT_O365_REDIRECT-NOOP -priority 90 -gotoPriorityExpression END -type REQUEST -invoke policylabel REPL-EXT_O365_REDIRECT
add cs vserver CS-EXTERNAL-TCP443 SSL <EXTERNAL IP> 443 -cltTimeout 18000
bind ssl vserver CS-EXTERNAL-TCP443 -certkeyName star.example.com
bind cs vserver CS-EXTERNAL-TCP443 -policyName REP-EXT_O365_REDIRECT-NOOP -priority 90 -gotoPriorityExpression END -type REQUEST -invoke policylabel REPL-EXT_O365_REDIRECT

Please leave a comment if something doesn’t work as expected or you have some enhancements that can make this work even better! I’ve tried it with ADFS on Windows Server 2012 R2.

Tags : NetScaler, Office 365

Comments

jason M says

Simon - thanks for the scripts. When External user access a smart link through my ADFS proxy (via - netscaler) an IIS ERROR 500 is returned from the ADFS host.
internal users work great.
Any thoughts?

Simon Gottschlag says

Hi Jason!
If you for example compare the internal and external actions to portal.office.com, you'll see that the only difference is in the LoginOptions=1 or LoginOptions=2 (URL Encoded, where = is %3D):
add responder action REA-INT_O365_PORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D1\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\""
add responder action REA-EXT_O365_PORTAL-REDIRECT redirect "\"https://adfs.example.com/adfs/ls\?\" + \"wa=wsignin1.0&lc=1033&wtrealm=urn:federation:MicrosoftOnline\" + \"&wctx=wa%3Dwsignin1.0%26rpsnv%3D4%26ct%3D1435822861%26rver%3D6.4.6456.0%26\" + \"wp%3DMCMBI%26lc%3D1033%26id%3D501392%26bk%3D1435822861%26\" + \"LoginOptions%3D3\" + \"%26wreply%3Dhttps:%252F%252F\" + \"portal.office.com\""
That option decides if the check box for "remember me" is checked or not, if I'm not mistaken. Have you tried using the internal expression externally, does it work? Maybe there's some ADFS settings? (extranet user have different permissions?)

Jason says

I'm using the external settings for both internal and external users. Internal users get the ADFS webpage, authenticate and then are redirector properly.
External users get the netscape gateway, authenticate and then receive an IIS 500 from the internal ADFS site.

Add comment

Your comment will be revised by the site if needed.