SNI Routing with BIG-IP

In the previous article, The Three HTTP Routing Patterns, Lori MacVittie covers 3 methods of routing. Today we will look at Server Name Indication (SNI) routing as an additional method of routing HTTPS or any protocol that uses TLS and SNI.

Using SNI we can route traffic to a destination without having to terminate the SSL connection. This enables several benefits including:

  • Reduced number of Public IPs
  • Simplified configuration
  • More intelligent routing of TLS traffic

Terminating SSL Connections

When you have a SSL certificate and key you can perform the cryptographic actions required to encrypt traffic using TLS.  This is what I refer to as “terminating the SSL connection” throughout this article.  When you want to route traffic this is a chicken and an egg problem, because for TLS traffic you want to be able to route the traffic by being able to inspect the contents, but this normally requires being able to “terminate the SSL connection”.  The goal of this article is to layer in traffic routing for TLS traffic without having to require having/knowing the original SSL certificate and key.

Server Name Indication (SNI)

SNI is a TLS extension that makes it possible to "share" certificates on a single IP address. This is possible due to a client using a TLS extension that requests a specific name before the server responds with a SSL certificate.

Prior to SNI, the other options would be a wildcard certificate or Subject Alternative Name (SAN) that allows you to specify multiple names with a single certificate.

SNI with Virtual Servers

It has been possible to use SNI on F5 BIG-IP since TMOS 11.3.0. The following KB13452 outlines how it can be configured. In this scenario (from the KB article) the BIG-IP is terminating the SSL connection.  Not all clients support SNI and you will always need to specify a “fallback” profile that will be used if a SNI name is not used or matched. The next example will look at how to use SNI without terminating the SSL connection.

SNI Routing

Occasionally you may have the need to have a hybrid configuration of terminating  SSL connections on the BIG-IP and sending connections without terminating SSL.  

One method is to create two separate virtual servers, one for SSL connections that the BIG-IP will handle (using clientssl profile) and one that it will not handle SSL (just TCP). This works OK for a small number of backends, but does not scale well if you have many backends (run out of Public IP addresses).

Using SNI Routing we can handle everything on a single virtual server / Public IP address. There are 3 methods for performing SNI Routing with BIG-IP

  1. iRule with binary scan a. Article by Colin Walker code attribute to Joel Moses b. Code Share by Stanislas Piron
  2. iRule with SSL::extensions
  3. Local Traffic Policy

Option #1 is for folks that prefer complete control of the TLS protocol. It only requires the use of a TCP profile. Options #2 and #3 only require the use of a SSL persistence profile and TCP profile.

SNI Routing with Local Traffic Policy

We will skip option #1 and #2 in this article and look at using a Local Traffic Policy for SNI Routing. For a review of Local Traffic Policies check out the following DevCentral articles:

In previous articles about Local Traffic Policies the focus was on routing HTTP traffic, but today we will use it to route SSL connections using SNI.

In the following example, using a Local Traffic Policy named “sni_routing”, we are setting a condition on the SSL Extension “servername” and sending the traffic to a pool without terminating the SSL connection. The pool member could be another server or another BIG-IP device.

The next example will forward the traffic to another virtual server that is configured with a clientssl profile.  This uses VIP targeting to send traffic to another virtual server on the same device.

In both examples it is important to note that the “condition”/“action” has been changed from occurring on “request” (that maps to a HTTP L7 request) to “ssl client hello”.  By performing the action prior to any L7 functions occurring, we can forward the traffic without terminating the SSL connection.  The previous example policy, “sni_routing”, can be attached to a Virtual Server that only has a TCP profile and SSL persistence profile.  No HTTP or clientssl profile is required!

This method can also be used to solve the issue of how to consolidate multiple SSL virtual servers behind a single virtual server that have different APM and/or ASM policies.  This is similar to the architecture that is used by the Container Connector for Cloud Foundry; in creating a two-tier load balancing solution on a single device.

Routed Correctly?

TLS 1.3 has interesting proposals on how to obscure the servername (TLS in TLS?), but for now this is a useful and practical method  of handling multiple SSL certs on a single IP.  In the future this may still be possible as well with TLS 1.3.  For example the use of a HTTP Fronting service could be a tier 1 virtual server (this is just my personal speculation, I have not tried, at the time of publishing this was still a draft proposal). 

In other news it has been demonstrated that a combination of using SNI and a different host header can be used for “domain fronting”.  A method to enforce consistent policy (prevent domain fronting) would be to layer in additional conditions that match requested SNI servername  (TLS extension) with requested HOST header (L7 HTTP header).  This would help enforce that a tenant is using a certificate that is associated with their application and not “borrowing” the name and certificate that is being used by an adjacent service.

We don’t think of a TLS extension as an attribute that can be used to route application traffic, but it is useful and possible on BIG-IP.

Updated Mar 25, 2023
Version 2.0

Was this article helpful?

16 Comments

  • Hi Eric,

    At least it means I was reading article with some understanding 🙂

    I wonder if you can help with understanding LTP conditions evaluation. I am trying to setup VS on both 80 and 443 ports (as you know possible in v 14.1+). When dst port is 80 I am disabling Client SSL profile at client accepted (separate policy). Then I am checking Host header to make forwarding decision. So far so good. Then when port is 443 I am not disabling Client SSL and trying to check SNI at SSL Client Hello - now everything breaks for port 80 - immediate TCP RST. It looks like even if traffic is not matching port condition LTM is still trying to check SSL Extension - and this is not present in HTTP request, so no target is selected and there is failure. At the same time traffic to port 443 works without issue.

    If only port 443 is in conditions then both HTTP and HTTPS works.

    Here is LTP I tried to use:

     

    ltm policy vh-04_ltp {
    controls { forwarding server-ssl }
    last-modified 2019-02-19:16:47:48
    requires { http tcp client-ssl }
    rules {
        nothing-matched_r {
            actions {
                0 {
                    log
                    write
                    facility local0
                    message "No rule was matched"
                    priority info
                }
            }
            ordinal 3
        }
        site41-http-https_r {
            actions {
                0 {
                    log
                    write
                    facility local0
                    message "site41-http-https hit"
                    priority info
                }
                1 {
                    forward
                    select
                    pool site41-443_pl
                }
                2 {
                    tcl
                    set-variable
                    expression site41-http-https.f5demo.com
                    name host_ltp
                }
            }
            conditions {
                0 {
                    http-host
                    host
                    values { site41-http-https.vlab.f5demo.com }
                }
                1 {
                    tcp
                    client-accepted
                    port
                    local
                    values { 80 }
                }
            }
            ordinal 1
        }
        site41-http-to-http_r {
            actions {
                0 {
                    forward
                    select
                    pool site41-80_pl
                }
                1 {
                    server-ssl
                    disable
                }
                2 {
                    log
                    write
                    facility local0
                    message "site41-http-http hit"
                    priority info
                }
            }
            conditions {
                0 {
                    http-host
                    host
                    values { site41-http-http.vlab.f5demo.com }
                }
                1 {
                    tcp
                    client-accepted
                    port
                    local
                    values { 80 }
                }
            }
        }
        site41-https-https_r {
            actions {
                0 {
                    log
                    write
                    facility local0
                    message "site41-https-https HIT"
                    priority info
                }
                1 {
                    forward
                    select
                    pool site41-443_pl
                }
                2 {
                    tcl
                    set-variable
                    expression site41-https-https.f5demo.com
                    name host_ltp
                }
            }
            conditions {
                0 {
                    tcp
                    client-accepted
                    port
                    local
                    values { 443 }
                }
                1 {
                    ssl-extension
                    ssl-client-hello
                    server-name
                    values { site41-https-https.vlab.f5demo.com }
                    missing
                }
            }
            ordinal 2
        }
    }
    status published
    strategy first-match
    

     

    }

    I know how to do that other way, I am just curious why SSL Extension condition is breaking HTTP part.

    Piotr

  • @Piotr,

     

    Re: why is SSL condition breaking HTTP part

     

    The short answer is that you probably want to have a separate LTP for HTTP and HTTPS.

     

    I suspect the issue is that with HTTP you are missing condition HTTP request for checking a host header and that is incompatible with your condition for SSL Client Hello for HTTPS. These two will keep on conflicting. With an iRule you could disable HTTP or SSL Persistence, but not sure whether it is feasible with a LTP.

     

  • Hi Eric,

     

    Weird part is that you can actually disable both SSL persistence and HTTP profile. Disabling HTTP profile allows HTTPS traffic to pass through to another VS or Pool. Disabling Persistence do not help with HTTP traffic :-(

     

    Best result I was able to achieve is to forward HTTP traffic to another VS based on dst port (has to be set as first rule).

     

    For HTTPS I am able (disabling HTTP profile) to both forward traffic to another VS and directly to pool based on SNI.

     

    What bothers me is logic of performing Condition test. If my rule contains Condition TCP port at local side equal to 443 at client accepted then it should fail for HTTP (local port 80) and pass processing to another rule.

     

    It seems however that even both HTTPS related rules are failing they as well somehow stoping progress of connection processing (at least for rule conditions).

     

    Having iRule attached to the VS I can see log entries like that:

     

    • No match (from LTP log action)
    • Host is some.site.com from HTTP_REQUEST event log action

    So in the end traffic processing is continued for HTTP but it seems to be too late for rule condition. I wonder if this is feature or bug?

     

    Piotr

     

  • Hi,

    I have trouble implementing this solution.

    Can you help me configure the first VS with the policy ?

     

    I have :

    1st VS with policy and ssl client profile "test"

    the policy forward traffic to a 2nd VS

    2nd VS with ssl client profile "subtest"

     

    what is the good config for ssl client profile "test" ?

     

    thanks in advance

  • Hi, Eric, per your SNI Routing diagram above, it seems it is possible to use SNI for SSL pass-through as well as SSL termination. Do you have an example of how to implement SNI Routing diagram above? Thanks.

  • These comments have been moved.