Forum Discussion

kridsana's avatar
kridsana
Icon for Cirrocumulus rankCirrocumulus
Feb 07, 2018

iRule Choose pool base on SNI and disable ssl base on SNI

Hi

 

We have the need to select pool via irule but we don't want to decrypt all HTTPS traffic.

 

Can we do this? (This is outbound traffic)

 

  1. We have list of URL in iRule Datagroup

     

  2. if user access HTTPS website -> F5 detect SNI and check if that URL is in datagroup or not.

     

    • if it's in datagroup -> Load balance to pool A.

       

    • if it's not in datagroup -> disable http profile, ssl profile and other profile (if any) to just forward HTTPS traffic only. don't decrypt/encrypt anything.

       

Is this possible?

 

Thank you

 

  • Surgeon's avatar
    Surgeon
    Ret. Employee

    hmm, interesting. If you do no want offload ssl on server side than you need somehow to re-initiate ssl handshake between a client and back-end server, since ssl handshake is already finished with big-ip when iRule checks for URL

     

    SSL forward proxy bypass looks reasonable. big-ip should bypass ssl, based on host name in the server cert If ssl forward proxy is enabled then ssl handshake on the client side will not finish until big-ip receives certificate from the back-end server.

     

  • I changed the Kevin Stewart's irule to solve your issue (not tested, developed with tcl shell out of box)

    Updated to add unset command and remove useless temp variables

    when CLIENT_ACCEPTED {
        TCP::collect
        set default_pool [LB::server pool]
    }
    when CLIENT_DATA {
        set payload [TCP::payload]
        set payloadlen [TCP::payload length]
    
         - tls_record_content_type : Reccord layer content-type     (1 byte)
            Handshake value is 22 (required for CLIENT_HELLO packet)
         - tls_major_version : SSL / TLS version SSLv3 and TLS share major version value 3 (1 byte)
         - tls_minor_version : SSL / TLS minor version. (1 byte)
            SSLv3 value is 0 (doesn't support SNI, not valid in first condition)
            TLS_1.0 value is 1
            TLS_1.1 value is 2
            TLS_1.2 value is 3
         - tls_recordlen : Reccord layer content length (2 bytes) : must match payload length
               TLS Hanshake protocol
               - tls_action : Handshake action (1 byte) : CLIENT_HELLO = 1
               - handshake length not stored in a variable (3 bytes)
               -  SSL / TLS handshake major version not stored in a variable (1 byte)
               -  SSL / TLS handshake minor version not stored in a variable (1 byte)
               - hanshake random not stored in a variable (32 bytes)
               - tls_sessidlen : handshake sessionID length (1 byte)
               - handshake sessionID (length defined by sessionID length value, max 32-bit)
               - CipherSuites length (2 bytes)
               - CipherSuites (length defined by CipherSuites length value)
               - Compression length (2 bytes)
               - Compression methods (length defined by Compression length value)
               - Extensions 
                   - Extension length (2 bytes)
                   - list of Extensions records (length defined by extension length value)
                       - extension record type (2 bytes)
                       - extension record length (2 bytes)
                       - extension data (length defined by extension record length value)
        
        
           SNI extension data format:
               - SNI record length (2 bytes)
               - SNI record data (length defined by SNI record length value)
                   - SNI record type (1 byte)
                   - SNI record value length (2 bytes)
                   - SNI record value (length defined by SNI record value length value)
    
         If valid TLS 1.X CLIENT_HELLO handshake packet
        if { [binary scan $payload cccScx37c tls_record_content_type tls_major_version tls_minor_version tls_recordlen tls_action tls_sessidlen] == 6 && \
            ($tls_record_content_type == 22) && \
            ($tls_major_version == 3) && ($tls_minor_version > 0) && \
            ($tls_action == 1) && \
            ($payloadlen == $tls_recordlen+5)} {
    
             skip past the session id
            set record_offset [expr {44 + $tls_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_extenlen
                set record_offset [expr {$record_offset + 2}]
                 Check if extension length + offset equals payload length
                if {$record_offset + $tls_extenlen == $payloadlen} {
                     for each extension
                    while { $record_offset < $payloadlen } {
                        binary scan $payload @${record_offset}SSx3S etype elen erlen
                        if { ($etype == 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${erlen} tls_servername
                            set record_offset [expr {$record_offset + $elen + 4}]
                            break
                        } else {
                             skip over other extensions
                            set record_offset [expr {$record_offset + $elen + 4}]
                        }
                    }
                }
            } else {
            log local0. "packet is not a valid TLS 1.X CLIENT_HELLO handshake"
            reject
            return
        }
        unset -nocomplain payload payloadlen tls_record_content_type tls_major_version tls_minor_version tls_recordlen tls_action tls_sessidlen record_offset tls_ciphlen tls_complen tls_extenlen etype elen erlen 
    
        if { [info exists tls_servername] } {
            log local0. "tls_servername = ${tls_servername}"
            switch [string tolower $tls_servername] {
                "app1.company.com" {pool pool1}
                "app2.company.com" {pool pool2}
                default {pool $default_pool}
            }
        } else {
            log local0. "packet is a valid TLS 1.X CLIENT_HELLO handshake but doesn't contain server name extension"
            pool $default_pool
        }
    
        TCP::release
    }
    
  • Hi Surgeon

     

    From irule stanislas provide below . Is the result is the same as SSL Forward proxy ?

     

    I mean just choose pool base on Hostname. We don't want to do other than this.

     

    Kridsana