Forum Discussion

Piotr_Lewandows's avatar
Piotr_Lewandows
Icon for Altostratus rankAltostratus
Mar 02, 2016

serverssl, SNI, vHosts

Hi,

 

I wonder how to solve such issue:

 

  • Target server is using vHost and HTTPS
  • Each vHost has separate certificate for FQDN (no Wildcard or SAN)
  • Traffic is passed to target servers via one VS with Local Traffic Policy switching Pools based on Host header
  • VS is of course doing decrypting - so multiple clientssl profiles with SNI enabled assigned

I wonder how to configure serverssl profiles. Serverssl profiles are assigned at VS not Pool so how it can be done? Multiple serverssl profiles - each with FQDN of target vHost? If so how VS will know which serverssl use to present correct FQDN to target vHost during SSL handshake?

 

For me it seems not possible - or there is some way?

 

Example:

 

VS IP has two entries in DNS:

 

I have two clientssl profiles assigned to VS with Server Name field values:

 

I have two serverssl profiles assigned to VS with Server Name field values:

 

  • serverssl_host1 - host.host1.com
  • serverssl_host2 - host.host2.com

VS has no default Pool, just Local Traffic Policy choosing:

 

  • Pool host1_pl if Host header is www.host1.com. First of course it's doing SSL handshake using clientssl_host1 based on SNI value from client. This Pool points to Pool Member 192.168.1.1:443 (this is necessary to assign monitor sending correct value in Host header - in this case Host: host.host1.com)
  • Pool host2_pl if Host header is www.host2.com. This Pool is as well pointing to Pool Member 192.168.1.1:443 (this is necessary to assign monitor sending correct value in Host header - in this case Host: host.host2.com)

Let's say target node is 192.168.1.1. On this node we have to vHosts using certificates for FQDN:

 

  • host.host1.com
  • host.host2.com

So how VS can know that when pool host1_pl is chosen serverssl_host1 profile should be used to send SNI value host.host1.com to target vHost? Both Pool Members are using same IP:port, so even if reverse DNS query would be performed it will return both host.host1.com and host.host2.com no matter which Pool will be used (same defined IP:port for Pool Members in both Pools).

 

Probably it could be solved in 11.6+ using FQDN for Nodes? So we can define two separate nodes:

 

  • host.host1.com
  • host.host2.com

and then use them when creating Pool Members for each Pool, so:

 

  • Pool host1_pl - host.host1.com:443
  • Pool host2_pl - host.host2.com:443

but I am not sure if it will work? Piotr

 

8 Replies

  • Hi Piotr,

     

    It is very likely you will need iRules to achieve this, as I don't think the ServerSSL profile supports SNI. We basically read the SSL extension in CLIENTSSL_HANDSHAKE and keep it in the session table with the SSL::sessionid as the key (value is SSL::extensions -type 0), and then inserting it in SERVERSSL_CLIENTHELLO_SEND.

     

    This is only a problem of course if your servers also serve different SSL certs using SNI so this solution is more for a niche problem. If your servers don't actually run SNI themselves, then sending the SNI value in the CLIENTHELLO is a moot point.

     

    Radu

     

    • dragonflymr's avatar
      dragonflymr
      Icon for Cirrostratus rankCirrostratus
      Hi, I am sure that serverssl profile supports SNI - of course as a client, so it means it can send SNI in client hello when starting SSL Handshake - then target server supporting SNI can choose correct certificate for SSL Handshake. Problem here is how correct serverssl profile can be slected when connecting with target server. Piotr
    • raduioncu_16351's avatar
      raduioncu_16351
      Icon for Nimbostratus rankNimbostratus
      Hi Piotr, You don't need multiple ServerSSL profiles - just the default one. You then use the iRule to manually insert the SNI value in the client hello when establishing the serverside connection.
    • dragonflymr's avatar
      dragonflymr
      Icon for Cirrostratus rankCirrostratus
      Hi, Well, with iRule for sure everything is possible :-). I was just curious if this is possible as well using just GUI and ssl profiles - like it's possible with clientssl. As far as I understand your answer it's not really possible this way? Piotr
  • Hi Piotr,

    you have to use iRule to select different SNI enabled Server SSL Profiles (with appropiate "Server Name" fields) to support SNI on the web server level, or simply use a unified Server SSL Profile that would be valid for every website (e.g. using a fixed internal SSL certificate for every internal site).

    You could also try to automate the SNI-enabled Server SSL profile selection by parsing the SNI value submitted by your client and then apply a custom logic to select the right SNI enabled Server SSL Profile on the fly within the SERVER_CONNECTED event. It should be somehow possible to do so, but I haven't digged into such a code yet... 😉

    Cheers, Kai

     

  • Hey Radu,

    your mentioned SNI-Relay logic is indeed a cool method to support Piotrs requirement without switching between multiple different Server_SSL_Profiles. Great hint and Kudo +1...;-)

     

    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
        }
    }
    

     

    Cheers, Kai

     

  • Hi Piotr, Radu,

    continued to play with the SNI-Relay and implemented a server side "CN" and/or "SAN" verification code. Seems that this technique could be securely used to relay SNI values between client/server side and use the provided SNI value to verify the serverside CN or SAN values.

     

    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
        }
    }
    when SERVERSSL_HANDSHAKE {
        set cname_valid 0
        if { [SSL::cert count] > 0 } then {
            set cnames [string tolower [string map { "*" "" } [findstr [X509::subject [SSL::cert 0]] "CN=" 3 ","]]]
            set cnames [lsort -unique "$cnames [string tolower [string map { ", DNS:*" " " ", DNS:" " " ",DNS:*" " " ",DNS:" " " "*" "" ", " " " "," " " } [findstr [X509::extensions [SSL::cert 0]] "DNS:" 4 "\n"]]]"]
        }
        foreach cname $cnames {
            if { $cname starts_with "." } then {
                if { [llength [split $tls_sni_extension "."]] == [llength [split $cname "."]] } then {
                    if { [findstr $tls_sni_extension "\." 0 end] ends_with $cname } then {
                        set cname_valid 1
                        return
                    }
                }
            } else {
                if { $tls_sni_extension equals $cname } then {
                    set cname_valid 1
                    return
                }
            }       
        }
    }
    when HTTP_REQUEST_SEND {
        clientside {
            HTTP::respond 200 content "Client/Server SSL SNI = [string range $tls_sni_extension 9 end], Server SSL CNAMES = $cnames, Verification Result = $cname_valid" Content-Type "text/txt"
        }
    }
    

     

    Note: This is a proof of concept code. Don't use it in production environments without further testings. And by any means implement OneConnect to reduce the overhead of the iRule... 😉

     

    Cheers, Kai

     

  • To finish it up 🙂

    This question is quite clear explaining that without iRule correct serverssl is not selected by VS How can I configure Server SSL Profiles....

    I am not sure but it seems that assigning multiple serverssl profiles allow iRule to select correct from assigned - so iRule can only select profile that is attached to VS - or it's not necessary?

    Here is iRule:

     

    when HTTP_REQUEST {
        set hostname [getfield [HTTP::host] ":" 1]
    }
    
    when SERVER_CONNECTED {
        switch -glob [string tolower [hostname]] {
            "site1.domain.uk" { SSL::profile site1.domain.uk-server }
            "site2.domain.uk" { SSL::profile site2.domain.uk-server }
        }
    }
    

     

    Piotr