
Deploy new sites faster and improve IP address utilization with name based virtual host pool resolution on F5 LTM. Configuring a Virtual Server as described below will allow your F5 to support multiple Drupal (and other) websites on a single IP while supporting custom redirects and SSL/SNI.
At CU-Boulder HDS, we use a pair of F5's to route traffic across a redundant server pool (I'll post about that another time) and faced issues associated with a growing number of websites requiring address space on an already heavily-utilized block of 256 IP addresses. After we received a request for a redirect which could only be implemented (at that time) by deploying yet another IP address, I searched for a method to implement an Apache style Name Based Virtual Host on the F5. As such, our needs were as follows:
- Support multiple websites requiring SSL with SNI
- Allow redirection of traffic from HTTP to HTTPS
- Permit sites to function entirely as redirects to other sites (eg. redirect cuhousing.colorado.edu to housing.colorado.edu)
Prerequisites
In order to take full advantage of this article, you should have following available.
- Understanding of general configuration of SSL profiles on F5 LTM
- Understanding of general configuration of Virtual Servers on F5 LTM
- Existing pools configured within F5 LTM
- Available network addresses, DNS entries (wildcard testing A Records are helpful!)
Preparing SSL profiles to support SNI
Let's start off the SSL section with some clarification regarding Server Name Indication (SNI) support on the F5: Since LTM v11.1.0 there is native support for Virtual Servers to accept multiple SSL certificates. There are numerous articles referencing older SNI implementation options, but you can implement SNI through configuration only. Prior to LTM v11.1.0 it was possible to support SNI via iRules thanks to Joel Moses's iRule; however, you should utilize the built in SNI support unless you need super-advance-SSL-support™. Thanks to Joel for his great code as I was able to use the example understand class match support. If you're thinking about using Joel's rule, consider reading on.
Begin by configuring an SSL profile which you would like to fallback to if SNI is not support by the browser requesting your site. In our case, we simply editing the built in "wom-default-clientssl" certificate since we wanted to avoid causing conflicts with default configurations when moving between virtual servers. Once you're satisfied with your usual configuration, select the advance configuration dropdown and check the two checkboxes to the right of "Default SSL Profile for SNI." One checkbox enables the option and the other enables the profile as default.
Create a second SSL profile for a domain you will to have hosted on the same server virtual server.
iRules and dependent data groups
When I initially started working with iRules, I was confused about where data groups are setup. Within the UI, you can create data groups under Local Traffic > iRules > Data Group List.
Create a string data group entitled "Name_Based_VirtualServer" consisting of the following:
String: example.com
Value: example_pool
example.com should be replaced with a lower case hostname for the site you wish you direct to the pool.
Create a string data group entitled "Name_Based_VirtualServer_Redirect" consisting of the following:
String: example.org
Value: example.com
example.org should be replaced with a lower case hostname for the site you wish to redirect to the site URL defined in its paired value. In this case, example.org will redirect to example.com.
Create an integer data group entitled "Name_Based_VirtualServer_Ports" consisting of the following:
Integer: 80
Value: 80
Integer: 443
Value: 443
This data group will define which ports to accept traffic on. Be sure to allow 80 and 443, but also 1443 or 8080 in certain use cases.
Define an iRule entitled "Name_Based_VirtualServer"
To understand the mechanism by which the iRule below functions, review the iRule Event Order Flowchart (courtesy F5 forums user "What Lies Beneath"). The implementation I have defined below is similar to the Name Based Virtual Hosting with LTM except the rule below uses data groups which improves portability between systems and even Virtual Servers on the same F5.
Text Version of code belowwhen RULE_INIT {
# Requests to ports not defined in either the ports list will be reset
# Define virtual server ports that should respond to requests
# in an integer type datagroup (example: 80, 443, etc)
# and update the name here from Name_Based_VirtualServer_Ports to the data group name you create
set static::web_ports "Name_Based_VirtualServer_Ports"
# Define domains which should be redirected when requests are received
# in a string type datagroup (example: domain.com (string) redirecturl (value))
# and update the name here from Name_Based_VirtualServer_Redirect to the data group name you create
set static::web_redirect "Name_Based_VirtualServer_Redirect"
# Set this option to 1 to redirect client requests from HTTP to HTTPS. Set to 0 to not redirect clients from HTTP to HTTPS.
set static::redirect_http_to_https 1
# Set this option to 1 to log debug messages (to /var/log/ltm by default)
set static::single_vs_debug 1
}
when CLIENT_ACCEPTED {
if { not([matchclass [TCP::local_port] equals $static::web_ports]) }{
# Request wasn't to a defined port, so reset the TCP connection.
if {$static::single_vs_debug}{log local0. "$log_prefix:\
Dropping request to undefined port [IP::local_addr]:[TCP::local_port]"}
reject
}
}
when HTTP_REQUEST {
set redirect_domain [class match -value [string tolower [HTTP::host]] equals $static::web_redirect]
if { not ($redirect_domain == "") } {
HTTP::redirect "$redirect_domain"
} else {
# Redirects to HTTPS address if redirect_http_https is set
if { $static::redirect_http_to_https == 1 && [TCP::local_port] == 80 }{
HTTP::respond 301 Location "https://[getfield [HTTP::host] : 1][HTTP::uri]"
}
set named_pool [class match -value [string tolower [HTTP::host]] equals Name_Based_VirtualServer]
if { not ($named_pool == "") } {
pool $named_pool
} else {
# Rejects connection
reject
# Consider using the below configuration to default to specified pool.
#pool [LB::server pool]
}
}
}
Tie Everything together with a Virtual Server
Add a new Virtual Server (or modify existing) with the following settings:
- Service Port: 0 / All Ports
- HTTP Profile: http
- SSL Profile (Client): Add both previously defined SSL profiles above.
- Source Address Translation: Automap (in our use case, you may be able to skip this)
- Port Translation: Enabled (Warning, this gets disable upon enable service port: all ports. Recheck this setting if you are having connection issues.
- iRules: Name_Based_VirtualServer
Your F5 should now support multiple hostnames on a single virtual server. Hope that helps!