photo by geralt (pixabay.com)
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.
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 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 192.168.200.100 80 -cltTimeout 180 -AuthenticationHost aaa.training.lab -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 192.168.13.254 TCP 22
(192.168.13.254 is the IP address of the SSL server)
creating the lb-vServer
add lb vserver lb_vsrv_tcp_ssh TCP 192.168.0.1 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!
very inventive solution 🙂
I do have one question related to packet flow. If user (probably some administrator) wants to start putty towards service protected by NS. First it should probably hit CS with AAA bound to it, right? AAA and CS in your case are both HTTP 80. How do you redirect traffic from HTTP based CS to TCP based service once authenticated as you cannot have TCP LB behind HTTP CS (or there is some ‘secret magic ingredient in the recipe’?
Thank you very much,
Great to see you like it! I don’t. This is something, the admin has to do himself (it’s mainly for Admins).
So (s)he has to go to the logon point via SSL, log on, then start putty and connect. I would agree, this is not possible for Jennifer Superblond-Secretary, however, something a helpdesk guy would be able to do easily.