F5 is upgrading its customer support chat feature on My.F5.com. Chat support will be unavailable from 6am-10am PST on 1/20/26. Refer to K000159584 for details.

Forum Discussion

Nathan_Andrews_'s avatar
Nathan_Andrews_
Icon for Nimbostratus rankNimbostratus
Feb 27, 2014

iRule required to change Citrix ICA IP address.

Hi,

 

I am currently load balancing a Citrix Web Interface using a BIG-IP LTM.

 

When I connect to the external facing VIP I can login to the web interface and browse the citrix apps as expected. The issue I am experiencing is that when I try and launch the chosen app the ICA launcher tries to connect to the XenApp servers using a private IP address and hence fails to launch.

 

Is there any iRules available to re-write the ICA IP address?

 

Thanks,

 

Nathan

 

7 Replies

  • There's an entire product capability based on this. The Access Policy Manager (APM) module provides a gateway replacement and ICA patching functionality that super-simplifies a XenApp environment.

     

  • Hamish's avatar
    Hamish
    Icon for Cirrocumulus rankCirrocumulus

    Yeah. I have an iRule for that... Hold on...

    Not sure whether I got the original from codeshare and then added a few bits... APM is way easier though...

    You'll also need a separate VS for the ICA traffic, because you need HTTP enabled on this one, but the ICA traffic can't go through an HTTP enabled VS (Or write another iRule to detect the ICA traffic and disable HTTP processing of course)

    Oh... I had to change a few bits in there on the fly (My original one actually re-wrote the SSLProxy differently)... Any syntax errors are the responsibility of the keyboard.

    timing on
    
    when CLIENT_ACCEPTED {
      set al_debugFlag 2
    
      set icapHSL        [HSL::open -proto UDP -pool hsl-log-01]
      set icapLogPrefix "<190>[virtual]:ica-patch-1.2HSL:[IP::client_addr]:[TCP::client_port]:"
    }
    
    when HTTP_REQUEST {
      HSL::send $icapHSL "$icapLogPrefix: [HTTP::uri]"
    
      set vipHost "[HTTP::host]"
    }
    
     catch the ICA file
    when HTTP_RESPONSE {
      if { [HTTP::header Content-Type] contains "application/x-ica" } {
        HSL::send $icapHSL "$icapLogPrefix: application/x-ica Collecting [HTTP::header Content-Length]" 
        HTTP::collect [HTTP::header Content-Length]
      }
    }
    
     and patch it
    when HTTP_RESPONSE_DATA {
       set proxy thru this VIP
      set payload [HTTP::payload]
      set vipHost "my.vs.address"
    
       Wherever the SSLProxyHost exists... Re-write with the vip
      set payload [ regsub -nocase -all {^SSLProxyHost=[\w\d\.\:]+\n} $payload "SSLProxyHost=$vipHost\r\n" ]
    
      HTTP::respond 200 content $payload Content-Type [HTTP::header Content-Type]
    
      HSL::send $icapHSL "$icapLogPrefix: Patched ICA host ==> $vipHost:444"
    }
    
  • Hamish's avatar
    Hamish
    Icon for Cirrocumulus rankCirrocumulus

    It goes on the VIP that fronts the web interface.

     

    FWIW if you go for APM, you can configure this WITHOUT the web interface servers. The APM web top includes javascript that the browser uses to build the interface o the fly from the XMLBroker information.

     

    H

     

  • Hamish's avatar
    Hamish
    Icon for Cirrocumulus rankCirrocumulus

    Just change the line

    set vipHost "my.vs.address"
    

    to put the FQDN of the ICA VS in there and you should be fine (You could comment out the line in HTTP_REQUEST that sets it as well... That's not actually needed (Sorry, I left a few old bits in there. I only used it for real for a few weeks).

    e.g.

    when HTTP_REQUEST {
      HSL::send $icapHSL "$icapLogPrefix: [HTTP::uri]"
    
      set vipHost "[HTTP::host]"
    }
    

    That HTTP_REQUEST is really just debugging, it does nothing useful any more...

  • Hamish's avatar
    Hamish
    Icon for Cirrocumulus rankCirrocumulus

    Ahh... Sorry. I read through your emails again... I had an additional piece of kit doing the ICA proxy that we were just front ending...

     

    Hmm... You may want to pend some $$ on APM... What you'll need to do is

     

    1. Adjust the iRule to insert the necessary information to use an SSL proxy for the ICA traffic. Also save the destination IP that was originally specified.
    2. Write an iRule that will recognise the ICA proxy request coming in and lookup the previously saved destination IP and use that as the pool member you dispatch to..

    Which is essentially what APM does for you...

     

    H

     

  • Marvin's avatar
    Marvin
    Icon for Cirrocumulus rankCirrocumulus

    Hi Nathan, can you please post your complete up to date Irule that you used to integrate LTM with the Citrix solution? Thank you Marvin

     

  • I spent quite some time looking into this one and have seen multiple suggestions but here is what worked for me (using [HTTP::payload] enables you to split the stream expression to run only when a specific IP is matched other wise you can't rewrite the session reliability port this is identical in every ICA file);

    Apply the following iRule to your StoreFront VIP;

    when HTTP_REQUEST {
         Disable stream profile to improve performance.
        STREAM::disable
             Remove Accept-Encoding to prevent compression or STREAM will not work.
            HTTP::header remove "Accept-Encoding"
        set payload 0
       }
    when HTTP_RESPONSE {
         Run stream profile only when application header contains x-ica
        if { [HTTP::header value Content-Type] contains "application/x-ica" }{
        set payload [HTTP::payload]
         Check ICA file individually so you can port map ICA and Session Reliability port.
        if { $payload contains "[INTERNAL-IP_1]:1494" }{
            STREAM::expression {@[INTERNAL-IP_1]:1494@[EXTERNAL-IP_1]:1495@@:2598@:2599@}
            STREAM::enable
            }
        elseif { $payload contains "[INTERNAL-IP_2]:1494" }{
            STREAM::expression {@[INTERNAL-IP_2]:1494@[EXTERNAL-IP_1]:1496@@:2598@:2600@}
            STREAM::enable
        } else { 
            if { $payload contains "[INTERNAL-IP_3]:1494" }{
            STREAM::expression {@[INTERNAL-IP_3]:1494@[EXTERNAL-IP_1]:1497@@:2598@:2601@}
            STREAM::enable
                }
            }
        }
    }
    

    This rule maps each internal resource server to a public IP and unique port above the standard 1494 and 2598. You can then map these ports back to the correct resource server with the following rule applied to a VIP listening on ANY port (remember to enable port translation on the VIP) with a pool defined for each resource server;

    when CLIENT_ACCEPTED {
        switch -glob [TCP::local_port] {
        1495 { pool citrix-vda-1_1494_pool }
        2599 { pool citrix-vda-1_2598_pool }
        1496 { pool citrix-vda-2_1494_pool }
        2600 { pool citrix-vda-2_2598_pool }
        1497 { pool citrix-vda-3_1494_pool }
        2601 { pool citrix-vda-3_2598_pool }
        default { discard }
        }
    }
    

    This doesn't replace APM in anyway as you can't tunnel over SSL etc, but it works nicely and is scalable..