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.11.6KViews0likes0CommentsExtract 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 extract the STA ticekt information from the client's ICA proxy request. the iRule will then force a sideband call to a local virtual server which is responsible for validating the STA ticket with the STA server. How to use this snippet: See DC Article "Solution for Citrix Optimal Gateway Routing" for implementation. Code : ## ## Extractor iRule ## To enable detailed iRule debugging, set the static::debug_sta_extr variable in the RULE_INIT event to 1 ## Updated July 14, 2021 by b.otlin@f5.com ## when RULE_INIT { set static::debug_sta_extr 0 } #collect TLS data for evaluation when CLIENTSSL_HANDSHAKE { SSL::collect } when CLIENTSSL_DATA { set data [SSL::payload] # Look for specific Session Reliability CGP payload; Pre-amble is hex 1A followed by ASCII encoded CGP/01 # Look for specific non-Session Reliability ICA Payload; Pre-amble is hex 05 01 00 03 # ICA ticket info follows these pre-ambles if { $data starts_with "\x1ACGP/01" || $data starts_with "\x05\x01\x00\x03"} { regexp -line {;([\d\w;]*)} $data -> ticket if { $static::debug_sta_extr && $data starts_with "\x1ACGP/01" } { log local0. "CGP with SR ticket is $ticket" } if { $static::debug_sta_extr && $data starts_with "\x05\x01\x00\x03" } { log local0. "ICA without SR ticket is $ticket" } if { [string length $ticket] > 0 } { # create ticket variable from CGP or ICA payload set ticket [string trimleft $ticket ";"] # make sideband call to resolver VS # resolver VS gets a synthetic ICA download and then performs STA validation set conn [connect "sta-resolver-vs"] send $conn "GET /f5apm/ctx-sta?$ticket HTTP/1.0\r\nHost: APM\r\n\r\n" recv -eol $conn close $conn } } SSL::release SSL::collect } Tested this on version: 15.11KViews0likes0Comments