Forum Discussion

PG0581's avatar
PG0581
Icon for Cirrus rankCirrus
Apr 21, 2018

Send DNS request to specific pool member

I created an iRule to attempt to send a couple of FDQNs to specific pool members and in testing with an openssl connect command it's failing. So I just wanted to confirm that there isn't and issue with the iRule or how the virtual server/pools are setup.

 

! Note: the virtual server is doing SSL passthrough - is there a potential that since the data is not being decrypted by the LTM that it's not seeing the FDQN?

 

! Also, the pools aren't directly tied to the virtual server, could this cause an issue?

 

8 Replies

  • Adding in the config as my question was being marked as spam

    Virtual server config:

     ltm virtual Test123-8892 {
    destination 1.1.1.1:8892
    ip-protocol tcp
    mask 255.255.255.255
    profiles {
        fastL4 { }
    }
    rules {
      FQDN-pool-select
    }
    source 0.0.0.0/0
    translate-address enabled
    translate-port enabled
    vs-index 170
    }
    `
    
    
    Pool config:
    
    `    ltm pool Test_Pool-8892-pool {
    members {
        2.2.2.2:8892  {
            address 2.2.2.2
            session monitor-enabled
            state up
        }
    }
    monitor tcp 
    `
    
    }
    
    `ltm pool Test_Pool_2-8892-pool {
    members {
        3.3.3.3:8892 {
            address 3.3.3.3
            session monitor-enabled
            state down
        }
        4.4.4.4:8892 {
            address 4.4.4.4
            session monitor-enabled
            state up
        }
    }
    monitor tcp 
    `
    
    }
    
    iRule config:
    
    
    `    ltm rule FQDN-pool-select {
    when HTTP_REQUEST {
    if { [HTTP::host] eq "test.abc.com" } {
        pool Test_Pool-8892-pool
    } 
    elseif { [HTTP::host] eq "test2.def.com" } {
        pool Test_Pool_2-8892-pool
    }
    

    } }

  • That won't work, because without SSL offload on the virtual to decrypt the request and an HTTP profile, you will not trigger the HTTP_REQUEST event.

     

    Take a look at

     

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

     

    This irule snoops the (unencrypted) SNI request header from the SSL ClientHello packet, and then chooses the pool member based on that.

     

    It will work with SSL passthrough

     

  • Thanks for your reply! Creating iRules are a new thing for me and this one looks fairly complicated, but would I be right in thinking that I only need to edit the below part of the rule that is in the link you provided, to match the specific FQDNs and pools I'm using?

     

     if { [info exists tls_servername] } {
        log local0. "tls_servername = ${tls_servername}"
        switch [string tolower $tls_servername] {
            "test.abc.com" {pool Test_Pool-8892-pool}
            "test2.def.com" {pool Test_Pool_2-8892-pool}
            default {pool $default_pool}

    If so, the fully configured iRule would look like this?

     

    Code when CLIENT_ACCEPTED {
    TCP::collect
    set default_pool [LB::server pool]

    } when CLIENT_DATA { set payload [TCP::payload] set payloadlen [TCP::payload length]

     

     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] {
            "test.abc.com" {pool Test_Pool-8892-pool}
            "test2.def.com" {pool Test_Pool_2-8892-pool}
            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

    }

     

    Lastly, is there anything else that needs to be added to the virutal server? Or is what I have enough?

     

    Thanks again!

     

  • That is correct.

    At some point after testing, comment out the debug log lines

    log local0. ...

    with

     log local0.

    so you don't clutter up your log files.

  • Great! I will try this out this week and get back to you. And just to confirm, the virtual server config is fine as it is? Doesn't need an HTTP profile added or anything?

     

  • Ah okay, thanks.

     

    One other question for you when you have a moment. I noticed a default pool is referenced in the rule, how does this line of code work?

     

    set default_pool [LB::server pool]

    As I believe this line would reference the above?

     

    pool $default_pool
  • So the requirements have now changed since we don't know for sure if the clients will include an SNI field, so we're going to terminate the SSL on the LTM. That being said, will the initial configuration I posted work to base the pool selection on the HTTP host header?