For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

seamlessfirework's avatar
seamlessfirework
Icon for Cirrostratus rankCirrostratus
Aug 22, 2024
Solved

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

     

6 Replies

    • seamlessfirework's avatar
      seamlessfirework
      Icon for Cirrostratus rankCirrostratus

      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.

  • 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"

  • 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]"
        }
      }
      }

       

      • seamlessfirework's avatar
        seamlessfirework
        Icon for Cirrostratus rankCirrostratus

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