Forum Discussion

Eldad_162351's avatar
Eldad_162351
Icon for Nimbostratus rankNimbostratus
May 25, 2015

F5 HTTPS Transparent to Forward Proxy Encapsulator

Hello,

 

My setup includes legacy clients sending https requests but cannot set their https proxy.

 

f5 is a transparent proxy and the goal is to dynamically forward their traffic to an external squid proxy's using CONNECT method.

 

the squid's ip and port is transferred in a specific http header.

 

as for http traffic it works fine by using an iRule to forward their traffic to the destined proxy based on the header contents, for HTTPS it's a bit complicated and we havent figured out a way so far to establish it.

 

would appriciate any suggestion for a way to establish it.

 

Thanks. E.

 

  • Hey,

     

    Thanks for the reply, when using the above iRule it fails at the CLIENT_DATA stage.

     

    it never reaches the SERVER_CONNECTED stage...

     

    the clients sends "client hello" and than nothing happens until the connections resets.

     

    any idea?

     

    Thanks E.

     

  • update:

     

    when trying to debug this iRule it seems that LB_FAILED event is triggered.

     

    still not sure why it happens, without the irule it works fine, when using the irule the LB_FAILED is triggered even when adding pool https_proxies to the end of CLIENT_DATA.

     

  • can you post the virtual server and irule configurations?

     tmsh list ltm virtual (name)
     tmsh list ltm rule (name)
    

    by the way, you know we cannot grab squid ip and port from header because it is encrypted, don't you?

  • The Virtual Server

    ltm virtual SSL_VS {
        destination 0.0.0.0:https
        ip-protocol tcp
        mask any
        pool Squid_GW
        profiles {
            tcp { }
        }
        rules {
            HTTPS_Proxy
        }
        source 192.168.100.0/24
        translate-address disabled
        translate-port disabled
        vlans {
            SSL_Clients_Vlan
        }
        vlans_enabled
        vs-index 21
    }
    

    The iRule

    ltm rule HTTPS_Proxy {
     Tested on v10.2 version and up
    
        when RULE_INIT {
             Change to "1" to enable debugging log statements
            set static::proxydebug 0
    }
    
    when CLIENT_ACCEPTED {
        if { $static::proxydebug != 0 } { log local0. "Client connected" }
        set bypass 0
        set bufferdata ""
          0 to make sure the server-side connection is opened right away
        TCP::collect 0 0
        set srcip [IP::client_addr]
        set my_lookup [table lookup -subtable "clients" $srcip]
        my_lookup contains the ip and port for proxies relevant for the legacy cliensts
        now parsing $my_lookup to get the destination ip as $destip and port as $destport for proxy
    }
    
    when CLIENT_DATA {
        if { $static::proxydebug != 0 } { log local0. "CLIENT_DATA before is |[TCP::payload]|" }
    
         accumulate until ready, release when connected
        if { $bypass eq 1 } {
             TCP::payload replace 0 [string length $bufferdata] ""
            TCP::release   
            return
        }
        set bufferdata [TCP::payload]
        TCP::collect
    }
    
    when LB_SELECTED {
        translating the packet to a new dest ip and port from lookup table
        node $destip $destport
    }
    
    when SERVER_CONNECTED {
        serverside {TCP::respond "CONNECT :[TCP::local_port clientside] HTTP/1.0\r\n\r\n"}
        TCP::collect
    }
    
    when SERVER_DATA {
        if { $bypass eq 1 } {
            TCP::release
            return
        }
        if { $static::proxydebug != 0 } { log local0. "PAYLOAD before is |[TCP::payload]|" }
    
         You might need HTTP/1.1 for your proxy, my version of squid was 1.0 
        if { [TCP::payload] starts_with "HTTP/1.0 200 Connection established\r\n\r\n" } {
            TCP::payload replace 0 39 ""
            if { $static::proxydebug != 0 } { log local0. "PAYLOAD after is |[TCP::payload]|" }
    
            TCP::respond $bufferdata
            TCP::release
    
            set bypass 1
    
        } else {
            TCP::close
        }  
    }
    }
    

    with this iRule the packet's dest ip and port doesnt work since the translate is disabled. if i use translate address enable and translate port enable in the iRule im getting LB_FAILED. im adding the translate to the CLIENT_DATA event.

    Thanks nitass

    really appriciated

    E.

  • looks great, my issue starts when im not using a specific or known pool but rather sending the traffic to an unspecified node in which im getting it's ip and port from a table as seen above in my irule.

     

    still not sure what causes the LB_FAILED when im using node instead of default pool.

     

    the squid's are the default gateway for the f5.

     

  • Hey nitass,

    iv'e finally managed to make it work or at least partially work, so thanks very much.

    problem is where to place the node $destip $destport which im getting from a table based on the source ip.

    for now the iRule looks like:

    when CLIENT_ACCEPTED {
        translate address enable
        translate port enable
        if { $static::proxydebug != 0 } { log local0. "Client connected" }
        set bypass 0
        set bufferdata ""
          0 to make sure the server-side connection is opened right away
        TCP::collect 0 0
        set srcip [IP::client_addr]
        set my_lookup [table lookup -subtable "clients" $srcip]
        my_lookup contains the ip and port for proxies relevant for the legacy cliensts
        now parsing $my_lookup to get the destination ip as $destip and port as $destport for proxy
        node $destip $destport
    }
    
    when CLIENT_DATA {
        if { $static::proxydebug != 0 } { log local0. "CLIENT_DATA before is |[TCP::payload]|" }
         accumulate until ready, release when connected
        if { $bypass eq 1 } {
             TCP::payload replace 0 [string length $bufferdata] ""
            TCP::release   
            return
        }
        set bufferdata [TCP::payload]
        TCP::collect
    }
    
    when SERVER_CONNECTED {
        serverside {TCP::respond "CONNECT [IP::local_addr clientside]:[TCP::local_port clientside] HTTP/1.0\r\n\r\n"}
        TCP::collect
    }
    
    when SERVER_DATA {
        if { $bypass eq 1 } {
            TCP::release
            return
        }
        if { $static::proxydebug != 0 } { log local0. "PAYLOAD before is |[TCP::payload]|" }
    
         You might need HTTP/1.1 for your proxy, my version of squid was 1.0 
        if { [TCP::payload] starts_with "HTTP/1.0 200 Connection established\r\n\r\n" } {
            TCP::payload replace 0 39 ""
            if { $static::proxydebug != 0 } { log local0. "PAYLOAD after is |[TCP::payload]|" }
    
            TCP::respond $bufferdata
            TCP::release
    
            set bypass 1
    
        } else {
            TCP::close
        }  
    }
    }
    

    iv'e placed the node at the end of the CLIENT_ACCEPTED but most time on the first session it's not working. only after a refresh it. any idea how to optimize the irule or place the node somewhere else...?

    Thanks Eldad.

  • iv'e placed the node at the end of the CLIENT_ACCEPTED but most time on the first session it's not working. only after a refresh it. any idea how to optimize the irule or place the node somewhere else...?

     

    is table record for client existing on the first session? can you check what table lookup returns on the first session?

     

    • Eldad_162351's avatar
      Eldad_162351
      Icon for Nimbostratus rankNimbostratus
      the table record exists and the variables are ok when i print them at CLIENT_ACCEPTED. where would you place the node $destip $destport?
    • nitass_89166's avatar
      nitass_89166
      Icon for Noctilucent rankNoctilucent
      i do not use node command because all traffic will be sent to pool (web proxy pool) that is assigned to virtual server. does it work if you send all traffic to web proxy pool (i.e. not looking up web proxy node from table)?
    • Eldad_162351's avatar
      Eldad_162351
      Icon for Nimbostratus rankNimbostratus
      yes it works fine. in this case i cannot use the pool because the table records keep updating all the time by an sideband call to different i rule from a different virtual server. so actually the nodes are dynamic and changing from time to time...
  • iv'e placed the node at the end of the CLIENT_ACCEPTED but most time on the first session it's not working. only after a refresh it. any idea how to optimize the irule or place the node somewhere else...?

     

    is table record for client existing on the first session? can you check what table lookup returns on the first session?

     

    • Eldad_162351's avatar
      Eldad_162351
      Icon for Nimbostratus rankNimbostratus
      the table record exists and the variables are ok when i print them at CLIENT_ACCEPTED. where would you place the node $destip $destport?
    • nitass's avatar
      nitass
      Icon for Employee rankEmployee
      i do not use node command because all traffic will be sent to pool (web proxy pool) that is assigned to virtual server. does it work if you send all traffic to web proxy pool (i.e. not looking up web proxy node from table)?
    • Eldad_162351's avatar
      Eldad_162351
      Icon for Nimbostratus rankNimbostratus
      yes it works fine. in this case i cannot use the pool because the table records keep updating all the time by an sideband call to different i rule from a different virtual server. so actually the nodes are dynamic and changing from time to time...