Forum Discussion

seamlessfirework's avatar
Aug 22, 2024

iRule to extract SNI and forward to Virtual Server

Hey guys,

Currently I use traffic policies on a frontside VS to inspect the SNI and forward the traffic to a backside VS properly. I tried to use an iRule instead because sometimes traffic policies are a bit nasty to configure. Anyways, I deployed this simple iRule

when CLIENTSSL_CLIENTHELLO priority 100 {
    set sni [SSL::extensions -type 0]

    log local0. "Client SNI: $sni"
    
    if { $sni equals "fqdn.com" } {
        virtual vs-https443-fqdn.com
    } else {
        drop
    }
}

The log says this

<CLIENTSSL_CLIENTHELLO>: Client SNI: �����������fqdn.com

The forwarding does not work. I think it's binary. I was able to trim away these characters

set sni [string range [SSL::extensions -type 0] 9 end]

and the forwarding worked out. I thought I had to trim 10 characters (indexing starts with 0, not 1) but I had to use 9 because the first character of the FQDN has been removed. Any ideas on this?

However, it works. What would be your approach to solve this? And lastly is there a possibility to decode the binary stuff?

  • DanSkow's avatar
    DanSkow
    Aug 27, 2024

    This looks great Lucas. Line 6 is missing the close quote. To make it map fqdn's to virtuals, I assume it would look like this:

    when CLIENTSSL_CLIENTHELLO priority 100 {
    if {[SSL::extensions exists -type 0]} {
      binary scan [SSL::extensions -type 0] @9a* SNI
      if {[regexp {(?i)[^a-z0-9.-]} $SNI]} {
        log local0. "CLIENTSSL_CLIENTHELLO client offered bogus SNI: $SNI" } 
        elseif {[info exists SNI] && ($SNI equals "fqdn-a.com")} {
            virtual a 
            #log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $SNI]"
      }
        elseif {[info exists SNI] && ($SNI equals "fqdn-b.com")} {
            virtual b 
            #log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $SNI]"
      }
    }
    }

     

  • I think you're missing one step to post-process the data.

    This iRule snippet is used within SSL Orchestrator to catch the SNI so it's been tested extensively.

    if {[SSL::extensions exists -type 0]} {
      binary scan [SSL::extensions -type 0] @9a* SNI
      if {[regexp {(?i)[^a-z0-9.-]} $SNI]} {
        log local0. "CLIENTSSL_CLIENTHELLO client offered bogus SNI"
      } else {
        log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $SNI]
      }
    }

     

    • DanSkow's avatar
      DanSkow
      Icon for Cirrus rankCirrus

      This looks great Lucas. Line 6 is missing the close quote. To make it map fqdn's to virtuals, I assume it would look like this:

      when CLIENTSSL_CLIENTHELLO priority 100 {
      if {[SSL::extensions exists -type 0]} {
        binary scan [SSL::extensions -type 0] @9a* SNI
        if {[regexp {(?i)[^a-z0-9.-]} $SNI]} {
          log local0. "CLIENTSSL_CLIENTHELLO client offered bogus SNI: $SNI" } 
          elseif {[info exists SNI] && ($SNI equals "fqdn-a.com")} {
              virtual a 
              #log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $SNI]"
        }
          elseif {[info exists SNI] && ($SNI equals "fqdn-b.com")} {
              virtual b 
              #log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $SNI]"
        }
      }
      }

       

      • Thanks a lot that worked great!

        I added some (cosmetical) improvements. The irule VS code extension suggested to add "--" after the regexp because of argument injection. So I added it.

        when CLIENTSSL_CLIENTHELLO priority 100 {
            if { [SSL::extensions exists -type 0] } {
                binary scan [SSL::extensions -type 0] @9a* client_sni
                if { [regexp -- {(?i)[^a-z0-9.-]} $client_sni] } {
                    log local0. "CLIENTSSL_CLIENTHELLO client offered bogus SNI: $client_sni"
                }
            elseif { [info exists client_sni] && ($client_sni equals "fqdn-a.com") } {
                virtual a
                log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $client_sni]"
            }
            elseif { [info exists client_sni] && ($client_sni equals "fqdn-b.com") } {
                virtual b
                log local0. "CLIENTSSL_CLIENTHELLO client offered this SNI: [string tolower $client_sni]"
            }
            else { drop }
            }
        }

         

  • I assume you'd either have to add a binary scan event to the iRule, or change the logic from "equals fqdn.com" to "contains fqdn.com" or "ends_with fqdn.com"

    • Thanks for your reply. Well, yes, traffic policies are easy in a way. On the other hand they feel not native to me in the BIG-IP ecosystem. I can't explain that exactly, it's very subjective. Also, configuring a traffic policy through REST is kind of complicated: create a draft, add the configuration, publish the policy. Changing an iRule is one single POST.