Quick one from a recent deployment. This customer had chosen Swivel (http://www.swivelsecure.com) as their secondary authentication. Swivel requires a Turing image to be displayed on the VPN logon page to provide the user with one time password they need to provide for RADIUS authentication.
Swivel have helpfully provided very detailed guides on how to do this by replacing the index.html and login.js various NetScaler versions which helped a great deal. (https://kb.swivelsecure.com/wiki/index.php/Citrix_Netscaler_Gateway_10.x)
I Wanted to achieve this without changing any files on the NetScaler filesystem because..
1. The customer only wished to use Swivel on a single NetScaler Gateway VServer so we only wanted the turing image to display when users hit that Vserver meaning that simply swapping out the index.html with the customised version Swivel provide was not an option. We could have used responder or javascript to switch between index.html files based on the HTTP.REQ.HOSTNAME but.. well I didn’t want to.
2. I didn’t want to modify any of the files located on the NetScaler. There are a number of reasons for this but mainly to prevent issues with older versions of the index.html and login.js being preserved in a customers customisations after new versions of the appliance is upgraded to a newer version of the firmware.
3. I like the idea of using NetScaler Rewrite and Responder where ever possible. The customer paid for the feature so why not use it?
Anyway.. Here are the policies I used in the end. hope there save someone some time.
Please ensure the rewrite and responder features are enabled on your appliance.
//Responds with the 'ShowTuring' function when the URL /vpn/pinsafe.js is requested. This action only responds with the Swivel specific 'showTuring' function allowing us to keep the default login.js. This also allows us not to have to upload a new file to the NetScaler which would need ot be compressed into a customtheme.tar.gz or copied at startup. Replace <SwivelServerExternalFQDN> with the External FQDN of your Swivel Server.
add responder action ResAct_ShowTuring respondwith ""function showTuring() {ntsUrl=\"https://<SwivelServerExternalFQDN>/proxy/SCImage?username=\";ntsUser = document.getElementsByName(\"login\")[0].value;" + "ntif (sUser==\"\") {nttalert (\"Please enter your username first!\");nttdocument.getElementsByName(\"login\")[0].focus();nt}" + " else" + " {n ntt//Find the image using Mozilla compatible pproach...nttvarImg = document.getElementById(\"imgTuring\");n " + "ntt//Set the image SRC and make it visiblenttvarImg.src = sUrl + sUser + \"&random=\" + Math.round(Math.random()*100000);" + "n nttvarImg.style.visibility = \"visible\";nt}n n}""
add responder policy ResPol_ShowTuring "HTTP.REQ.URL.EQ("/vpn/pinsafe.js")" ResAct_ShowTuring
//Inserts a call for 'pinsafe.js' after the 'login.js' is called. Both js are called with login.js default and pinsafe containing only the 'showturing' function.
add rewrite action ReAct_VPN_Insert_pinsafe.js insert_after_all "HTTP.RES.BODY(100000)" q{"<script language="JavaScript" type="text/javascript" src="pinsafe.js"></script>"} -search q{text("<script language="JavaScript" type="text/javascript" src="login.js"></script>")}
add rewrite policy RePol_VPN_Insert_pinsafe.js "HTTP.REQ.URL.EQ("/vpn/index.html")" ReAct_VPN_Insert_pinsafe.js
//Inserts Swivel 'Get Image' button in the same table column as the default login button by replacing the default column opening and adding one if it's own prior to inserting the 'Get Image' button.
add rewrite action ReAct_SwivelGetImage_ReplaceAll replace_all "HTTP.RES.BODY(100000)" q{"<td><span style="display:none"><img src="/vpn/images/LoginButtonRolloverGlow.gif"/></span>" + "<input type="button" id="btnTuring" value="Get Image" onclick="showTuring();" class="CTX_CaxtonButton" onmouseover="this.className='CTX_CaxtonButton_Hover';" onmouseout="this.className='CTX_CaxtonButton';" />"} -search q/text("<td align="right">")/
add rewrite policy RePol_SwivelGetImage_ReplaceAll "HTTP.REQ.URL.EQ("/vpn/index.html")" ReAct_SwivelGetImage_ReplaceAll
//Inserts the Turing image element in a table row below the default login button
add rewrite action ReAct_SwivelImage_InsertAfterAll insert_after_all "HTTP.RES.BODY(100000)" ""n</tr><tr>n<td colspan=\"3\"><img id=imgTuring name=imgTuring style='visibility:hidden'/></td>"" -search q{text("<input type="submit" id="Log_On" value="" class="CTX_CaxtonButton" onclick="ns_check();" onmouseover="this.className='CTX_CaxtonButton_Hover';" onmouseout="this.className='CTX_CaxtonButton';" /></td>")}
add rewrite policy RePol_SwivelImage_InsertAfterAll "HTTP.REQ.URL.EQ("/vpn/index.html")" ReAct_SwivelImage_InsertAfterAll
\Removes the 1 from the first Password field text when a secondary authenticaiton policy is bound to a Vserver - removes from login.js
add rewrite policy RePol_VPN_Password1_Del1 "http.req.url.path.endswith("vpn/login.js")" ReAct_VPN_Password1_Del
add rewrite action ReAct_VPN_Password1_Del1 delete_all "http.RES.BODY(120000).SET_TEXT_MODE(ignorecase)" -pattern "document.write(' 1');" -bypassSafetyCheck YES
\Replaces the first Password field text when a secondary authenticaiton policy is bound to a Vserver (left as password but policy added in case the customer wanted to change this in the future) - replaces variable from login.js
add rewrite action ReAct_VPN_Password1_text replace_all "http.RES.BODY(120000).SET_TEXT_MODE(ignorecase)" ""Password'"" -pattern ""Password"" -bypassSafetyCheck YES -refineSearch q/extend(50,50).REGEX_SELECT(re![ ]*'[ ]*+[ ]*_("Password")[ ]*!)/
add rewrite policy RePol_VPN_Password1_text "http.req.url.path.endswith("vpn/login.js")" ReAct_VPN_Password1_text
\Replaces the second Password field text when a secondary authenticaiton policy is bound to a Vserver (set to OTC:) - replaces variable from login.js
add rewrite action ReAct_VPN_Password2_text replace_all "http.RES.BODY(120000).SET_TEXT_MODE(ignorecase)" ""OTC:'"" -pattern ""Password2"" -bypassSafetyCheck YES -refineSearch q/extend(50,50).REGEX_SELECT(re![ ]*'[ ]*+[ ]*_("Password2")[ ]*!)/
add rewrite policy RePol_VPN_Password2_text "http.req.url.path.endswith("vpn/login.js")" ReAct_VPN_Password2_text
\Bind Rewrite policies to VPN Vserver
bind vpn vserver NetScalerGatewayVserver -policy ReAct_VPN_UserNameInputLength -priority 90 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_SwivelGetImage_ReplaceAll -priority 100 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_SwivelImage_InsertAfterAll -priority 110 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_VPN_Password1_Del1 -priority 120 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_VPN_Password2_text -priority 130 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_VPN_Password1_text -priority 140 -gotoPriorityExpression NEXT -type RESPONSE
bind vpn vserver NetScalerGatewayVserver -policy RePol_VPN_Insert_pinsafe.js -priority 150 -gotoPriorityExpression NEXT -type RESPONSE
\Bind Responder Policies to VPN Vserver
bind vpn vserver NetScalerGatewayVserver -policy ResPol_ShowTuring -priority 100 -gotoPriorityExpression END -type REQUEST
This is really helpful as I have actually been trying to do this as well, however I am unable to get the turing image to display. According to the code varImg is null.
Any tips would be appreciated.
I would recommend getting in touch with Swivel as according to them, this cannot be done…
Hi Stuart,
Thanks for reading and for your comment.
I had actually passed this to the Swivel consultant who was on site and demonstrated it working to them so they should be aware but potatnially this information hasn’t made it’s way to the people you’ve been speaking to.
varImg is in the pinsafe.js javascript which is delivered via responder – so where in your index.html calls /vpn/pinsafe.js NetScaler Responder will respond with the function showTuring which was lifted directly from Swivels own code – it’s worth checking this against the pinsafe.js that is relevant to the version of swivel you have installed in case it’s changed.
add responder action ResAct_ShowTuring respondwith “”function showTuring() {ntsUrl=\”https:///proxy/SCImage?username=\”;ntsUser = document.getElementsByName(\”login\”)[0].value;” + “ntif (sUser==\”\”) {nttalert (\”Please enter your username first!\”);nttdocument.getElementsByName(\”login\”)[0].focus();nt}” + ” else” + ” {n ntt//Find the image using Mozilla compatible pproach…nttvarImg = document.getElementById(\”imgTuring\”);n ” + “ntt//Set the image SRC and make it visiblenttvarImg.src = sUrl + sUser + \”&random=\” + Math.round(Math.random()*100000);” + “n nttvarImg.style.visibility = \”visible\”;nt}n n}””
add responder policy ResPol_ShowTuring “HTTP.REQ.URL.EQ(“/vpn/pinsafe.js”)” ResAct_ShowTuring
The best way to test this is to goto https:///vpn/pinsafe.js to ensure this is working and check the pinsafe.js contents
if you want drop my your NetScaler Gateway external FQDN and I’d be happy to take a look (stuart@stuartc.net)
Thanks,
Stuart