Forum Discussion

wlepkin_98758's avatar
wlepkin_98758
Icon for Nimbostratus rankNimbostratus
Aug 23, 2016

Modifying serverside SSL profile based on hostname for SNI

We have a vip where we're terminating SSL at the F5 and then re-encrypting to the servers. We've recently upgraded the pool members to a higher level of Apache that now uses SNI and which requires the SNI name in the client HELO request from the F5 to match the hostname in the http header. There are two possible hostnames, so I need to ensure that the SNI name matches the hostname.

 

I've created two serverside profiles, specifying one of the two hostnames in the SNI Name field of each profile, and now I need to determine which profile to use and to set it at the appropriate time. At the moment I'm using this iRule:

 

when HTTP_REQUEST {

 

set hostname [getfield [HTTP::host] ":" 1]

 

}

 

when LB_SELECTED {

 

switch [string tolower $hostname] {

 

name1 {

 

SSL::profile name1-serverside

 

}

 

name2 {

 

SSL::profile name2-serverside

 

}

 

}

 

}

 

 

I've also tried using the SERVER-CONNECTED event instead of LB_SELECTED. In both cases I get inconsistent results, where sometimes it works and other times Apache complains that the names don't match.

 

Any ideas about what I might be doing wrong? Might the use or non-use of Oneconnect be affecting things here? What about persistence (I'm using cookie persistence)? Can I set the serverside profile in the HTTP_REQUEST event, rather that in a serverside event?

 

Thanks for your help!

 

4 Replies

  • There's actually a really simple solution for this.

     

    When you have SNI enabled SSL profiles, you can apply multiple SSL profiles to a virtual server and the F5 just handles it.

     

    Cheers

     

  • you can use this irule to insert client side server name to servcerside:

     

    when CLIENTSSL_HANDSHAKE {
        if { [SSL::extensions exists -type 0] } then {
            set tls_sni_extension [SSL::extensions -type 0]
        }
    }
    when SERVERSSL_CLIENTHELLO_SEND {
        if { [info exists tls_sni_extension] } then {
            SSL::extensions insert $tls_sni_extension
        }
    }
    

     

  • Hi Wlepkin,

    you can pass a SNI value to you backend servers on multiple ways.

    Scenario 1.) Use different Server SSL Profiles with appropiate (serverside) SNI values and switch the profiles on demand using an iRule and the SSL::profile command during the SERVER_CONNECTED event.

     

    when HTTP_REQUEST {
        switch -exact -- [string tolower [getfield [HTTP::host] ":" 1]] {
            "site1.domain.tld" {
                pool site1.domain.tld
                set srv_ssl_profile "srv_profile_site1.domain.tld"
            }
            "site2.domain.tld" {
                pool site2.domain.tld
                set srv_ssl_profile "srv_profile_site1.domain.tld"
            }
        } default {
            HTTP::respond 403 content "Access Denied"
        }
    }
    when SERVER_CONNECTED {
        if{ [info exists srv_ssl_profile] } then {
            SSL::profile $srv_ssl_profile
        }
    }
    

     

    Scenario 2.) Use an SNI relay iRule to copy the clientside SNI information (if available) into the serverside SSL protocol handshake.

     

    when CLIENTSSL_HANDSHAKE {
        if { [SSL::extensions exists -type 0] } then {
            set tls_sni_extension [SSL::extensions -type 0]
        } else {
            set tls_sni_extension ""
        }
    }
    when SERVERSSL_CLIENTHELLO_SEND {
        if { $tls_sni_extension ne "" } then {
            SSL::extensions insert $tls_sni_extension
        }
    }
    

     

    Note: This technique will work very stable if the client provides a SNI record and the external and internal FQDN is identical.

    Scenario 3.) Use an SNI injection iRule to insert a SNI record of your choice (e.g. based on HTTP::host or datagroups).

     

    when HTTP_REQUEST {
        set http_host [getfield [HTTP::host] ":" 1]
    }
    
    when SERVERSSL_CLIENTHELLO_SEND {
    
         Compute a SNI extension record as defined in RFC 3546/3.1
        
         - TLS extension = SNI = 0000
         - TLS extension length (2 bytes)
           - SNI record length (2 bytes)
               - SNI record type = Hostname = 00 (1 bytes)
                  - SNI value length (2 bytes)
                  - SNI value (1 - X bytes)
        
    
         Compute the length of the server SNI value
        set sni_length [string length $http_host]
         Convert server SNI value to hex
        binary scan $http_host H* http_host_hex
         Set TLS extension to 0000
        set server_tls_sni_extension "0000"
         Calculate the length of the TLS extension   
        append server_tls_sni_extension "[format %4.4X [expr { $sni_length + 5 }]]"
         Calculate the length of the 1st SNI record
        append server_tls_sni_extension "[format %4.4X [expr { $sni_length + 3 }]]"
         Set 1st SNI record type to HOSTNAME.
        append server_tls_sni_extension "00"
         Calculate the length of the 1st SNI record value
        append server_tls_sni_extension "[format %4.4X $sni_length]"
         Add the SSL extension, 1st SNI record and 1st SNI value to serverside clienthello 
        SSL::extensions insert [binary format H* "$server_tls_sni_extension$http_host_hex"]
    
    }
    

     

    Note: This technique will work stable even in the case the client is unable to provide a SNI record and/or the internal/external FQDNs are different names .

    Additional Note: Scenario 1.) allows you to validate the received CNAME/SAN attributes of the serverside SSL certificate via the "Authenticate Name" setting. The SNI relay/insert iRules are not able to overwrite the "Authenticate Name" SSL profile setting, to match the currently selected SNI value. So you have to use additional iRule code to verify the CNAME/SAN attributes of the server side certificates - if this is important for you. You may take a look to the link Stanislas has already provided for further details on CNAME/SAN verification...

    https://devcentral.f5.com/questions/serverssl-sni-vhosts

    Cheers, Kai