Forum Discussion

jackmoscow_2940's avatar
jackmoscow_2940
Icon for Nimbostratus rankNimbostratus
Dec 02, 2016

Selecting Basque-end based on the SNI

Is it possible to do balancing tcp-sessions on F5 to different back-ends, based on the value of the value field in SNI Client Hellos ssl-session? Having a license only on LTM. Set the encryption is not supported by F5, since they are used GOST cryptography in Russia.

 

3 Replies

  • If the F5 doesn't support the cryptography, you can't decrypt at the F5. And more importantly, you can't do SNI-based processing the easy way. Otherwise you could very easily build a CPM policy to load balance to different pools based on the SNI.

    But since we're not talking about the easy way, I'll show you a harder way involving some layer 4 iRule parsing:

    when CLIENT_ACCEPTED priority 300 {
        set detect_handshake 1
        TCP::collect
    }
    when CLIENT_DATA priority 200 {
        binary scan [TCP::payload] H* orig
        if { [binary scan [TCP::payload] cSS tls_xacttype tls_version tls_recordlen] < 3 } {
            reject
            return
        }
    
         768 SSLv3.0
         769 TLSv1.0
         770 TLSv1.1
         771 TLSv1.2
        switch $tls_version {
            "769" -
            "770" -
            "771" {
                if { ($tls_xacttype == 22) } {
                    binary scan [TCP::payload] @5c tls_action
                    if { not (($tls_action == 1) && ([TCP::payload length] > $tls_recordlen)) } {
                        set detect_handshake 0
                    }
                }
            }
            "768" {
                set detect_handshake 0
            }
            default {
                set detect_handshake 0
            }
        }
    
        if { ($detect_handshake) } {
             skip past the session id
            set record_offset 43
            binary scan [TCP::payload] @${record_offset}c tls_sessidlen
            set record_offset [expr {$record_offset + 1 + $tls_sessidlen}]
    
             skip past the cipher list
            binary scan [TCP::payload] @${record_offset}S tls_ciphlen
            set record_offset [expr {$record_offset + 2 + $tls_ciphlen}]
    
             skip past the compression list
            binary scan [TCP::payload] @${record_offset}c tls_complen
            set record_offset [expr {$record_offset + 1 + $tls_complen}]
    
             check for the existence of ssl extensions
            if { ([TCP::payload length] > $record_offset) } {
                 skip to the start of the first extension
                binary scan [TCP::payload] @${record_offset}S tls_extenlen
                set record_offset [expr {$record_offset + 2}]
                 read all the extensions into a variable
                binary scan [TCP::payload] @${record_offset}a* tls_extensions
    
                 for each extension
                for { set ext_offset 0 } { $ext_offset < $tls_extenlen } { incr ext_offset 4 } {
                    binary scan $tls_extensions @${ext_offset}SS etype elen
                    if { ($etype == 0) } {
                         if it's a servername extension read the servername
                        set grabstart [expr {$ext_offset + 9}]
                        set grabend [expr {$elen - 5}]
                        binary scan $tls_extensions @${grabstart}A${grabend} tls_servername_orig
                        set tls_servername [string tolower ${tls_servername_orig}]
                        set ext_offset [expr {$ext_offset + $elen}]
                        break
                    } else {
                         skip over other extensions
                        set ext_offset [expr {$ext_offset + $elen}]
                    }
                }
            }
        }
    
        if { ![info exists tls_servername] } {
             This isn't TLS so we can't decrypt it anyway
            SSL::disable clientside
            SSL::disable serverside
        } else {
            log local0. "tls_servername = ${tls_servername}"
    
             This is where you'd check a datagroup and disable SSL profiles as necessary
    
    
        }
    
        TCP::release
    }
    

    What you're looking at here is the standard iRule for SNI extraction at OSI layer 4 (TCP). At the end you see the clause that contains:

     This is where you'd check a datagroup and disable SSL profiles as necessary
    

    This is where you have access the SNI in the ${tls_servername} variable, and where you could do your pool processing. You must also do this in a Standard VIP configuration to ensure client side TCP completes before starting server side TCP.

    • jackmoscow_2940's avatar
      jackmoscow_2940
      Icon for Nimbostratus rankNimbostratus

      Can you show a simple example or link for CPM policy to load balance to different pools based on the SNI?