In this post, I’ll detail the configuration for Netscaler ADC Load Balancer when it’s deployed for the purposes of load balancing multiple ISE nodes
There’s already a great post by Brad Johnson available here: https://community.cisco.com/t5/security-knowledge-base/citrix-netscaler-cli-configuration-for-cisco-ise-radius-and/ta-p/4679861.
I will expand on that configuration and add screenshots on how things are configured through the GUI.
Topology
I’m going to cover the most basic topology. That is ISE nodes being behind the Load Balancer configured in Layer 3 mode. ISE nodes use the Load Balancer IP address as the default gateway.
Lab Testing
Netscaler provides a low bandwidth free version of ADC Load Balancer in AWS. I’ve created a terraform template to deploy ADC along with a set of FreeRADIUS servers to allow for learning and testing Netscaler configuration. The RADIUS servers are provisioned with username cisco and password cisco.
The template can be found here: https://github.com/vbobrov/terraform/tree/main/aws/freeradius-netscaler
The following topology is deployed:
ISE LB Best Practices
There are many detailed guides and forum discussions on this topic and I won’t cover all those details here. Be sure to review this document that goes into great details about LB and ISE requirements: https://community.cisco.com/t5/security-knowledge-base/how-to-cisco-amp-f5-deployment-guide-ise-load-balancing-using/ta-p/3631159
To summarize, these are the base requirements:
- ISE must see the original IP addresses of network devices
- To satisfy the previous requirement, LB must be deployed inline between network devices and ISE. In other words, return traffic from ISE to the network devices must flow through the LB.
- For RADIUS ports, persistence and LB should be done based on RADIUS Calling-Station-Id attribute rather than Source IP. Source IP should be the backup to Calling-Station-ID
- RADIUS Authentication (udp/1812) and RADIUS Accounting (udp/1813) for the same Calling-Station-Id should be forwarded to the same ISE node behind the LB.
- Service monitoring should be done using RADIUS polling, not ICMP or TCP. This will correctly detect when RADIUS server on ISE is ready
- CoA (udp/1700) traffic initiated from ISE towards network devices should be NAT’d to the IP address of the LB VIP
Netscaler Configuration
Source IP Mode
In order for ISE to properly handle RADIUS and TACACS requests from network devices
By default, ADC performs source NAT on the incoming requests. When these requests get to the nodes behind it, they’re NAT’d to the IP address of Load Balancer itself.
This can be enabled either globally or per-service. This link describes how to enabled this capability: https://docs.netscaler.com/en-us/citrix-adc/current-release/networking/ip-addressing/enabling-use-source-ip-mode.html
Globally, this can be enabled in the GUI or via CLI.
Per-service configuration will be covered later in this document.
CLI Configuration
enable ns mode USIP
Radius Monitor
ADC natively supports RADIUS monitor that provides the ability to send synthetic RADIUS request to ISE nodes to check their status.
To add the monitor from the GUI navigate to Monitors and click Add
When filling out the form, be sure to specify 1812 as the destination port since we will apply the same monitor to RADIUS Accounting (1813) as well.
The configuration below looks for Access-Accept (Code 2). It is also possible to configured it to consider Access-Reject (Code 3) as well. Both codes can be specified at the same time.
CLI Configuration
add lb monitor radius-cisco RADIUS -respCode 2 -userName cisco -password <password> -radKey <secret> -LRTM DISABLED -destPort 1812
Add Servers
ADC refers individual hosts serving requests as Servers. Servers are added from a menu option of the same name
Using CLI
add server radius-1 10.1.1.13
add server radius-2 10.1.1.9
add server radius-3 10.1.1.119
add server radius-4 10.1.1.100
Add Service Groups
Service groups are added from the menu of the same name.
One service group is needed for each service: udp/1812, udp/1813 and tcp/49.
Once the service group is added, we add group members which are the servers defined in a previous step.
In order for ISE to see the original Network Device IP, we need to enable Use Client IP option
Next, we add a attach the Monitor to the group
Follow the same steps for udp/1813 and tcp/49. The following are screenshots for TACACS.
Use the same RADIUS monitor for TACACS as well
CLI Commands
add serviceGroup radius-auth RADIUS -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport NO -cltTimeout 120 -svrTimeout 120 -CKA NO -TCPB NO -CMP NO
add serviceGroup radius-acct RADIUS -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport NO -cltTimeout 120 -svrTimeout 120 -CKA NO -TCPB NO -CMP NO
add serviceGroup tacacs TCP -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport YES -cltTimeout 9000 -svrTimeout 9000 -CKA NO -TCPB NO -CMP NO
bind serviceGroup radius-auth radius-4 1812
bind serviceGroup radius-auth radius-3 1812
bind serviceGroup radius-auth radius-2 1812
bind serviceGroup radius-auth radius-1 1812
bind serviceGroup radius-auth -monitorName radius-cisco
bind serviceGroup radius-acct radius-4 1813
bind serviceGroup radius-acct radius-3 1813
bind serviceGroup radius-acct radius-2 1813
bind serviceGroup radius-acct radius-1 1813
bind serviceGroup radius-acct -monitorName radius-cisco
bind serviceGroup tacacs radius-4 49
bind serviceGroup tacacs radius-3 49
bind serviceGroup tacacs radius-2 49
bind serviceGroup tacacs radius-1 49
bind serviceGroup tacacs -monitorName radius-cisco
Perstence Groups
Persistence groups will configure the Load Balancer to send udp/1812 and udp/1813 traffic from the same Calling-Station-ID to the same ISE node.
For persistence, we use the following rule: CLIENT.UDP.RADIUS.ATTR_TYPE(31)
Virtual Servers
Next, we create Virtual Servers and attach them to Service Groups
For persistence, we use the same rule: CLIENT.UDP.RADIUS.ATTR_TYPE(31)
Repeat the same process for udp/1813 and tcp/49. The following are screenshots for TACACS.
For TACACS, persistence is based on source IP address
CLI Configuration
add lb vserver radius-auth RADIUS 10.2.1.1 1812 -rule "CLIENT.UDP.RADIUS.ATTR_TYPE(31)" -cltTimeout 120
add lb vserver radius-acct RADIUS 10.2.1.1 1813 -rule "CLIENT.UDP.RADIUS.ATTR_TYPE(31)" -cltTimeout 120
add lb vserver tacacs TCP 10.2.1.1 49 -persistenceType SOURCEIP -cltTimeout 9000
bind lb vserver radius-auth radius-auth
bind lb vserver radius-acct radius-acct
bind lb vserver tacacs tacacs
CoA NAT
Best practice for Load Balancers is to NAT CoA (udp/1700) traffic to the IP Address of the VIP.
We will create a rule to NAT udp/1700 traffic from specific ISE nodes only. It is also possible to create a rule for all udp/1700.
First step is to create a Data Set listing out ISE node IP addresses.
To perform NAT on ADC, we first need to define an ACL to match udp/1700 traffic. ACLs are created from System \ Network \ ACLs. We will create an extended ACL
When ACLs are created on ADC, they need to be applied to be activated.
Next, we add a NAT rule under System \ Network \ NATs \ RNAT
CLI Configuration
add policy dataset radius_servers ipv4
bind policy dataset radius_servers 10.1.1.13 -index 1
bind policy dataset radius_servers 10.1.1.9 -index 2
bind policy dataset radius_servers 10.1.1.119 -index 3
bind policy dataset radius_servers 10.1.1.100 -index 4
add ns acl CoA ALLOW -srcIP = radius_servers -srcPort = 1-65535 -destPort = 1700 -protocol UDP -priority 10
apply ns acls
add rnat coa-nat radius-coa
bind rnat coa-nat 10.2.1.1
Full CLI Configuration
# Add RADIUS Servers
add server radius-1 10.1.1.13
add server radius-2 10.1.1.9
add server radius-3 10.1.1.119
add server radius-4 10.1.1.100
# Add Service Groups
add serviceGroup radius-auth RADIUS -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport NO -cltTimeout 120 -svrTimeout 120 -CKA NO -TCPB NO -CMP NO
add serviceGroup radius-acct RADIUS -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport NO -cltTimeout 120 -svrTimeout 120 -CKA NO -TCPB NO -CMP NO
add serviceGroup tacacs TCP -maxClient 0 -maxReq 0 -cip DISABLED -usip YES -useproxyport YES -cltTimeout 9000 -svrTimeout 9000 -CKA NO -TCPB NO -CMP NO
# Add Vservers
add lb vserver radius-auth RADIUS 10.2.1.1 1812 -rule "CLIENT.UDP.RADIUS.ATTR_TYPE(31)" -cltTimeout 120
add lb vserver radius-acct RADIUS 10.2.1.1 1813 -rule "CLIENT.UDP.RADIUS.ATTR_TYPE(31)" -cltTimeout 120
add lb vserver tacacs TCP 10.2.1.1 49 -persistenceType SOURCEIP -cltTimeout 1800
# Add RADIUS Monitor
add lb monitor radius-cisco RADIUS -respCode 2 -userName cisco -password cisco -radKey cisco -LRTM DISABLED -destPort 1812
# Bind Vservers to Service Groups
bind lb vserver radius-auth radius-auth
bind lb vserver radius-acct radius-acct
bind lb vserver tacacs tacacs
# Add Persistency Group
add lb group radius -persistenceType RULE -persistenceBackup SOURCEIP -rule "CLIENT.UDP.RADIUS.ATTR_TYPE(31)"
# Bind Vservers to Persistency Group
bind lb group radius radius-auth
bind lb group radius radius-acct
# Bind RADIUS Servers to Service Groups
bind serviceGroup radius-auth radius-4 1812
bind serviceGroup radius-auth radius-3 1812
bind serviceGroup radius-auth radius-2 1812
bind serviceGroup radius-auth radius-1 1812
bind serviceGroup radius-acct radius-4 1813
bind serviceGroup radius-acct radius-3 1813
bind serviceGroup radius-acct radius-2 1813
bind serviceGroup radius-acct radius-1 1813
bind serviceGroup tacacs radius-4 49
bind serviceGroup tacacs radius-3 49
bind serviceGroup tacacs radius-2 49
bind serviceGroup tacacs radius-1 49
# Bind RADIUS Monitor to Service Groups
bind serviceGroup radius-auth -monitorName radius-cisco
bind serviceGroup radius-acct -monitorName radius-cisco
bind serviceGroup tacacs -monitorName radius-cisco
# Create dataset
add policy dataset radius_servers ipv4
bind policy dataset radius_servers 10.1.1.13 -index 1
bind policy dataset radius_servers 10.1.1.9 -index 2
bind policy dataset radius_servers 10.1.1.119 -index 3
bind policy dataset radius_servers 10.1.1.100 -index 4
# Create COA ACL
add ns acl radius-coa ALLOW -srcIP = radius_servers -srcPort = 1-65535 -destPort = 1700 -protocol UDP -priority 10 -kernelstate SFAPPLIED61
apply ns acls
# Create CoA NAT
add rnat coa-nat radius-coa
bind rnat coa-nat 10.2.1.1
Monitoring and Troubleshooting
Connection Table
To view active connections, click on TCP/IP Connections in System \ Network.
It is possible to filter connections using Expression Builder
As I’m writing this, the connection table does not show Load Balanced UDP connections including RADIUS.
CLI Command
> show connectiontable CONNECTION.PORT.EQ(49)
SRCIP SRCPORT DSTIP DSTPORT SVCTYPE IDLTIME STATE Traffic Domain
10.1.2.89 41184 10.2.1.1 49 TCP 181 ESTABLISHED 0 C
10.1.2.89 5329 10.1.1.119 49 TCP 181 ESTABLISHED 0 S
Persistence Table
The table can be viewed from Traffic Management section.
The following example shows two separate sessions based on the MAC address going to two different ISE nodes. Note that Virtual Server is set to the name of the Persistence Group and not individual services
To clear persistence records, click the Clear link shown on the screenshot above and select which records to clear.
CLI Command
> show persistentSessions radius
Type SRC-IP DST-IP PORT VSNAME TIMEOUT PERSISTENCE-PARAMETER
RULE * 10.1.1.13 0 radius 54 11-22-33-44-55-66
RULE * 10.1.1.9 0 radius 51 11-22-33-44-55-77
> show persistentSessions tacacs
Type SRC-IP DST-IP PORT VSNAME TIMEOUT PERSISTENCE-PARAMETER
SOURCEIP 10.1.2.89 10.1.1.9 49 tacacs 87 10.1.2.89
> clear persistentSessions radius
Done
Packet Capture
Packet capture facility is available under System \ Diagnostics
Filter can be defined using Expression Editor
Once the capture is started, a confirmation is displayed
The following screen shows status of the trace
Once completed, we can download the pcap file
The capture contains packets from both internal and external interface and wireshark sees them as duplicates
Captures can also be saved in NSTRACE format which includes additional ADC specific information.
The following commands can be used to start trace from CLI. Once completed, the traces can be download via SCP from /var/nstrace.
> start nstrace -filter "CONNECTION.PORT.EQ(1812)" -size 1500 -traceformat PCAP -fileName radius
Done
> show nstrace
State: RUNNING Scope: LOCAL TraceLocation: "/var/nstrace/16Nov2024_04_16_35/..." Nf: 24 Time: 3600
Size: 164 Mode: TXB NEW_RX Traceformat: NSCAP PerNIC: DISABLED FileName: 16Nov2024_04_16_35 Filter: "CONNECTION.PORT.EQ(1812)"
Link: DISABLED Merge: ONSTOP Doruntimecleanup: ENABLED TraceBuffers: 5000 SkipRPC: DISABLED SkipLocalSSH: DISABLED
Capsslkeys: DISABLED Capdroppkt: DISABLED InMemoryTrace: DISABLED
Done
> stop nstrace
Done
>
Unix Shell
To access Unix Shell, enter the following in CLI.
> shell
root@ns#
It is possible to run tcpdump from command line. Note that you must use this shell script and not tcpdump itself.
root@ns# nstcpdump.sh host 10.1.2.89
reading from file -, link-type EN10MB (Ethernet), snapshot length 65535
04:27:03.396185 IP 10.1.2.89.46398 > 10.2.1.1.1812: RADIUS, Access-Request (1), id: 0xb8 length: 82
04:27:03.396248 IP 10.1.2.89.46398 > 10.1.1.13.1812: RADIUS, Access-Request (1), id: 0xb8 length: 82
04:27:03.397179 IP 10.1.1.13.1812 > 10.1.2.89.46398: RADIUS, Access-Accept (2), id: 0xb8 length: 38
04:27:03.397185 IP 10.2.1.1.1812 > 10.1.2.89.46398: RADIUS, Access-Accept (2), id: 0xb8 length: 38