Forum Discussion
APM Irule multiple access profile for one virtual server
Since I am apparently not able to edit, there was a small point of clarification about assigning the apm policy to the virtual servers which would be called from this irule:
What you are asking is quite easy to do. I cannot take full credit for it completely. I took the SNI irule published by Joel Moses a few years back and changed it to simply extract the SNI and put that info into the variable called "tls_servername"
If you look at the very bottom, you will see some generic logic to make a virtual call to the value of tls_servername and if that is unsuccessful, to look up the mapping in a datagroup called "Content_Switch_dg" which is simply a mapping of requested hostname to virtualserver name. As above, you would then create and apply your APM policy to the appropriate virtuals and everything would be nicely published behind a single public IP.
when CLIENT_ACCEPTED {
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 default_tls_pool [LB::server pool]
set detect_handshake 1
TCP::collect
}
when CLIENT_DATA {
set detect_handshake 1
TCP::collect
if { ($detect_handshake) } {
If we're in a handshake detection, look for an SSL/TLS header.
binary scan [TCP::payload] cSS tls_xacttype tls_version tls_recordlen
TLS is the only thing we want to process because it's the only
version that allows the servername extension to be present. When we
find a supported TLS version, we'll check to make sure we're getting
only a Client Hello transaction -- those are the only ones we can pull
the servername from prior to connection establishment.
switch $tls_version {
"769" -
"770" -
"771" {
if { ($tls_xacttype == 22) } {
binary scan [TCP::payload] @5c tls_action
if { not (($tls_action == 1) && ([TCP::payload length] > $tls_recordlen)) } {
set detect_handshake 0
}
}
}
default {
set detect_handshake 0
}
}
if { ($detect_handshake) } {
If we made it this far, we're still processing a TLS client hello.
Skip the TLS header (43 bytes in) and process the record body. For TLS/1.0 we
expect this to contain only the session ID, cipher list, and compression
list. All but the cipher list will be null since we're handling a new transaction
(client hello) here. We have to determine how far out to parse the initial record
so we can find the TLS extensions if they exist.
set record_offset 43
binary scan [TCP::payload] @${record_offset}c tls_sessidlen
set record_offset [expr {$record_offset + 1 + $tls_sessidlen}]
binary scan [TCP::payload] @${record_offset}S tls_ciphlen
set record_offset [expr {$record_offset + 2 + $tls_ciphlen}]
binary scan [TCP::payload] @${record_offset}c tls_complen
set record_offset [expr {$record_offset + 1 + $tls_complen}]
If we're in TLS and we've not parsed all the payload in the record
at this point, then we have TLS extensions to process. We will detect
the TLS extension package and parse each record individually.
if { ([TCP::payload length] > $record_offset) } {
binary scan [TCP::payload] @${record_offset}S tls_extenlen
set record_offset [expr {$record_offset + 2}]
binary scan [TCP::payload] @${record_offset}a* tls_extensions
Loop through the TLS extension data looking for a type 00 extension
record. This is the IANA code for server_name in the TLS transaction.
for { set x 0 } { $x < $tls_extenlen } { incr x 4 } {
set start [expr {$x}]
binary scan $tls_extensions @${start}SS etype elen
if { ($etype == "00") } {
A servername record is present. Pull this value out of the packet data
and save it for later use. We start 9 bytes into the record to bypass
type, length, and SNI encoding header (which is itself 5 bytes long), and
capture the servername text (minus the header).
set grabstart [expr {$start + 9}]
set grabend [expr {$elen - 5}]
binary scan $tls_extensions @${grabstart}A${grabend} tls_servername
set start [expr {$start + $elen}]
} else {
Bypass all other TLS extensions.
set start [expr {$start + $elen}]
}
set x $start
}
Check to see whether we got a servername indication from TLS. If so,
make the appropriate changes.
if { ([info exists tls_servername] ) } {
log local0. "Requested host: $tls_servername"
if { [catch { virtual $tls_servername } ]} {
if { $::DEBUG == 1 } { log local0. "Virtual Server $tls_servername doesn't exist...Searching Datagroup"}
if { [class match $tls_servername equals Content_Switch_dg]} {
if { $::DEBUG == 1 } { log local0. "Server found in Datagroup" }
virtual [class lookup $tls_servername Content_Switch_dg]
TCP::release
}
} else {
if { $::DEBUG == 1 } { log local0. "switching to virtual server $tls_servername" }
virtual $tls_servername
TCP::release
}
}
} else {
Servername not found
log local0. "Servername could not be determined"
}
}
}
}
- Jeppe_KoefoedJan 28, 2016EmployeeThe SNI part can be done easier: when CLIENTSSL_HANDSHAKE { set sni_exists [SSL::extensions exists -type 0] log local0.info "SSL extension type 0 exists: $sni_exists" if {$sni_exists} { set scan [binary scan [SSL::extensions -type 0] S1S1@9A* ext_type ext_len sni] log local0.info "SSL extension type 0 (SNI): $sni" } }
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com