Forum Discussion

Philip_Jonsson_'s avatar
Philip_Jonsson_
Icon for Altostratus rankAltostratus
Oct 17, 2018

Performing SSL Bypass for Forward Proxy Traffic based using an iRule capturing the SNI

Hey everyone!

I'm currently developing an iRule to exclude certain traffic from the "Full Proxy" Architecture by turning off the HTTP Profile and Client/Server SSL Profile for our SSL Forward Proxy. We are using the built in function in SWG but for some banking applications it still does not seem to work and the SWG's intelligence is getting in the way. We have been hit with a few bugs which we are currently resolving but in the mean time we need to have this iRule in place to create a workaround.

We would like a clean cut for some of the applications they have by adding them to a Data Group and building an iRule for this purpose.

Here is the current iRule:

when RULE_INIT {
  set 1 to enable logging, 0 to disable
  set static::debug 0
}

when CLIENT_ACCEPTED { 
This iRule is meant to Passthrough SSL Connections for SWG in order to solve SSL issue. Based on Data Group List of IP addresses.
if { [class match [IP::local_addr] equals DG_SWG_SSL_Passthrough_IP] }
        { 
        SSL::disable clientside
        SSL::disable serverside
        HTTP::disable
        if {$static::debug}{log local0. "ir181017-1 - Client IP: {[IP::client_addr]} Server IP: {[IP::local_addr]}: - Match DataGroup DG_SWG_SSL_Passthrough_IP! Disabling SSL"}
        } 
    }


when CLIENTSSL_CLIENTHELLO { 
This iRule is meant to Passthrough SSL Connections for SWG in order to solve SSL issues. Based on Data Group List of FQDNs.
if {$static::debug}{log local0. "ir181017-2 - Client IP: {[IP::client_addr]} - Client Request Server SSL SNI: {[SSL::sni name]}"}
if { [class match [string tolower [SSL::sni name]] contains DG_SWG_SSL_Passthrough_FQDN] }
        { 
        SSL::disable clientside
        SSL::disable serverside
        HTTP::disable
        if {$static::debug}{log local0. "ir181017-3 - Client IP: {[IP::client_addr]} - Server SSL SNI: {[SSL::sni name]} - Match DataGroup DG_SWG_SSL_Passthrough_FQDN! Disabling SSL"}
        } 
    }

The most relevant part of the iRule is the CLIENTSSL_CLIENTHELLO section. When logging the entries, we cannot see any SSL::sni in the logs. But when tcpdumping we can clearly see that there is Server Name Indication fields in the traffic.

Perhaps we are using the SSL::sni command wrong. Perhaps we can use the SSL::extension and have it return the SNI from there and we match against that instead?

6 Replies

  • Try this (in CLIENTSSL_CLIENTHELLO):

    [SSL::extensions exists -type 0]
    

    But also, configuring bypass data groups in the SSL Forward Proxy section of the client SSL profile doesn't work either?

  • Try this code (not tested) and change line 52 with data group name

     

    when CLIENT_ACCEPTED {
        SSL::disable
        SSL::disable serverside               
        TCP::collect
        set tls_servername ""
    }
    when CLIENT_DATA {
         Store TCP Payload up to 2^14 + 5 bytes (Handshake length is up to 2^14)
        set payload [TCP::payload 16389]
        set payloadlen [TCP::payload length]
    
         If valid TLS 1.X CLIENT_HELLO handshake packet
        if { [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action tls_handshake_version tls_handshake_sessidlen] == 6 && \
            ($tls_record_content_type == 22) && ([string match {030[1-3]} $tls_version]) && \
            ($tls_handshake_action == 1) && ($payloadlen == $tls_recordlen+5)} {
             skip past the session id
            set record_offset [expr {44 + $tls_handshake_sessidlen}]
    
             skip past the cipher list
            binary scan $payload @${record_offset}S tls_ciphlen
            set record_offset [expr {$record_offset + 2 + $tls_ciphlen}]
    
             skip past the compression list
            binary scan $payload @${record_offset}c tls_complen
            set record_offset [expr {$record_offset + 1 + $tls_complen}]
    
             check for the existence of ssl extensions
            if { ($payloadlen > $record_offset) } {
                 skip to the start of the first extension
                binary scan $payload @${record_offset}S tls_extension_length
                set record_offset [expr {$record_offset + 2}]
                 Check if extension length + offset equals payload length
                if {$record_offset + $tls_extension_length == $payloadlen} {
                     for each extension
                    while { $record_offset < $payloadlen } {
                        binary scan $payload @${record_offset}SS tls_extension_type tls_extension_record_length
                        if { $tls_extension_type == 0 } {
                             if it's a servername extension read the servername
                             SNI record value start after extension type (2 bytes), extension record length (2 bytes), record type (2 bytes), record type (1 byte), record value length (2 bytes) = 9 bytes
                            binary scan $payload @[expr {$record_offset + 9}]A[expr {$tls_extension_record_length - 5}] tls_servername
                            set record_offset [expr {$record_offset + $tls_extension_record_length + 4}]
    
                        } else {
                             skip over other extensions
                            set record_offset [expr {$record_offset + $tls_extension_record_length + 4}]
                        }
                    }
                }
            }
        } 
        unset -nocomplain payload payloadlen tls_record_content_type tls_recordlen tls_handshake_action tls_handshake_sessidlen record_offset tls_ciphlen tls_complen tls_extension_length tls_extension_type tls_extension_record_length tls_supported_versions_length tls_supported_versions
        if {$tls_servername equals "" || [class match $tls_servername equals "MyDG"] == 0} {
            SSL::enable
            SSL::enable serverside 
        }
        TCP::release
    }
  • James_Hu_141729's avatar
    James_Hu_141729
    Historic F5 Account

    Did SWG URL classification fail to identify the site as banking? Did you try using a custom category instead? Or was the SNI extraction itself not working in SWG?