sni
43 TopicsSNI 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 havea 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 chickenand an egg problem, becausefor 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 iRule with binary scan a. Article by Colin Walker code attribute to Joel Moses b. Code Share by Stanislas Piron iRule with SSL::extensions 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: LTM Policy Jan 2015 Simplifying Local Traffic Policies in BIG-IP 12.1 June 2016 In previous articles about Local Traffic Policiesthe 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.24KViews0likes16CommentsServerside SNI injection iRule
Problem this snippet solves: Hi Folks, the iRule below can be used to inject a TLS SNI extension to the server side based on e.g. HOST-Header values. The iRule is usefull if your pool servers depending on valid SNI records and you don't want to configure dedicated Server SSL Profiles for each single web application. Cheers, Kai How to use this snippet: Attach the iRule to the Virtual Server where you need to insert a TLS SNI expension Tweak the $sni_value variable within the HTTP_REQUEST to meet your requirements or move it to a different event as needed. Make sure you've cleared the "Server Name" option in your Server_SSL_Profile. Code : when HTTP_REQUEST { #Set the SNI value (e.g. HTTP::host) set sni_value [getfield [HTTP::host] ":" 1] } when SERVERSSL_CLIENTHELLO_SEND { # SNI extension record as defined in RFC 3546/3.1 # # - TLS Extension Type = int16( 0 = SNI ) # - TLS Extension Length = int16( $sni_length + 5 byte ) # - SNI Record Length = int16( $sni_length + 3 byte) # - SNI Record Type = int8( 0 = HOST ) # - SNI Record Value Length = int16( $sni_length ) # - SNI Record Value = str( $sni_value ) # # Calculate the length of the SNI value, Compute the SNI Record / TLS extension fields and add the result to the SERVERSSL_CLIENTHELLO SSL::extensions insert [binary format SSScSa* 0 [expr { [set sni_length [string length $sni_value]] + 5 }] [expr { $sni_length + 3 }] 0 $sni_length $sni_value] } Tested this on version: 12.06.3KViews7likes30CommentsHTTPS SNI Monitoring How-to
Hi, You may or may not already have encountered a webserver that requires the SNI (Server Name Indication) extension in order to know which website it needs to serve you. It comes down to "if you don't tell me what you want, I'll give you a default website or even simply reset the connection". A typical IIS8.5 will do this, even with the 'Require SNI' checkbox unchecked. So you have your F5, with its HTTPS monitors. Those monitors do not yet support SNI, as they have no means of specifying the hostname you want to use for SNI. In comes a litle script, that will do exactly that. Here's a few quick steps to get you started: Download the script from this article (it's posted on pastebin: http://pastebin.com/hQWnkbMg). Import it under 'System' > 'File Management' > 'External Monitor Program File List'. Create a monitor of type 'External' and select the script from the picklist under 'External Program'. Add your specific variables (explanation below). Add the monitor to a pool and you are good to go. A quick explanation of the variables: METHOD (GET, POST, HEAD, OPTIONS, etc. - defaults to 'GET') URI ("the part after the hostname" - defaults to '/') HTTPSTATUS (the status code you want to receive from the server - defaults to '200') HOSTNAME (the hostname to be used for SNI and the Host Header - defaults to the IP of the node being targetted) TARGETIP and TARGETPORT (same functionality as the 'alias' fields in the original monitors - defaults to the IP of the node being targetted and port 443) DEBUG (set to 0 for nothing, set to 1 for logs in /var/log/ltm - defaults to '0') RECEIVESTRING (the string that needs to be present in the server response - default is empty, so not checked) HEADERX (replace the X by a number between 1 and 50, the value for this is a valid HTTP header line, i.e. "User-Agent: Mozilla" - no defaults) EXITSTATUS (set to 0 to make the monitor always mark te pool members as up; it's fairly useless, but hey... - defaults to 1) There is a small thing you need to know though: due to the nature of the openssl binary (more specifically the s_client), we are presented with a "stdin redirection problem". The bottom line is that your F5 cannot be "slow" and by slow I mean that if it requires more than 3 seconds to pipe a string into openssl s_client, the script will always fail. This limit is defined in the variable "monitor_stdin_sleeptime" and defaults to '3'. You can set it to something else by adding a variable named 'STDIN_SLEEPTIME' and giving it a value. From my experience, anything above 3 stalls the "F5 script executer", anything below 2 is too fast for openssl to read the request from stdin, effectively sending nothing and thus yielding 'down'. When you enable debugging (DEBUG=1), you can see what I mean for yourself: no more log entries for the script when STDIN_SLEEPTIME is set too high; always down when you set it too low. I hope this script is useful for you, Kind regards, Thomas Schockaert6.2KViews0likes22CommentsTLS server_name extension based routing without clientssl profile
Problem this snippet solves: Some configuration requires to not decrypt SSL traffic on F5 appliances to select pool based on HTTP Host header. I found a useful irule and this code keeps the structure and most of binary commands of it. I'm not sure if the first author was Kevin Stewart or Colin Walker. thanks both of them to have provided such code. I worked to understand it reading TLS 1.2 RFC 5246 and TLS 1.3 draft-23 and provided some enhancements and following description with irule variables references. According to TLS 1.3 draft-23, this code will still be valid with next TLS version. the following network diagram shows one use cases where this code will help. This diagram show how this code works based on the tls_servername_routing_dg Datagroup values and detected server name and TLS versions detected in the CLIENT_HELLO packet. For performances reasons, only the first TCP data packet is analyzed. Versions : 1.1 : Updated to support TLS version detection and SSL offload feature. (05/03/2018) 1.2 : Updated to support TLS Handshake Failure Messages instead of reject. (09/03/2018) 1.3 : Updated to support node forwarding, logs only for debug (disabled with static variable), and changed the Datagroup name to tls_servername_routing_dg . (16/03/2018) 1.4 : Added 16K handshake length limit defined in RFC 1.2 in variable payload. (13/04/2018) 1.5 : Added supported version extension recursion, to bypass unknown TLS version if a known and allowed version is in the list. This correct an issue with Google chrome which include not documented TLS version on top of the list. (30/04/2018) How to use this snippet: create a virtual server with following configuration: type : Standard SSL Profile (client) : Only if you want to enable SSL offload for some pools irule : code bellow create all objects used in following datagroup (virtual servers, pools) create a data-group named tls_servername_routing_dg. if you want to forward to pool, add the value pool NameOfPool if you want to forward to pool and enable SSL Offload (ClientSSL profile must be enabled on virtual server), add the value pool NameOfPool ssl_offload if you want to forward to virtual server, add the value virtual NameOfVirtual if you want to forward to an IP address, add the value node IPOfServer , backend server will not be translated if you want to reject the connection with RFC compliant handshake_failure message, add the value handshake_failure if you want to reject the connection, add the value reject if you want to drop the connection, add the value drop The default value keyword is search if there is no TLS server name extension or if TLS server name extension is not found in the data group. here is an example: ltm data-group internal tls_servername_routing_dg { records { app1.company.com { data "virtual vs_app1.company.com" } app2.company.com { data "pool p_app2" } app3.company.com { data "pool p_app3 ssl_offload" } app4.company.com { reject } default { data "handshake_failure" } } type string } Code : when RULE_INIT { set static::sni_routing_debug 0 } when CLIENT_ACCEPTED { if { [PROFILE::exists clientssl] } { # We have a clientssl profile attached to this VIP but we need # to find an SNI record in the client handshake. To do so, we'll # disable SSL processing and collect the initial TCP payload. set ssldisable "SSL::disable" set sslenable "SSL::enable" eval $ssldisable } TCP::collect set default_pool [LB::server pool] set tls_servername "" set tls_handshake_prefered_version "0000" } when CLIENT_DATA { # Store TCP Payload up to 2^14 + 5 bytes (Handshake length is up to 2^14) set payload [TCP::payload 16389] set payloadlen [TCP::payload length] # - Record layer content-type (1 byte) --> variable tls_record_content_type # Handshake value is 22 (required for CLIENT_HELLO packet) # - SSLv3 / TLS version. (2 byte) --> variable tls_version # SSLv3 value is 0x0300 (doesn't support SNI, not valid in first condition) # TLS_1.0 value is 0x0301 # TLS_1.1 value is 0x0302, 0x0301 in CLIENT_HELLO handskake packet for backward compatibility (not specified in RFC, that's why the value 0x0302 is allowed in condition) # TLS_1.2 value is 0x0303, 0x0301 in CLIENT_HELLO handskake packet for backward compatibility (not specified in RFC, that's why the value 0x0303 is allowed in condition) # TLS_1.3 value is 0x0304, 0x0301 in CLIENT_HELLO handskake packet for backward compatibility (explicitly specified in RFC) # TLS_1.3 drafts values are 0x7FXX (XX is the hexadecimal encoded draft version), 0x0301 in CLIENT_HELLO handskake packet for backward compatibility (explicitly specified in RFC) # - Record layer content length (2 bytes) : must match payload length --> variable tls_recordlen # - TLS Hanshake protocol (length defined by Record layer content length value) # - Handshake action (1 byte) : CLIENT_HELLO = 1 --> variable tls_handshake_action # - handshake length (3 bytes) # - SSL / TLS handshake version (2 byte) # In TLS 1.3 CLIENT_HELLO handskake packet, TLS hanshake version is sent whith 0303 (TLS 1.2) version for backward compatibility. a new TLS extension add version negociation. # - hanshake random (32 bytes) # - handshake sessionID length (1 byte) --> variable tls_handshake_sessidlen # - handshake sessionID (length defined by sessionID length value, max 32-bit) # - CipherSuites length (2 bytes) --> variable tls_ciphlen # - CipherSuites (length defined by CipherSuites length value) # - Compression length (2 bytes) --> variable tls_complen # - Compression methods (length defined by Compression length value) # - Extensions # - Extension length (2 bytes) --> variable tls_extension_length # - list of Extensions records (length defined by extension length value) # - extension record type (2 bytes) : server_name = 0, supported_versions = 43--> variable tls_extension_type # - extension record length (2 bytes) --> variable tls_extension_record_length # - extension data (length defined by extension record length value) # # TLS server_name extension data format: # - SNI record length (2 bytes) # - SNI record data (length defined by SNI record length value) # - SNI record type (1 byte) # - SNI record value length (2 bytes) # - SNI record value (length defined by SNI record value length value) --> variable tls_servername # # TLS supported_version extension data format (added in TLS 1.3): # - supported version length (1 bytes) --> variable tls_supported_versions_length # - List of supported versions (2 bytes per version) --> variable tls_supported_versions # If valid TLS 1.X CLIENT_HELLO handshake packet if { [binary scan $payload cH4Scx3H4x32c tls_record_content_type tls_version tls_recordlen tls_handshake_action tls_handshake_version tls_handshake_sessidlen] == 6 && \ ($tls_record_content_type == 22) && \ ([string match {030[1-3]} $tls_version]) && \ ($tls_handshake_action == 1) && \ ($payloadlen == $tls_recordlen+5)} { # store in a variable the handshake version set tls_handshake_prefered_version $tls_handshake_version # skip past the session id set record_offset [expr {44 + $tls_handshake_sessidlen}] # skip past the cipher list binary scan $payload @${record_offset}S tls_ciphlen set record_offset [expr {$record_offset + 2 + $tls_ciphlen}] # skip past the compression list binary scan $payload @${record_offset}c tls_complen set record_offset [expr {$record_offset + 1 + $tls_complen}] # check for the existence of ssl extensions if { ($payloadlen > $record_offset) } { # skip to the start of the first extension binary scan $payload @${record_offset}S tls_extension_length set record_offset [expr {$record_offset + 2}] # Check if extension length + offset equals payload length if {$record_offset + $tls_extension_length == $payloadlen} { # for each extension while { $record_offset < $payloadlen } { binary scan $payload @${record_offset}SS tls_extension_type tls_extension_record_length if { $tls_extension_type == 0 } { # if it's a servername extension read the servername # SNI record value start after extension type (2 bytes), extension record length (2 bytes), record type (2 bytes), record type (1 byte), record value length (2 bytes) = 9 bytes binary scan $payload @[expr {$record_offset + 9}]A[expr {$tls_extension_record_length - 5}] tls_servername set record_offset [expr {$record_offset + $tls_extension_record_length + 4}] } elseif { $tls_extension_type == 43 } { # if it's a supported_version extension (starting with TLS 1.3), extract supported version in a list binary scan $payload @[expr {${record_offset} + 4}]cS[expr {($tls_extension_record_length -1)/2}] tls_supported_versions_length tls_supported_versions set tls_handshake_prefered_version [list] foreach version $tls_supported_versions { lappend tls_handshake_prefered_version [format %04X [expr { $version & 0xffff }] ] } if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : prefered version list : $tls_handshake_prefered_version"} set record_offset [expr {$record_offset + $tls_extension_record_length + 4}] } else { # skip over other extensions set record_offset [expr {$record_offset + $tls_extension_record_length + 4}] } } } } } elseif { [binary scan $payload cH4 ssl_record_content_type ssl_version] == 2 && \ ($tls_record_content_type == 22) && \ ($tls_version == 0300)} { # SSLv3 detected set tls_handshake_prefered_version "0300" } elseif { [binary scan $payload H2x1H2 ssl_version handshake_protocol_message] == 2 && \ ($ssl_version == 80) && \ ($handshake_protocol_message == 01)} { # SSLv2 detected set tls_handshake_prefered_version "0200" } unset -nocomplain payload payloadlen tls_record_content_type tls_recordlen tls_handshake_action tls_handshake_sessidlen record_offset tls_ciphlen tls_complen tls_extension_length tls_extension_type tls_extension_record_length tls_supported_versions_length tls_supported_versions foreach version $tls_handshake_prefered_version { switch -glob -- $version { "0200" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : SSLv2 ; connection is rejected"} reject return } "0300" - "0301" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : SSL/TLS ; connection is rejected (0x$version)"} # Handshake Failure packet format: # # - Record layer content-type (1 byte) --> variable tls_record_content_type # Alert value is 21 (required for Handshake Failure packet) # - SSLv3 / TLS version. (2 bytes) --> from variable tls_version # - Record layer content length (2 bytes) : value is 2 for Alert message # - TLS Message (length defined by Record layer content length value) # - Level (1 byte) : value is 2 (fatal) # - Description (1 bytes) : value is 40 (Handshake Failure) TCP::respond [binary format cH4Scc 21 $tls_version 2 2 40] after 10 TCP::close #drop #reject return } "030[2-9]" - "7F[0-9A-F][0-9A-F]" { # TLS version allowed, do nothing break } "0000" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : No SSL/TLS protocol detected ; connection is rejected (0x$version)"} reject return } default { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : Unknown CLIENT_HELLO TLS handshake prefered version : 0x$version"} } } } if { $tls_servername equals "" || ([set sni_dg_value [class match -value [string tolower $tls_servername] equals tls_servername_routing_dg]] equals "")} { set sni_dg_value [class match -value "default" equals tls_servername_routing_dg] } switch [lindex $sni_dg_value 0] { "virtual" { if {[catch {virtual [lindex $sni_dg_value 1]}]} { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; Virtual server [lindex $sni_dg_value 1] doesn't exist"} } else { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; forwarded to Virtual server [lindex $sni_dg_value 1]"} } } "pool" { if {[catch {pool [lindex $sni_dg_value 1]}]} { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; Pool [lindex $sni_dg_value 1] doesn't exist"} } else { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; forwarded to Pool [lindex $sni_dg_value 1]"} } if {[lindex $sni_dg_value 2] equals "ssl_offload" && [info exists sslenable]} { eval $sslenable } } "node" { if {[catch {node [lindex $sni_dg_value 1]}]} { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; Invalid Node value [lindex $sni_dg_value 1]"} } else { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; forwarded to Node [lindex $sni_dg_value 1]"} } } "handshake_failure" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; connection is rejected (with Handshake Failure message)"} TCP::respond [binary format cH4Scc 21 $tls_handshake_prefered_version 2 2 40] after 10 TCP::close return } "reject" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; connection is rejected"} reject return } "drop" { if {$static::sni_routing_debug} {log local0. "[IP::remote_addr] : TLS server_name value = ${tls_servername} ; TLS prefered version = 0x${tls_handshake_prefered_version} ; connection is dropped"} drop return } } TCP::release }3.5KViews6likes10CommentsLoad balance messaging protocols like AMQP, MQTT, STOMP over TLS for ActiveMQ Artemis
Messaging protocols like AMQP, MQT, STOMP, OpenWire, HornetQ can all be load balanced trough the F5. Making the setup I encountered some issues. There is some specific configuration that needs to be apllied, which i will try to clearify. The setup: devices sending AMQP messages trough the F5 to a backend server over TLS. The F5 is configured as full proxy. The Virtual Server was pretty basic config. Standard Virtuals server, TCP, HTTP, SSL client & server profiles. Also websocket auto SNAT automap enabled. I selected the virtual server which had the correct certificate in the Clientssl profile. The client initiating the request has our root /& issuing CA trusted in their truststore. Using a tcpdump and wireshark, I saw the corect TLS handshakes, Application data over TLS is being send, but not much data and due an error *** the client sends a TCP reset. The F5 will only apply a profile, when that specific data is being detected (trigger). So yeah, there are TCP profiles to handle TCP and i have an SSL Client & server profile to handle TLS. Strangly it doesn't work. In the capture i took on the F5, wireshark sees the TLS application data as http-over-tls. hmm.. what if the F5 did the same? Then it would apply the http profile on the AMQP data, and that might screw things up. Disable the HTTP processing for that hostname, bingo. The next problem: the hostname, part off [HTTP::host] is not our event scopes. It is only activated when http profile is triggered. So the solution is to get the hostname, in this case the SNI (server name indication) from the TLS Client hello in eventCLIENTSSL_HANDSHAKE. And check the SNI value to disable http processing. caveat: this will only work for TLS 1.2. When TLS 1.3 is used with encrypted SNI, another solution is needed. Solution in short: it should work using a seperate virtual server with only TCP, SSL client & server profiles and have a load balancing default pool. If your setup is more complex and are reusing an existing VS, do the following. I use a combination of an iRule and datagrouplists to extract the SNI, disable HTTP processing and send it to the right pool. For a current setup the SNI is also inserted server-side. I think this step is optional but i'll paste the code too. datagrouplist dgl_vs01_sni_targetpool: messaging.company.local and value "the targetpool" datagrouplist dgl_vs01_disable_http_procesin: messaging.company.local without a value when CLIENTSSL_HANDSHAKE { if { [SSL::extensions exists -type 0] } { set dgl "dgl_vs01_sni_targetpool" set dgl_nohttp "dgl_vs01_disable_http_processing" # read SNI value and place into variable sni_value binary scan [SSL::extensions -type 0] {@9A*} sni_value log local0. "sni value: [expr {[info exists sni_value] ? ${sni_value} : {not found} }]" #disable HTTP processing for AMQP, MQTT, STOMP, etc if { [class match $sni_value equals $dgl_nohttp] }{ HTTP::disable log local0. "HTTP Disabled for $sni_value" } if { [class match $sni_value equals $dgl] }{ set pool_target [class match -value [string tolower $sni_value] equals $dgl] pool $pool_target log local0. "pool chosen for $sni_value, pool $pool_target " } } } when SERVERSSL_CLIENTHELLO_SEND { #Inject SNI serverside if { [class match $sni_value equals $dgl] }{ SSL::extensions insert [binary format SSScSa* 0 [expr { [set sni_length [string length $sni_value]] + 5 }] [expr { $sni_length + 3 }] 0 $sni_length $sni_value] log local0. "SNI inserted $sni_value" } } For those less comfterable with iRule, you can do SNI-based load balancing like thishttps://community.f5.com/t5/technical-articles/sni-routing-with-big-ip/ta-p/282018 but I'm not sure how to disable HTTP processing with a policy in the ssl client hello. If someone knows please post below, thx. Now you are all set! Good luck with your F5 adventures. Documentation used: https://clouddocs.f5.com/training/community/irules/html/class1/module1/iRuleEventsFlowHTTPS.html https://community.f5.com/t5/crowdsrc/serverside-sni-injection-irule/ta-p/286745 https://community.f5.com/t5/crowdsrc/extracting-the-sni-server-name/ta-p/288029 https://activemq.apache.org/components/artemis/documentation/1.5.1/protocols-interoperability.html3.3KViews0likes0CommentsHow can I configure Server SSL Profiles to connect to different URLs on the same server?
Hi, We have a web server which has two sites published on it via a single Virtual Server on the BIG-IP: site1.domain.uk site2.domain.uk Our security policy dictates that we must encrypt the connections between the user and the BIG-IP and between the BIG-IP and the web server. We initially purchased a SAN certificate with site1.domain.uk and site2.domain.uk on it (site1.domain.uk is the default name). We have tried various methods of getting the end to end connectivity working with a user connecting using both URLs but all have failed. Can anyone provide any guidance on how to achieve this?Solved2.1KViews0likes19CommentsMultiple HTTPS sites on a single IP
I'm trying to host multiple HTTPS sites with different HTTP backends on a single IP address, following https://support.f5.com/csp/article/K13452 as much as possible. Version: BIG-IP 13.1.1 Build 0.0.4 Final I have imported my wildcard certificate and have configured the following: Client SSL profiles domain.com: No "Server Name" "Default SSL Profile for SNI" checked sub1.domain.com: Parent: "domain.com" Server Name: "sub1.domain.com" "Default SSL Profile for SNI" NOT checked sub2.domain.com Parent: "domain.com" Server Name: "sub2.domain.com" "Default SSL Profile for SNI" NOT checked Virtual Servers sub1 Destination 10.0.0.1 Service port: 443 HTTPS HTTP profile: http SSL Profile (Client): domain.com + sub1.domain.com Source Address Translation: Auto Map sub2 Destination 10.0.0.2 Service port: 443 HTTPS HTTP profile: http SSL Profile (Client): domain.com + sub2.domain.com Source Address Translation: Auto Map But as mentioned I only have a single IP address, but when I change the destination on VS for sub2 to "10.0.0.1" I'm presented with this error: 01070333:3: Virtual Server /Common/sub2 illegally shares destination address, source address, service port, ip-protocol, and vlan with Virtual Server /Common/sub1 I was under the impression that because I'm using multiple SSL profiles with specific Server Names it should be possible to host this on a single IP.2KViews0likes7CommentsSNI Routing with DNS Lookup
SNI (Server Name Indication) is an extension of the TLS protocol that is used by the client to indicate the hostname it is attempting to connect to at the start of the SSLhandshake. In the case of the BIG-IP the SNI can be used to select which client SSL profile should be applied to the ingress traffic but it requires at least one client SSL profile to be attached to the virtual server. In other words the SSL data must still be decrypted. This iRule which is suitable for some SSLO use cases does not require SSL traffic to be terminated (i.e. decrypted) on the BIG-IP. It only needs a generic SSL persistence profile to be attached to the virtual server and this just to circumvent MCP validation issues. Once the CLIENTSSL_CLIENTHELLO iRule event is triggered, the SNI can then be determined and used to steer traffic. In this example below, once the SNI is found, an attempt is made to resolve it using DNS and then route the request using the BIG-IP to a gateway which could (in the case of SSLO) be a Secure Web Gateway device. Note SSL::extensions -type <extension type value> returns the opaque extension byte array corresponding to the specified extension type value, or an empty string if not found. Extension Type 0 is the server name. The "node" command is useful if you want to send traffic to a specific IP/port combination that is not defined as a pool member iRule [root@sslo:Active:Standalone] config # tmsh list ltm rule bluecoat ltm rule bluecoat { when RULE_INIT { set static::resolver "8.8.8.8" } when CLIENT_ACCEPTED { HTTP::disable } when CLIENTSSL_CLIENTHELLO { set s_sni "NULL" set sni_exists [SSL::extensions exists -type 0] if {$sni_exists} { binary scan [SSL::extensions -type 0] @9a* s_sni log local0. "sni: $s_sni" if { [catch "RESOLV::lookup @$static::resolver -a ${s_sni}" addrs] } { log local0. "DNS resolution error" reject } else { log local0. "Resolved: [lindex ${addrs} 0]" node [lindex ${addrs} 0] [TCP::local_port] } } } }2KViews2likes3CommentsSSL Orchestrator Use Case: Inbound SNI Switching
Introduction SSLO will generate a single set of SSL profiles for use in a topology. It may be useful, especially in an inbound gateway mode, to process traffic to multiple sites, requiring different server certificates. The use case is to employ native BIG-IP SNI switching in SSLO, such that an SSLO topology can select a correct client SSL profile and server certificate based on the incoming SNI. In this example we have a single web server with multiple IP addresses hosting different web site domains: en.appserverone.com resides on 10.1.10.90 en.appservertwo.com resides on 10.1.10.91 When an external client requests https://en.appserverone.com we want the SSL Orchestrator to use a specific keypair for the sessions and direct the traffic to 10.1.10.90.When an external client requests https://en.appservertwo.com we want the SSL Orchestrator to use a different keypair for the sessions and direct the traffic to 10.1.10.91. Configuration Steps Import Private Keys and Certificates Create Client SSL Profiles Create a String Data Group* Import iRules Attach iRule to existing Topology Attach iRule to Existing Application Topology Attach iRule to existing Topology Interception Rule (alternative) *Note: You can either use an iRule and Data Group to match SNI to a Profile, or a static SNI-to-profile mapping in an iRule.The advantage of using a Data Group is all management is done at the Data Group. Import the Private Key and Certificate for the different web site domains From the BIG-IP Configuration Utility go to SSL Orchestrator > Certificates Management > Certificates and Keys. Click Import on the right. For the Import Type select Key. Give it a name, en.appserverone.com in this example.For the Key Source you can upload a file or paste in the text.We’ll use the Paste option which you can see below.Click Import when done. Click on the Key Name created in the previous step. Click Import. For the Certificate Source you can upload a file or paste in the text.We’ll use the Paste option which you can see below.Click Import when done. Repeat these steps for other web site domains.In this example we will add one more, en.appservertwo.com as you can see below. Create a Client SSL Profile for each certificate/key pair From the BIG-IP Configuration Utility go to SSL Orchestrator > Components > Profiles > Client SSL. Click Create on the right. Give it a name, en.appserverone.com in this example.Select the Custom box on the far right then click Add for the Certificate Key Chain. Select the Certificate and Key created previously and click Add.A Passphrase and Chain can be specified if needed.Click Add when done. Select the Advanced option next to Configuration. Scroll down and find the Server Name field.Enter the FQDN that external clients will request, en.appserverone.com in this example. Note: when an external client requests https://en.appserverone.com their TLS Client Hello will contain an extension value for ‘server_name’ field with a value of ‘en.appserverone.com’.We’re instructing SSL Orchestrator to use this Client SSL Profile when it receives this type of request from a client. Scroll to the bottom and click Finished when done. Repeat these steps for other web site domains.In this example we will add one more, en.appservertwo.com as you can see below. Create a String Data Group that maps the SNI to the Client SSL Profile. This step is only required if using the ‘in-t-rule-datagroup’ iRule. From the BIG-IP Configuration Utility go to Local Traffic > iRules > Data Group List. Click Create over on the right. Give it a name, SNI_switcher in this example and select String next to Type. Create a String that maps the SNI to the Client SSL Profile name created previously.Click Add. Note: If creating these profiles in other partitions, or if the VIP runs in another partition, it’s useful to prepend the partition name to avoid confusion. So, in this case, the client SSL profile would be: /Common/en.appserverone.com Repeat this step for as many mappings as you require.We’re doing two in this example and it should look like the following.Click Finished when done. Import iRules We will create two iRules from the following 1.library-rule: This rule is required. The next two iRules will make calls to the Library iRule to check the TCP payload for the ‘server_name’ field in the TLS Client Hello. 2.in-t-rule-datagroup: This iRule depends upon a ‘Data Group’ to map the SNI to the Client SSL Profile 3.in-t-rule-static: This is a static iRule does not depend upon a ‘Data Group’.This iRule contains the mapping of theSNI(s) to the Client SSL Profile(s) Note: Rules 2 and 3 perform the same function in a slightly different manner.We will cover both options but you can pick whichever one you want to use. Note: Using a data group or static iRule to switch the client SSL profile, requires a binary-scanning TCP iRule that may have an impact on performance Click Create over on the Right. Give it the name library-rule . Copy and paste the following into the Definition field: proc getSNI { payload } { set detect_handshake 1 binary scan ${payload} H* orig if { [binary scan [TCP::payload] cSS tls_xacttype tls_version tls_recordlen] < 3 } { reject return } ## 768 SSLv3.0 ## 769 TLSv1.0 ## 770 TLSv1.1 ## 771 TLSv1.2 switch $tls_version { "769" - "770" - "771" { if { ($tls_xacttype == 22) } { binary scan ${payload} @5c tls_action if { not (($tls_action == 1) && ([string length ${payload}] > $tls_recordlen)) } { set detect_handshake 0 } } } "768" { set detect_handshake 0 } default { set detect_handshake 0 } } if { ($detect_handshake) } { ## skip past the session id set record_offset 43 binary scan ${payload} @${record_offset}c tls_sessidlen set record_offset [expr {$record_offset + 1 + $tls_sessidlen}] ## skip past the cipher list binary scan ${payload} @${record_offset}S tls_ciphlen set record_offset [expr {$record_offset + 2 + $tls_ciphlen}] ## skip past the compression list binary scan ${payload} @${record_offset}c tls_complen set record_offset [expr {$record_offset + 1 + $tls_complen}] ## check for the existence of ssl extensions if { ([string length ${payload}] > $record_offset) } { ## skip to the start of the first extension binary scan ${payload} @${record_offset}S tls_extenlen set record_offset [expr {$record_offset + 2}] ## read all the extensions into a variable binary scan ${payload} @${record_offset}a* tls_extensions ## for each extension for { set ext_offset 0 } { $ext_offset < $tls_extenlen } { incr ext_offset 4 } { binary scan $tls_extensions @${ext_offset}SS etype elen if { ($etype == 0) } { ## if it's a servername extension read the servername set grabstart [expr {$ext_offset + 9}] set grabend [expr {$elen - 5}] binary scan $tls_extensions @${grabstart}A${grabend} tls_servername_orig set tls_servername [string tolower ${tls_servername_orig}] set ext_offset [expr {$ext_offset + $elen}] break } else { ## skip over other extensions set ext_offset [expr {$ext_offset + $elen}] } } } } if { ![info exists tls_servername] } { ## This isn't TLS so we can't decrypt it anyway return "null" } else { return ${tls_servername} } TCP::release } Click Finished when Done From the BIG-IP Configuration Utility go to Local Traffic > iRules > iRule List. Click Create over on the Right. Give it a name, in-t-rule-datagroup in this example.Copy and paste the following into the Definition field: ## SSLO SNI Switching Rule ## Date: July 2020 ## Purpose: Useful in SSLO versions 8.x and below to switch the client SSL profile based on ClientHello SNI ## in inbound SSLO topologies. This would be practical for L3 inbound gateway mode or L2 inbound topologies. ## Instructions: ## - Import server certificates and private keys ## - Create a separate client SSL profile for each cert/key pair ## - Create a string data group that maps the SNI to the client SSL profile name (ex. test0.f5labs.com := test0-clientssl) ## - Add the library-rule iRule to LTM (name "library-rule") ## - Add the SNI-switching-rule to LTM (name is arbitrary) ## - Create an L3 inbound gateway mode SSLO topology. Define a server cert/key to be used as the "default" (when no SNI matches) ## - Edit the L3 inbound Interception Rule and add the SNI switching rule. Deploy and test. when CLIENT_ACCEPTED priority 250 { TCP::collect } when CLIENT_DATA priority 250 { ## call the external procedure set sni [call library-rule::getSNI [TCP::payload]] ## lookup SSL profile in data group set sslprofile [class lookup ${sni} sni-switching-dg] if { ${sslprofile} ne "" } { set cmd "SSL::profile /Common/${sslprofile}" ; eval $cmd } TCP::release } Click Finished when done. Click Create over on the Right. Give it a name, in-t-rule-static in this example.Copy and paste the following into the Definition field: when CLIENT_ACCEPTED priority 250 { TCP::collect } when CLIENT_DATA priority 250 { ## call the external procedure set sni [call library-rule::getSNI [TCP::payload]] switch [string tolower ${sni}] { "en.appserverone.com" { set cmd1 "SSL::profile /Common/en.appserverone.com" ; eval $cmd1 } "en.appservertwo.com" { set cmd2 "SSL::profile /Common/en.appservertwo.com" ; eval $cmd2 } } TCP::release } Note: this iRule requires customization.The items in Blue are the Server Names that external clients will request.The items above in Red are the names of the Client SSL Profiles created previously. Click Finished when done Attach iRule to existing Inbound Topology If you have an existing Topology you can attach the iRule to that Topology.To do this you will need to disable Strictness on the Topology.From the BIG-IP Configuration Utility select SSL Orchestrator > Configuration.Click the padlock on the right to Unprotect the Configuration. Click OK when presented with the following message. Note: Disabling Strictness on the Topology is needed to modify it and add multiple client-ssl profiles. Select Local Traffic > Virtual Servers.Click the name of your Topology, sslo_Inbound_Topology in this example. Scroll down to the SSL Profile (Client).Add the SSL Profiles created previously, 10.1.10.90 and 10.1.10.91 in this example.You also need a default SNI SSL Profile in case no SNI matches.We’re using clientssl in this example.Your screen should look similar to the following. Click Update at the bottom when done. Select Resources so we can attach the iRule. In the iRule section select Manage. Locate the iRule in the Available field.Select the one you want to use, in-t-rule-static in this example, then click the << to move it to Enabled.Click Finished when done. The configuration is now complete.When external clients request en.appserverone.com or en.appservertwo.com the correct Client SSL profile will be used. Existing Application Topology SSL Orchestrator manages the services and service policies for existing applications while an existing ADC application manages its own SSL settings and application delivery, consuming the SSL Orchestrator service policy information. The Existing Application topology is available for SSL Orchestrator addon licensed devices. ·Traffic flows into the BIG-IP. (This is configured outside of this topology.) ·Traffic is decrypted and sent to security devices. ·Traffic is re-encrypted and sent out to the applications. (This is configured outside of this flow.) From the BIG-IP Configuration Utility select SSL Orchestrator > Configuration. Scroll down and click Next. Give it a name, Inbound in this example.Choose the Existing Application option and click Save & Next. On the Services List screen you can Add Services now or do so later.In this example we’ll have a Generic L3 Inline Service.Click Save & Next. On the Services Chain List screen you can Add or Edit a Service Chain.In this example it’s named ServiceChain.Click Save & Next. On the Security Policy screen click the pencil icon on the right to edit the Rule. Select the Service Chain and click OK. Click Save & Next. Review the configuration summary and make any changes as needed.Otherwise click Deploy. Add iRule and SSL Profiles to the Virtual Server From the BIG-IP Configuration Utility select Local Traffic > Virtual Servers.Click the name of the Virtual Server you want to configure, SNI in this example. Scroll down to the SSL Profile (Client) section.Highlight the SSL Profiles created previously and click << to move them to Selected. Scroll to the bottom and click Update. Select Resources to attach the iRule. Click Manage to the right of iRules. Select the iRule you want to use, in-t-rule-static in this example.Click << to move it to Enabled.Click Finished. The configuration is now complete. Attach iRule to existing Topology Interception Rule (alternative) Assign an iRule to the Interception Rule ·This incurs a performance hit without the SSL profiles attached to a VIP, but doesn’t require default-for-sni to be configured and does not require Strictness to be disabled on the Topology. From the BIG-IP Configuration Utility select SSL Orchestrator > Configuration. Select Interception Rules then click on the name of your Interception Rule, sslo_Inbound_App in this example. Click the pencil icon to edit the rule. Scroll down until you get to the section on iRules.Select the iRule you want to use, in-t-rule-static in this example and click the arrow to move it to Selected. Click Save & Next at the bottom. Click Deploy to complete the configuration. The configuration is now complete.1.9KViews0likes5Comments