Citrix ADC (NetScaler) 13: Pre-authenticating to TCP based services

photo by geralt (

last update: January 5th 2020

Recently I had to find a solution to block all connections to a TCP based service (SSH, TCP port 22), except of connections from IP addresses that pr-eauthenticated using a AAA vServer. This is something, most firewalls can do, but a Citrix ADC / NetScaler can’t.

Ok, it can do, or would you think, I’ll write a blog about me failing? (cross fingers: I never fail 🙂 ) So: this is something, most firewalls can do, and a NetScaler also can do. Unfortunately it’s not a built in functionality.

The idea

There is nothing easier than dropping connection attempts: We need a responder policy, action DROP. Why drop? Because it’s silent. A reset would be something you would see doing a port scan. A drop not. You could tell me, it’s security by obscurity. My answer would be: Why not?

An other thing about drop: They don’t drop existing connections, instead they simply drop all TCP SYNC packets coming in. Therefore, an existing connection is not affected by a drop policy.

So we have a responder policy dropping all connection attempts to a certain vServer on our Citrix ADC / NetScaler. But we also need to be able to allow connections, if a user authenticated successfully. That’s a bit more difficult. I spent some time thinking about that subject. My solution: I could store the IP address of a user in a Citrix ADC / NetScaler variable. If someone connects, the responder policy would check, weather this variable contains this user’s IP address, or not.

The server used for SSH would be a Citrix ADC / NetScaler lb-vServer, the server used for authentication would be a Citrix ADC / NetScaler CS vServer with an AAA bound to it. This article is not about the AAA part, it’s just about opening the port 🙂

Some built in services, like RDP, don’t allow using responder policies. You have to use TCP instead.

1st part of the solution: Seting the variable

The cs-vServer

The cs-vServer is quite simple, it may be either of type HTTP or SSL. Mine is HTTP, as it is easier to debug. I could have used a lb-vServer instead, but different to cs-vServers, lb-vServers are down if you don’t bind services to it.

add cs vserver cs_vsrv_openPort HTTP 80 -cltTimeout 180 -AuthenticationHost -Authentication ON -authnVsName aaa_vServer

Creating the Citrix ADC / NetScaler variable

add ns variable VAR_DONT_BLOCK -type ulong -expires 120
This will create the variable giving 120 seconds (2 minutes) of time to users to start the application. Of course you may change this timeout value, depending on your needs.

Assigning an IP address to this variable

add ns assignment assign_IP -variable "$VAR_DONT_BLOCK" -set CLIENT.IP.SRC

This will assign client’s IP to this variable.

The Citrix ADC / NetScaler responder policy

add responder policy res_pol_openFirewall true assign_IP
The point of this policy seems obvious: Its action is the assignment, so the variable gets assigned as soon as this policy is hit.

Binding the policy

bind lb vserver cs_vsrv_openPort -policyName res_pol_openFirewall -priority 100 -gotoPriorityExpression END -type REQUEST

The SSH vServer you want togrant access too

creating the service

add service svc_tcp_ssh TCP 22
( is the IP address of the SSL server)

creating the lb-vServer

add lb vserver lb_vsrv_tcp_ssh TCP 22
bind lb vserver lb_vsrv_tcp_ssh svc_tcp_ssh

The responder policy

add responder policy res_pol_drop_unauthenticated "CLIENT.IP.SRC.EQ($VAR_DONT_BLOCK).NOT" DROP
A Responder policy with action drop won’t kill an existing connection, instead it won’t allow new connections. Therefore we can use the built in action drop. The condition is hit, if client’s IP address is not stored in our variable.

bind lb vserver lb_vsrv_tcp_ssh -policyName res_pol_drop_unauthenticated -priority 100 -gotoPriorityExpression END -type REQUEST
binds our policy to the Citrix ADC / NetScaler vServer.

Remaining problems (Problems I didn’t have to solve)

My solution allows just 1 client to log on at a time. This was sufficient as my customer just wanted to grant administrative access from outside. (However it would allow an unlimited ammount of established connections)
Instead of using a variable I could have used an array of variables. This would have allowed to store several IPs.

An other drawack: There is no visible success message.

Feel free to post your thoughts and concerns. I’d be happy to answer your questions!

Leave a Reply

Your email address will not be published. Required fields are marked *