Resolve Citrix Secure Ticket Authority (STA)
Problem this snippet solves:
Optimal Gateway Routing (OGR) for Citrix Storefront is a design whereby a Citrix web client is directed to an ICA Proxy Gateway (ICA-GW) anywhere in the world that is closest to the app/desktop hosting environment (XenApp and XenDesktop servers) which may not be on the same Citrix StoreFront ADC (NetScaler) Gateway (SF-GW) which has authenticated the user. This is in contrast to being directed to a single ADC Gateway device that hosts SF-GW and ICA-GW. In a Citrix ADC deployment, the ICA-GW (not the SF-GW) is responsible for validating/resolving the STA ticket provided by a Secure Ticket Authority (STA) server. Since the ICA-GW is responsible for this validation, it allows OGR to function and send ICA traffic to a different ICA-GW than what was used to download the ICA file from StoreFront.
This iRule will be used to resolve/validate the STA ticket which has already been extracted from the client's ICA proxy request.
How to use this snippet:
See DC Article "Solution for Citrix Optimal Gateway Routing" for implementation.
Code :
## ## Resolver iRule ## To enable detailed iRule debugging, set the static::debug_sta_rslv variable in the RULE_INIT event to 1 ## Updated July 14, 2021 by b.otlin@f5.com ## when RULE_INIT { set static::debug_sta_rslv 0 } when HTTP_REQUEST { if { [HTTP::has_responded] } { if { $static::debug_sta_rslv } { log local0. "HTTP::has_responded" } return } else { if { $static::debug_sta_rslv } { log local0. "HTTP::has NOT responded" } } set sta_request [expr {[HTTP::path] == "/f5apm/ctx-sta"}] if { $static::debug_sta_rslv } { log local0. "req is [HTTP::uri]" } if { $static::debug_sta_rslv } { log local0. "sta req is $sta_request" } if {!$sta_request} { # exit event if the request was not a STA request if { $static::debug_sta_rslv } { log local0. "sta_request does NOT exist, exit event" } return } else { if { $static::debug_sta_rslv } { log local0. "sta_request does exist, continue" } } sharedvar internal_ica_file_request if { [info exists internal_ica_file_request] } { if { $static::debug_sta_rslv } { log local0. "internal_ica_file_request exists...return 200 ok to SF APM with mod ICA info" } HTTP::respond 200 content \ "\[ApplicationServers\]\nApp=\n\[App\]\nAddress=;[HTTP::query]" \ "Content-Type" "application/x-ica" if { $static::debug_sta_rslv } { log local0. "unset internal_ica_file_request and exit event" } unset internal_ica_file_request return } else { if { $static::debug_sta_rslv } { log local0. "internal_ica_file_request does NOT exist, continue" } } if { [info exists sta_request_sid] } { if { $static::debug_sta_rslv } { log local0. "sta_request_sid is $sta_request_sid" } if { $static::debug_sta_rslv } { log local0. "insert MRHSession cookie = $sta_request_sid and X-F5-Client header" } HTTP::header insert \ "Cookie" "MRHSession=$sta_request_sid" \ "X-F5-Client" "citrix-launch" VDI::enable if { $static::debug_sta_rslv } { log local0. "enable VDI" } set internal_ica_file_request 1 if { $static::debug_sta_rslv } { log local0. "internal_ica_file_request set to 1" } SSL::disable serverside if { $static::debug_sta_rslv } { log local0. "disable SSL serverside" } virtual [virtual name] if { $static::debug_sta_rslv } { log local0. "go back to same VS" } } else { if { $static::debug_sta_rslv } { log local0. "sta_request_sid does NOT exist" } HTTP::header insert "clientless-mode" "1" if { $static::debug_sta_rslv } { log local0. "insert clientless-mode header" } } } when HTTP_RESPONSE { if { [HTTP::payload] contains "Address=;" } { if { $static::debug_sta_rslv } { log local0. "Response payload contains Address=; which denotes an ICA File" } set sta_address [HTTP::payload] regexp -line {^(?:[^;]*;){2}([^;]*)} $sta_address -> sta1 regexp -line {^(?:[^;]*;){4}([^;]*)} $sta_address -> sta2 set STA1 [class match -value -- $sta1 equals sta_dg] if { [info exists sta2] } { set STA2 [class match -value -- $sta2 equals sta_dg] ACCESS::session data set session.citrix.sta_servers "$STA1;$STA2" if { $static::debug_sta_rslv } { log local0. "STA from SF is $sta1 and $sta2 resolved to FQDN: $STA1 and $STA2 and adding to access session table" } } else { if { $static::debug_sta_rslv } { log local0. "STA from SF is $sta1 resolved to FQDN: $STA1 and adding to access session table" } ACCESS::session data set session.citrix.sta_servers "$STA1" } } else { if { $static::debug_sta_rslv } { log local0. "not an ICA" } } } when HTTP_RESPONSE_RELEASE { if { [HTTP::has_responded] } { if { $static::debug_sta_rslv } { log local0. "HTTP has_responded...exit event" } return } if {!$sta_request} { if { $static::debug_sta_rslv } { log local0. "sta_request does NOT exist...exit event" } return } if { [HTTP::status] == 200 } { if { $static::debug_sta_rslv } { log local0. "HTTP status is 200, remove Set-Cookie header" } # There is no need to expose this SID HTTP::header remove Set-Cookie } else { if { $static::debug_sta_rslv } { log local0. " HTTP status is NOT 200, remove access session" } # Remove session on failed STA resolution ACCESS::session remove } } when ACCESS_SESSION_STARTED { if { !$sta_request } { if { $static::debug_sta_rslv } { log local0. "sta_request does NOT exist...exit event" } return } else { if { $static::debug_sta_rslv } { log local0. "sta_request exists...continue" } } if { $static::debug_sta_rslv } { log local0. "sta_request exists so set 'session.external_sta_ticket' to 1" } ACCESS::session data set "session.external_sta_ticket" "1" } when ACCESS_POLICY_COMPLETED { if { !$sta_request } { if { $static::debug_sta_rslv } { log local0. "sta_request does NOT exist...exit event" } return } else { if { $static::debug_sta_rslv } { log local0. "sta_request exists...continue" } } set sta_request_sid [ACCESS::session sid] if { $static::debug_sta_rslv } { log local0. "sta_request_sid is $sta_request_sid" } }
Tested this on version:
15.1