Hi All,
In my previous job i was tasked with the design and implementation of a SSO solution for Service-Now.
I was given the liberty on what technology i wanted to use. The main requirement was SSO for internal users, ensuring redundancy and ensuring DR capability and secure access for external users.
As the design had to be across a dual site environment, combined with a lot of different components (ADFS, SAML, SQL etc...) i thought it would be a smart idea to document the steps and configurations.
The PDF walks through how you setup an ADFS v3.0 cluster, setup Netscalers to load balance the cluster, setup other Netscalers to become the ADFS proxy for SAML and more.
Unfortunate i wasn't able to rerun the steps to proof-check so if you find any errors please let me know and i will update the paper.
Enjoy.
Netscaler SAML SSO to Service-Now
Monday, 24 August 2015
Sunday, 19 April 2015
Netscaler Form SSO
FormSSO
Recently my company asked if an internal website Cognology could be made SSO for the users. The version we are currently running has a known bug where the native SSO capability doesn’t work. Sounds like an easy job for the Netscalers.
After multiple failures and banging my head repeatedly onto the desk I resorted to using Citrix support.
The usage of SSO forms is not very well documented on the internet and troubleshooting the issue even less. Edocs, well we all see the value in those so hence this document to hopefully help out another person trying to achieve the same thing, now please be gentle I am far from an expert so if you find any mistakes or things are not explained completely let me know and I’ll update the doco.
First a bit of background. We have a development environment with our webserver and our Netscaler and a client pc. See diagram below.
The first step is really trying to understand the web form. The best way to do this is to get a network capture of the traffic between the client and the web server without the use of the Netscaler. I configured the clients host record to go directly to the webserver and used wireshark on the client side to capture a network trace.
I start the trace and browse to the website, in our case cognology.lab.local. The site presents a username and password field, hence I enter my credentials and click login. The login is successful so I stop the trace. In the filter to make it easier to read I put in http.host==”cognology.lab.local” and click apply. This will show me the packets I am interested in.
On top I find the first GET request, right click on the packet and choose follow TCP stream. This gives me the entire conversation in “human” readable text. I have highlighted the important bits and the bits we will need to use in the SSOform.
Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Connection: Keep-Alive
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
Set-Cookie: ASP.NET_SessionId= dffdsqert1llp0oamshug3yguxh; path=/; HttpOnly
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:01 GMT
Content-Length: 128
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx">here</a>.</h2>
</body></html>
GET /Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=dffdsqert1llp0oamshug3yguxh
HTTP/1.1 200 OK
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:01 GMT
Content-Length: 10892
In our case, when I browse to the website I use the first get. The site responds with a 302 and redirects me to /Login.aspx. Great now we know what the action URL is. I perform a new get to the correct site /login.aspx and I receive a 200 response and with it the actual logon form. This is really the information we are after. The first important bit is the content length: when you configure the SSOForm you will need to make sure that the value of the response size is bigger than the actual form’s content length. This was the first issue I encountered and very easy to overlook. The form itself starts with <form> and ends with </form>so we know that the username and password field is in between those tags. An easy way to identify these fields is using the developer’s tools in Internet Explorer
Use the selector (first red arrow) by clicking on it and then clicking inside the User ID field. This highlights the code being used for the user ID. We are only interested in the name of the field NOT the ID. In most cases I would assume the name and ID will be the same however in the off chance (like here) it can be very frustrating when your SSO isn’t working and you don’t know why. Do the same for the password field. Now we have all the information to fill out the form, so the last bits we need is to tell the Netscaler when the SSO is successful. So back to our network capture, here we find following info
Referer: http://cognology.lab.local/Login.aspx
Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Content-Length: 353
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ASP.NET_SessionId=mspdkk1llp0oamshug3yguxh
__LASTFOCUS=&__VIEWSTATE=%2FwEPDwULLTEzMDYyNzAwNTRkZNCLdZaXDK53ikEL8%2B2Ab3dNs8rt&__VIEWSTATEGENERATOR=C2EE9ABB&__SCROLLPOSITIONX=0&__SCROLLPOSITIONY=0&__EVENTTARGET=&__EVENTARGUMENT=&__EVENTVALIDATION=%2FwEdAALx4sL0xF501t4NZGIg8k46XO1iYFEISUsvEldktgB8sALwR90yULLCW2gXpSlMzwESSrWR&ctl00%24CP1%24edtUserID=myuser&edtPassword=MyPassword&btnLogin=Login
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Employee/MenuPIN.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: CognologyEnterprise.aspxauth=778D49EEC38CAA85DADCE664DF36BE2F186CC77DB6839C16FBC17B04AFE450D0C98D7BE21208D5C9F6B075AC5A53A34D04C0D51DB52AA751171FCEC6253DCA2182FE77DC7554C9C099159F5441343C2E862B05089E7961A36948305E2AAB26101F42AF3440A90A89C895C05D59E127F8430EB5AD; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:11 GMT
Content-Length: 139
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Employee/MenuPIN.aspx">here</a>.</h2>
</body></html>
On the new page click the Bind button and then click add.
Now the traffic policy window comes up, Provide a meaningful name under the Name section.
Under the expression section you need to specify the url where the SSO form needs to be applied upon. So in our case it would look like
Troubleshooting tips.
As said in the beginning I did bang my head a fair few times. Here are the mistakes I made
No the only thing not working is IE responding with the logged on user credentials to the 401 from the netscaler. But that's for another time.
After multiple failures and banging my head repeatedly onto the desk I resorted to using Citrix support.
The usage of SSO forms is not very well documented on the internet and troubleshooting the issue even less. Edocs, well we all see the value in those so hence this document to hopefully help out another person trying to achieve the same thing, now please be gentle I am far from an expert so if you find any mistakes or things are not explained completely let me know and I’ll update the doco.
First a bit of background. We have a development environment with our webserver and our Netscaler and a client pc. See diagram below.
The first step is really trying to understand the web form. The best way to do this is to get a network capture of the traffic between the client and the web server without the use of the Netscaler. I configured the clients host record to go directly to the webserver and used wireshark on the client side to capture a network trace.
I start the trace and browse to the website, in our case cognology.lab.local. The site presents a username and password field, hence I enter my credentials and click login. The login is successful so I stop the trace. In the filter to make it easier to read I put in http.host==”cognology.lab.local” and click apply. This will show me the packets I am interested in.
On top I find the first GET request, right click on the packet and choose follow TCP stream. This gives me the entire conversation in “human” readable text. I have highlighted the important bits and the bits we will need to use in the SSOform.
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Connection: Keep-Alive
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
Set-Cookie: ASP.NET_SessionId= dffdsqert1llp0oamshug3yguxh; path=/; HttpOnly
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:01 GMT
Content-Length: 128
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx">here</a>.</h2>
</body></html>
GET /Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=dffdsqert1llp0oamshug3yguxh
HTTP/1.1 200 OK
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:01 GMT
Content-Length: 10892
In our case, when I browse to the website I use the first get. The site responds with a 302 and redirects me to /Login.aspx. Great now we know what the action URL is. I perform a new get to the correct site /login.aspx and I receive a 200 response and with it the actual logon form. This is really the information we are after. The first important bit is the content length: when you configure the SSOForm you will need to make sure that the value of the response size is bigger than the actual form’s content length. This was the first issue I encountered and very easy to overlook. The form itself starts with <form> and ends with </form>so we know that the username and password field is in between those tags. An easy way to identify these fields is using the developer’s tools in Internet Explorer
Use the selector (first red arrow) by clicking on it and then clicking inside the User ID field. This highlights the code being used for the user ID. We are only interested in the name of the field NOT the ID. In most cases I would assume the name and ID will be the same however in the off chance (like here) it can be very frustrating when your SSO isn’t working and you don’t know why. Do the same for the password field. Now we have all the information to fill out the form, so the last bits we need is to tell the Netscaler when the SSO is successful. So back to our network capture, here we find following info
POST /Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*Referer: http://cognology.lab.local/Login.aspx
Accept-Language: en-AU
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: cognology.lab.local
Content-Length: 353
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ASP.NET_SessionId=mspdkk1llp0oamshug3yguxh
__LASTFOCUS=&__VIEWSTATE=%2FwEPDwULLTEzMDYyNzAwNTRkZNCLdZaXDK53ikEL8%2B2Ab3dNs8rt&__VIEWSTATEGENERATOR=C2EE9ABB&__SCROLLPOSITIONX=0&__SCROLLPOSITIONY=0&__EVENTTARGET=&__EVENTARGUMENT=&__EVENTVALIDATION=%2FwEdAALx4sL0xF501t4NZGIg8k46XO1iYFEISUsvEldktgB8sALwR90yULLCW2gXpSlMzwESSrWR&ctl00%24CP1%24edtUserID=myuser&edtPassword=MyPassword&btnLogin=Login
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Employee/MenuPIN.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: CognologyEnterprise.aspxauth=778D49EEC38CAA85DADCE664DF36BE2F186CC77DB6839C16FBC17B04AFE450D0C98D7BE21208D5C9F6B075AC5A53A34D04C0D51DB52AA751171FCEC6253DCA2182FE77DC7554C9C099159F5441343C2E862B05089E7961A36948305E2AAB26101F42AF3440A90A89C895C05D59E127F8430EB5AD; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Tue, 05 Aug 2014 05:38:11 GMT
Content-Length: 139
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Employee/MenuPIN.aspx">here</a>.</h2>
</body></html>
In the first section we see it is using the POST method to send the details. We will need that in the SSO form. We can also see our username and password there, so probably good to use the Netscaler to encrypt the traffic later on. The server responds with a 302 and sets a cookie: CognologyEnterprise.aspxauth=. Now we know when the authentication is successful it will set a cookie so we can use that as a success rule.
With all the information gathered lets create the form now.
Under security à aaa-application traffic à policies àtraffic highlight the Form SSO profiles and click add
Fill out the form with the info gathered
In the action URL with Netscaler version 10.5 do not put in the backslash in front of the login.aspx. In this case the HTML form specified the target as "login.aspx" and not "/login.aspx"
Click ok.
That should be it for the SSO form. Now we have to create a profile and bind it to the form.
My preferred method is creating the policy and profile while binding it to the vserver, but that is just a personal approach, no really right or wrong here.
Go to your vserrver and choose policies, click the + button.
Under choose policy select Traffic, and choose type select Request and click continue
Now the traffic policy window comes up, Provide a meaningful name under the Name section.
Under the expression section you need to specify the url where the SSO form needs to be applied upon. So in our case it would look like
http.REQ.URL.CONTAINS("Login.aspx")
under the profile section click the + button. This bring you to the TM Traffic Action form
Provide a name, apptimeout, ensure Single Sign On should be on and for Form SSO add the SSO form you created earlier
Click ok
This brings you back to the traffic policy that now should look like this
Click ok again so you are back in the traffic policy of the vserver
And click ok again. Now the traffic policy is bound to the vserver.
Next step is to bind the AAA server. I am assuming that you have already configured the AAA vserver with its primary authentication method set to ldap.(see screenshot)
Go to your virtual server and under authentication click the edit button. Add the authentication vserver and use 401 based authentication
Click save
I am using 401 to make it a bit easier, you can use form based authentication as well, you just need to specify the url pointing to the AAA server in that case.
That should be it now, we now need to update the DNS to point to our vserver and we can test.
Troubleshooting tips.
As said in the beginning I did bang my head a fair few times. Here are the mistakes I made
When filling in the SSO form I was using initially the UserID of the field and not the name. One way of finding out is by using the command nsconmsg –g sso_ -d current –s disptime=1
This command shows the stats when of the SSO form when used. (run the command first, then browse to your website and enter your credentials in the AAA popup or site)
So here in the output we can see the formextraction failed and 1 of the form fields was not found. This should give you a hint that the field name you used in the SSO form is incorrect. Also you now know that the SSO form is actually being used (Hit).
This command is a big help and thank you Citrix support (James Crocker) for sharing and explaining. Beforehand I was trolling through the network trace and could not find any “post” packet. So I was unsure if the SSOform was actually working or not.
The other issue was the content length. Per default it set the length on the SSO form for the response size is 8096. If the form is larger than this you might get a failure. As said before this one is easy to overlook.
Thirdly in the action URL, make sure that you that a backslash is needed or not. (is it a relative or absolute link)
Finally a successful outcome shows this output.
Subscribe to:
Posts (Atom)