citrix
9 TopicsCitrix Netscaler to F5 BIG-IP
Problem this snippet solves: This scripts is built to convert Citrix Netscaler text based configuration files to BIG-IP commands. This scripts aim to reduce the largest burden of entering object names, IP addresses and other parameters, as well as logically linking these objects to each other. This script is not meant to perform a totally automated and unattended migration. For the objects that the script migrates, not all parameters may be converted. For the parameters that are converted, some are mapped to the closest matching BIG-IP feature. If there are any non-ASCII characters or line-breaks in the source file, this will need to be manually fixed first (some screen captures may wrap lines so copying the file directly is preferred). All Netscaler commands are on a single line. Note that this script will produce a comprehensive list of errors, warnings, notes, etc, at the bottom of the output file. You should look through these first, starting with the errors and working your way down to less critical messages. You may need to correct problems in the input file or in the Perl script and re-run the conversion. Once you have reviewed the warnings and notes, you should look through the configuration that was generated. The original Netscaler commands are provided for each portion of the BIG-IP configuration and you should compare the "before and after" for each object. How to use this snippet: To get command line config execute the following command on Netscaler: more /nsconfig/ns.conf (Or you can secure copy it to your PC using something like winscp/pscp.) Input restrictions: Probably only supports Netscaler v7, v8, and v9 Output restrictions: Outputs BIG-IP 9.4.x format (which v10 appears to read fine) Output file contains warnings errors, base config, and main bigip.conf It is best to have Active Perl on you PC to perform the conversions. You can also use the Perl installation on the BIG-IP command line. Once the file is converted you can import the configuration into the BigIP using (b load, or b merge). You can also copy/paste into bpsh. Read the header of each of the scripts files, they have addition information on the usage of the scripts. Usage: perl nsv8_to_f5.pl netscalerconfigfile /var/tmp/bigipoutputfile5.3KViews0likes7CommentsResolve 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.1999Views0likes0CommentsCitrix VDI iApp template
Problem this snippet solves: You can use this F5 supported iApp template to configure availability and Secure ICA proxy remote access for Citrix XenApp or XenDesktop environments. This iApp template configures BIG-IP LTM, APM, and AFM for XenApp or XenDesktop services. When used with BIG-IP APM, this iApp template supports proxy authentication and secure remote access for all XenApp and XenDesktop HTTP-based protocols without requiring a VPN client. The iApp template includes the ability to configure BIG-IP APM for two factor authentication with RSA SecurID, and supports smart card authentication. The link below takes you to the official AskF5 SOL for Citrix VDI with instructions on downloading and using the iApp template. For the Deployment Guide, see http://www.f5.com/pdf/deployment-guides/citrix-vdi-iapp-dg.pdf Code : https://support.f5.com/kb/en-us/solutions/public/13000/700/sol13738.html641Views0likes0CommentsF5 StoreFront XML Broker Monitor
Problem this snippet solves: I successfully was able to manually build a monitor outside of the iApp for Citrix Storefront deployment, that is a little more complex than the original one. How to use this snippet: Just copy and past this into your send string for your monitor. There are a few things that will have to be adjusted, such as the host, the username, and password, and content-length. To find the proper content length, plug in your information that is necessary. Then take the characters between the '<?xml version' and '</NFuseProtocol>' and past them into a text editor. Remove all escape characters such as "\". Highlight the string and if your text editor has the option, it will show you the character count that you selected. This is your new content-length. If the code is executed correctly you will see a list of published Apps, such as Notepad. If you wanted to have a monitor that didn't pass the username and password in clear text, you will need to do so in an external monitor. In my use case, the user permissions were locked down just enough to make the monitor work and that is all. Code : POST /scripts/wpnbr.dll HTTP/1.1\r\nContent-Length: 492\r\nContent-Type: text/xml\r\nConnection: close\r\nHost: hostname\r\n\r\n permissions all ica30 content user password domain Tested this on version: 13.0436Views3likes0CommentsCitrix_APM_ Helper
Problem this snippet solves: With the combination of BIG-IP Access Policy Manager (APM) and Citrix XenApp, organizations can deliver a complete remote access solution that allows for scalability, security, compliance and flexibility. This iRule comprises the second part of two iRules that are part of the Citrix Secure Access deployment. The complete installation instructions for this setup will be located in the solutions section of F5.com shortly. With this iRule, users can connect using BIG-IP APM's secure proxy mode, allowing for connections to XenApp from a variety of clients including Citrix Receiver, PN Agent and Dazzle. Code : when CLIENT_ACCEPTED { TCP::collect 7 } when CLIENT_DATA { if { [TCP::payload 7] equals "CONNECT" } { SSL::disable } TCP::release } when HTTP_REQUEST { if { [HTTP::method] equals "CONNECT" } { set authstr [HTTP::header Proxy-Authorization] set authstr [lindex [ split $authstr " " ] 1 ] set remainder [ lindex [split [expr [string length $authstr] / 4.0 ] "." ] 1 ] if { $remainder != "0" } { if { [regsub -all {(A=)} $authstr = newstring] > 0 } { set authstr $newstring } } set authstr [b64decode $authstr] set tmm_apm_disp_sess_id [string range [lindex [split $authstr ":"] 0] 24 32] set authstr [ lindex [ split $authstr ":" ] 1 ] set ipport [split $authstr "-" ] set ip [ lindex $ipport 0 ] set port [ lindex $ipport 1 ] if { [matchclass CitrixAppServers equals "$ip-$port"] == 0 } { log -noname accesscontrol.local1.error "01490000 reject return } node $ip $port TCP::respond "HTTP/1.0 200 Connection established\r\n\r\n" HTTP::disable discard SSL::enable SSL::collect } } when CLIENTSSL_DATA { binary scan [SSL::payload] c socksver if { $socksver != 5 } { SSL::release return } SSL::payload replace 0 [SSL::payload length] {} if { [info exists socks_connect] } { SSL::respond [binary format H2H2H2H2H2H2H2H2H2H2 05 00 00 01 00 00 00 00 00 00] SSL::release } else { set socks_connect {} SSL::respond [binary format H2H2 05 00] SSL::collect } }408Views0likes0CommentsCitrix-APM New91
Problem this snippet solves: With the combination of BIG-IP Access Policy Manager (APM) and Citrix XenApp, organizations can deliver a complete remote access solution that allows for scalability, security, compliance and flexibility. This iRule comprises the first part of two iRules that are part of the Citrix Secure Access deployment. The complete installation instructions for this setup will be located in the solutions section of F5.com shortly. With this iRule, users can connect using BIG-IP APM's secure proxy mode, allowing for connections to XenApp from a variety of clients including Citrix Receiver, PN Agent and Dazzle. Code : when RULE_INIT { set tmm_apm_pnagent_url "/Citrix/PNAgent/config.xml" } when CLIENT_ACCEPTED { TCP::collect 7 } when CLIENT_DATA { # Disable SSL if it's HTTP CONNECT request if { [TCP::payload 7] equals "CONNECT" } { SSL::disable } TCP::release } when HTTP_REQUEST { set tmm_apm_host [HTTP::host] set tmm_apm_uri_path [HTTP::path] set tmm_apm_user_agent [HTTP::header "User-Agent"] set tmm_apm_http_method [HTTP::method] set tmm_apm_session_id "" set tmm_apm_citrix_receiver 0 set tmm_apm_citrix_pnagent 0 set tmm_apm_citrix_ica_patching 0 set tmm_apm_vip "$tmm_apm_host:[TCP::local_port clientside]" log -noname accesscontrol.local1.debug "01490000 if { [HTTP::cookie exists "MRHSession"] } { set tmm_apm_session_id [HTTP::cookie "MRHSession"] } if { $tmm_apm_user_agent contains "CitrixReceiver" } { set tmm_apm_citrix_receiver 1 } elseif { $tmm_apm_user_agent contains "PNAMAIN" or $tmm_apm_user_agent contains "Dazzle" } { set tmm_apm_citrix_pnagent 1 } if { $tmm_apm_http_method equals "CONNECT" } { # Handle the secure proxy connect requests. Return a Proxy-Authenticate header # field with a challenge if the user is not authenticated. if { ![HTTP::header exists "Proxy-Authorization"] } { HTTP::respond 407 Proxy-Authenticate "Basic realm=\"123\"" return } set authstr [lindex [ split [HTTP::header "Proxy-Authorization"] " " ] 1 ] # Seems like the Citrix base64 encoding logic has a bug that terminates # the input string with a null byte when the extra padding characters are # added. We remove the extra null character before we decode it. set remainder [lindex [split [expr [string length $authstr] / 4.0 ] "." ] 1] if { $remainder != "0" } { if { [regsub -all {(A=)} $authstr = newstring] > 0 } { set authstr $newstring } } #Decoded string format: 52553eb5b18572cdbe7dda4a8220bf35:172.30.6.197-1494 set apm_session [ lindex [ split [b64decode $authstr] ":" ] 0 ] if { ![ACCESS::session exists $apm_session] } { HTTP::respond 407 Proxy-Authenticate "Basic realm=\"123\"" return } # User is authenticated, send the traffic to the connect proxy virtual. log -noname accesscontrol.local1.notice "01490000 ACCESS::disable use virtual citrix_connect_proxy } if { ($tmm_apm_session_id == "") && ($tmm_apm_citrix_pnagent == 1) } { if { $tmm_apm_uri_path equals $::tmm_apm_pnagent_url } { ACCESS::disable return } # If the client is PNAgent or Dazzle, extract the credentials from the # payload and insert them in HTTP headers. HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" "" HTTP::header insert "password" "" if { ![info exists tmm_apm_citrix_username] && [HTTP::header exists Content-Length] } { HTTP::collect [HTTP::header Content-Length] } } if { $tmm_apm_citrix_receiver == 1 } { # Collect the user credentials and set ready for access policy validation if { $tmm_apm_uri_path equals "/cgi/login" } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" "" HTTP::header insert "password" "" HTTP::cookie remove MRHSession HTTP::collect [HTTP::header Content-Length] } elseif { $tmm_apm_uri_path equals "/ipad" } { set AD_only "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=1" set RSA_only "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=2" set AD_RSA "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=3" HTTP::respond 200 content " Click here for domain only authClick here for RSA onlyClick here for Two-factor auth " } } } when HTTP_REQUEST_DATA { if { ($tmm_apm_citrix_pnagent != 1) && ($tmm_apm_citrix_receiver != 1) } { return } set payload [HTTP::payload] if { $tmm_apm_citrix_receiver == 1 } { # Parse the user credentials from the payload log -noname accesscontrol.local1.debug "01490000 set tmm_apm_citrix_username "" set tmm_apm_citrix_password "" set tmm_apm_citrix_password1 "" set urlvars [ split $payload "&" ] foreach {u} $urlvars { set param [ lindex [ split $u "=" ] 0 ] set value [ lindex [ split $u "=" ] 1 ] if { $param equals "login" } { set tmm_apm_citrix_username $value } elseif { $param equals "passwd" } { set tmm_apm_citrix_password $value } elseif { $param equals "passwd1" } { set tmm_apm_citrix_password1 $value } } # Insert the parsed credentials into the HTTP request as headers HTTP::header replace "username" $tmm_apm_citrix_username HTTP::header replace "password" $tmm_apm_citrix_password HTTP::release } elseif { $tmm_apm_citrix_pnagent == 1 } { # Parse the user credentials from the payload log -noname accesscontrol.local1.debug "01490000 set tmm_apm_citrix_username "" set tmm_apm_citrix_password "" if { [regexp -nocase { ([^<]+) } $payload dummy tmm_apm_citrix_username] == 0 } { log -noname accesscontrol.local1.error "01490000 return } if { [regexp -nocase { ]+>([^<]+) } $payload dummy tmm_apm_citrix_password] == 0 } { log -noname accesscontrol.local1.error "01490000 return } # Decode the password binary scan $tmm_apm_citrix_password c* pass set len [llength $pass] set result {} for { set i 0 } { $i < $len } { incr i } { set hi [lindex $pass $i] set hi [ expr { $hi - 0x41 } ] set hi [ expr { $hi << 4 } ] incr i set lo [lindex $pass $i] set lo [ expr { $lo - 0x41 } ] set char [ binary format c [expr {$hi + $lo}] ] append result $char } binary scan $result H* pass binary scan $result c* pass set len [llength $pass] set result {} set first [lindex $pass 0] set char [ binary format c [expr { $first ^ 0xA5 } ] ] append result $char for { set i 1 } { $i < $len } { incr i } { set prev [ lindex $pass [expr {$i-1}] ] set curr [ lindex $pass $i ] set char [ binary format c [ expr {$curr ^ $prev ^ 0xA5} ] ] append result $char } binary scan $result H* pass set tmm_apm_citrix_password [ regsub -all {\000} $result {} ] # Insert the parsed credentials into the HTTP request as headers HTTP::header replace "username" $tmm_apm_citrix_username HTTP::header replace "password" $tmm_apm_citrix_password HTTP::release } } when HTTP_RESPONSE { if { [HTTP::header Content-Type] contains "application/x-ica" } { set tmm_apm_citrix_ica_patching 1 HTTP::collect [HTTP::header Content-Length] } } when HTTP_RESPONSE_DATA { # ICA patching: if { $tmm_apm_citrix_ica_patching == 1 } { # ICA file patching: Add entries to point citrix clients to the # Citrix ICA patching virtual as their HTTP proxy. It also sets # the ProxyUsername to the APM session id to let the Citrix clients # to connect to the proxy without requesting the user to authenticate # again. log -noname accesscontrol.local1.debug "01490000 set payload [HTTP::payload] set payload [ regsub -all {Proxy[^\n]+\n} $payload {} ] set payload [ regsub {DoNotUseDefaultCSL[^\n]+\n} $payload {} ] if { $tmm_apm_citrix_receiver == 1 } { set payload [ regsub {CGPAddress[^\n]+\n} $payload {} ] } regexp -line {Address=(.+)} $payload dummy CtxAddrPort set CtxAddr [lindex [split $CtxAddrPort ":"] 0] set CtxPort [lindex [split $CtxAddrPort ":"] 1] regexp -line {CGPAddress=(.+)} $payload dummy CGPAddrPort if { [info exists CGPAddrPort] } { set CtxPort [lindex [split $CGPAddrPort ":"] 1] } set payload [ regsub {\[WFClient\]} $payload "&\r\nProxyType=Secure\r\nProxyHost=$tmm_apm_vip\r\nProxyUsername=$tmm_apm_session_id\r\nProxyPassword=$CtxAddr-$CtxPort" ] set payload [ regsub {SSLEnable[^\n]+\n} $payload "SSLEnable=On\r\n" ] set payload [ regsub {Address[^\n]+\n} $payload "Address=$tmm_apm_host\r\n" ] HTTP::respond 200 content $payload Content-Type [HTTP::header Content-Type] } } when ACCESS_SESSION_STARTED { if { ($tmm_apm_citrix_receiver == 0) or ![info exists tmm_apm_citrix_password1] } { return } # Pass the domain password as a session variable. Logon page agent doesn't # take it from HTTP headers in clientless mode. ACCESS::session data set "session.logon.last.password1" [URI::decode $tmm_apm_citrix_password1] } when ACCESS_POLICY_COMPLETED { if { $tmm_apm_citrix_receiver == 0 } { return } set sid [ACCESS::session data get session.keydb] set result [ACCESS::policy result] # Remove the user credential variables if { [info exists tmm_apm_citrix_username] } { unset tmm_apm_citrix_username } if { [info exists tmm_apm_citrix_password] } { unset tmm_apm_citrix_password } if { [info exists tmm_apm_citrix_password1] } { unset tmm_apm_citrix_password1 } # Clear the domain password session variable created at the session validation start. ACCESS::session data set "session.logon.last.password1" "" if { $result equals "allow" } { set resp " " ACCESS::respond 200 content $resp Set-Cookie "MRHSession=$sid;path=/;secure" Set-Cookie "NSC_AAAC=123;path=/;secure" } }278Views0likes0CommentsCitrix_APM
Problem this snippet solves: Contributed by: F5 Solutions Engineering - solutionsfeedback at f5.com ===Description=== With the combination of BIG-IP Access Policy Manager (APM) and Citrix XenApp, organizations can deliver a complete remote access solution that allows for scalability, security, compliance and flexibility. This iRule comprises the first part of two iRules that are part of the Citrix Secure Access deployment. The complete installation instructions for this setup will be located in the solutions section of F5.com shortly. With this iRule, users can connect using BIG-IP APM's secure proxy mode, allowing for connections to XenApp from a variety of clients including Citrix Receiver, PN Agent and Dazzle. Code : when RULE_INIT { set tmm_apm_pnagent_url "/Citrix/PNAgent/config.xml" } when CLIENT_ACCEPTED { TCP::collect 7 } when CLIENT_DATA { # Disable SSL if it's HTTP CONNECT request if { [TCP::payload 7] equals "CONNECT" } { SSL::disable } TCP::release } when HTTP_REQUEST { set tmm_apm_host [HTTP::host] set tmm_apm_uri_path [HTTP::path] set tmm_apm_user_agent [HTTP::header "User-Agent"] set tmm_apm_http_method [HTTP::method] set tmm_apm_session_id "" set tmm_apm_citrix_receiver 0 set tmm_apm_citrix_pnagent 0 set tmm_apm_citrix_nongateway 0 set tmm_apm_citrix_ica_patching 0 set tmm_apm_vip "$tmm_apm_host:[TCP::local_port clientside]" log -noname accesscontrol.local1.debug "01490000 if { [HTTP::cookie exists "MRHSession"] } { set tmm_apm_session_id [HTTP::cookie "MRHSession"] } if { $tmm_apm_user_agent contains "CitrixReceiver" } { set tmm_apm_citrix_receiver 1 } elseif { ($tmm_apm_user_agent contains "PNAMAIN") or ($tmm_apm_user_agent contains "PNAMain") or ($tmm_apm_user_agent contains "pnamain") or ($tmm_apm_user_agent contains "Dazzle") or ($tmm_apm_user_agent contains "Darwin") } { set tmm_apm_citrix_pnagent 1 } if { $tmm_apm_http_method equals "CONNECT" } { if { ![HTTP::header exists "Proxy-Authorization"] } { HTTP::respond 407 Proxy-Authenticate "Basic realm=\"123\"" return } set authstr [lindex [ split [HTTP::header "Proxy-Authorization"] " " ] 1 ] set remainder [lindex [split [expr [string length $authstr] / 4.0 ] "." ] 1] if { $remainder != "0" } { if { [regsub -all {(A=)} $authstr = newstring] > 0 } { set authstr $newstring } } set apm_session [ lindex [ split [b64decode $authstr] ":" ] 0 ] if { ![ACCESS::session exists $apm_session] } { HTTP::respond 407 Proxy-Authenticate "Basic realm=\"123\"" return } log -noname accesscontrol.local1.debug "01490000 ACCESS::disable use virtual citrix_connect_proxy } if { ($tmm_apm_session_id == "") && ($tmm_apm_citrix_pnagent == 1) } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" "" HTTP::header insert "password" "" if { $tmm_apm_uri_path equals $::tmm_apm_pnagent_url } { ACCESS::disable return } if { ![info exists tmm_apm_citrix_username] && [HTTP::header exists "Content-Length"] } { HTTP::collect [HTTP::header Content-Length] } } if { $tmm_apm_citrix_receiver == 1 } { if { $tmm_apm_uri_path starts_with "/Citrix/PNAgent/" } { if { $tmm_apm_uri_path equals $::tmm_apm_pnagent_url } { ACCESS::disable return } if { ![info exists tmm_apm_citrix_username] } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" "" HTTP::header insert "password" "" if { [HTTP::header exists "Content-Length"] } { set tmm_apm_citrix_receiver 0 set tmm_apm_citrix_pnagent 0 set tmm_apm_citrix_nongateway 1 HTTP::collect [HTTP::header Content-Length] } } else { HTTP::header insert "username" $tmm_apm_citrix_username HTTP::header insert "password" $tmm_apm_citrix_password } } elseif { $tmm_apm_uri_path equals "/cgi/login" } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" "" HTTP::header insert "password" "" HTTP::cookie remove MRHSession HTTP::collect [HTTP::header Content-Length] } elseif { $tmm_apm_uri_path equals "/ipad" } { set AD_only "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=1" set RSA_only "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=2" set AD_RSA "citrixreceiver://createprofile/?s=$tmm_apm_host&pname=Profile-$tmm_apm_host&gw=1&gwt=2&gwa=3" HTTP::respond 200 content " Click here for domain only authClick here for RSA onlyClick here for Two-factor auth " } } } when HTTP_REQUEST_DATA { if { ($tmm_apm_citrix_pnagent != 1) && ($tmm_apm_citrix_receiver != 1) && ($tmm_apm_citrix_nongateway != 1) } { return } set payload [HTTP::payload] if { $tmm_apm_citrix_receiver == 1 } { log -noname accesscontrol.local1.debug "01490000 set tmm_apm_citrix_username "" set tmm_apm_citrix_password "" set tmm_apm_citrix_password1 "" set urlvars [ split $payload "&" ] foreach {u} $urlvars { set param [ lindex [ split $u "=" ] 0 ] set value [ lindex [ split $u "=" ] 1 ] if { $param equals "login" } { set tmm_apm_citrix_username $value } elseif { $param equals "passwd" } { set tmm_apm_citrix_password $value } elseif { $param equals "passwd1" } { set tmm_apm_citrix_password1 $value } } HTTP::header replace "username" $tmm_apm_citrix_username HTTP::header replace "password" $tmm_apm_citrix_password HTTP::release } elseif { ($tmm_apm_citrix_pnagent == 1) || ($tmm_apm_citrix_nongateway == 1) } { if { $tmm_apm_citrix_nongateway == 1 } { set tmm_apm_citrix_receiver 1 } log -noname accesscontrol.local1.debug "01490000 set tmm_apm_citrix_username "" set tmm_apm_citrix_password "" if { [regexp -nocase { ([^<]+) } $payload dummy tmm_apm_citrix_username] == 0 } { set tmm_apm_disp_sess_id [string range $tmm_apm_session_id 24 32] log -noname accesscontrol.local1.error "01490000 return } if { [regexp -nocase { ]+>([^<]+) } $payload dummy tmm_apm_citrix_password] == 0 } { set tmm_apm_disp_sess_id [string range $tmm_apm_session_id 24 32] log -noname accesscontrol.local1.error "01490000 return } binary scan $tmm_apm_citrix_password c* pass set len [llength $pass] set result {} for { set i 0 } { $i < $len } { incr i } { set hi [lindex $pass $i] set hi [ expr { $hi - 0x41 } ] set hi [ expr { $hi << 4 } ] incr i set lo [lindex $pass $i] set lo [ expr { $lo - 0x41 } ] set char [ binary format c [expr {$hi + $lo}] ] append result $char } binary scan $result H* pass binary scan $result c* pass set len [llength $pass] set result {} set first [lindex $pass 0] set char [ binary format c [expr { $first ^ 0xA5 } ] ] append result $char for { set i 1 } { $i < $len } { incr i } { set prev [ lindex $pass [expr {$i-1}] ] set curr [ lindex $pass $i ] set char [ binary format c [ expr {$curr ^ $prev ^ 0xA5} ] ] append result $char } binary scan $result H* pass set tmm_apm_citrix_password [ regsub -all {\000} $result {} ] HTTP::header replace "username" $tmm_apm_citrix_username HTTP::header replace "password" $tmm_apm_citrix_password HTTP::release } } when HTTP_RESPONSE { if { [HTTP::header Content-Type] contains "application/x-ica" } { set tmm_apm_citrix_ica_patching 1 HTTP::collect [HTTP::header Content-Length] } } when HTTP_RESPONSE_DATA { if { $tmm_apm_citrix_ica_patching == 1 } { log -noname accesscontrol.local1.debug "01490000 set payload [HTTP::payload] set payload [ regsub -all {Proxy[^\n]+\n} $payload {} ] set payload [ regsub {DoNotUseDefaultCSL[^\n]+\n} $payload {} ] if { $tmm_apm_citrix_receiver == 1 } { set payload [ regsub {CGPAddress[^\n]+\n} $payload {} ] } regexp -line {Address=(.+)} $payload dummy CtxAddrPort set CtxAddr [lindex [split $CtxAddrPort ":"] 0] set CtxPort [lindex [split $CtxAddrPort ":"] 1] regexp -line {CGPAddress=(.+)} $payload dummy CGPAddrPort if { [info exists CGPAddrPort] } { set CtxPort [lindex [split $CGPAddrPort ":"] 1] } set payload [ regsub {\[WFClient\]} $payload "&\r\nProxyType=Secure\r\nProxyUseFQDN=On\r\nProxyHost=$tmm_apm_vip\r\nProxyUsername=$tmm_apm_session_id\r\nProxyPassword=$CtxAddr-$CtxPort" ] set payload [ regsub {SSLEnable[^\n]+\n} $payload "SSLEnable=On\r\n" ] set payload [ regsub {Address[^\n]+\n} $payload "Address=$tmm_apm_host\r\nHTTPBrowserAddress=!\r\n" ] HTTP::respond 200 content $payload Content-Type [HTTP::header Content-Type] } } when ACCESS_SESSION_STARTED { if { ($tmm_apm_citrix_receiver == 0) or ![info exists tmm_apm_citrix_password1] } { return } ACCESS::session data set "session.logon.last.password1" [URI::decode $tmm_apm_citrix_password1] } when ACCESS_POLICY_COMPLETED { if { $tmm_apm_citrix_receiver == 0 } { return } set sid [ACCESS::session data get session.keydb] set result [ACCESS::policy result] if { [info exists tmm_apm_citrix_username] } { unset tmm_apm_citrix_username } if { [info exists tmm_apm_citrix_password] } { unset tmm_apm_citrix_password } if { [info exists tmm_apm_citrix_password1] } { unset tmm_apm_citrix_password1 } ACCESS::session data set "session.logon.last.password1" "" if { $result equals "allow" } { set resp " " ACCESS::respond 200 content $resp Set-Cookie "MRHSession=$sid;path=/;secure" Set-Cookie "NSC_AAAC=123;path=/;secure" } }235Views0likes0CommentsHTTP Connect Proxy_helper_for_ Citrix_ Xen APP_ Server
Problem this snippet solves: The HTTP Connect Proxy Helper iRule identifies whether a client is a PN Agent. If so, the connection is handled differently than normal Web Interface users. Among other things, the authentication credentials are received via headers and need to be passed through. This is also correlated with the APM session information. This iRule is part of the F5 Deployment Guide "Deploying the BIG-IP APM Secure Proxy with Citrix XenAPP" which can be found in Resources section of f5.com Please see the deployment guide for full instructions on how to install, use and configure this iRule. Code : # switch off SSL if this is HTTP CONNECT request when CLIENT_ACCEPTED { TCP::collect 7 } when CLIENT_DATA { if { [TCP::payload 7] equals "CONNECT" } { SSL::disable } TCP::release } when HTTP_REQUEST { if { [HTTP::method] equals "CONNECT" } { # IP:port of the citrix server is passed in proxy password set authstr [HTTP::header Proxy-Authorization] # extract the base64 username:pass set authstr [lindex [ split $authstr " " ] 1 ] # b64 decode it set authstr [b64decode $authstr] # extract password set authstr [ lindex [ split $authstr ":" ] 1 ] set ipport [split $authstr "-" ] set ip [ lindex $ipport 0 ] set port [ lindex $ipport 1 ] if { [matchclass CitrixAppServers equals "$ip-$port"] ne 0 } { } else { log local0. "Invalid IP and Port $ip-$port in CONNECT from IP [IP::client_addr]" reject return } node $ip $port TCP::respond "HTTP/1.0 200 Connection established\r\n\r\n" HTTP::disable discard SSL::enable SSL::collect TCP::collect 7 } } when CLIENTSSL_DATA { binary scan [SSL::payload] c socksver if { $socksver != 5 } { SSL::release return } SSL::payload replace 0 [SSL::payload length] {} if { [info exists socks_connect] } { SSL::respond [binary format H2H2H2H2H2H2H2H2H2H2 05 00 00 01 00 00 00 00 00 00] SSL::release } else { set socks_connect {} SSL::respond [binary format H2H2 05 00] SSL::collect } }169Views0likes0Comments