You are currently browsing the tag archive for the ‘IPv6’ tag.

Conserving public IP addresses has always been a good idea. Naturally, it’s become more important lately but that’s neither here nor there as far as this post goes.

Let’s assume you’re managing a website powered by an F5 BIG-IP LTM. You’ve got the following setup:

1. Virtual Server with IP Address 1.1.1.1 and listening on port 80.

2. A Pool called “pool_webservers” containing web servers 10.1.1.1:80, 10.1.1.2:80, 10.1.1.3:80, 10.1.1.4:80, and 10.1.1.5:80.

3. A DNS record “www.sample.com” pointing to the Virtual Server’s IP Address of 1.1.1.1

While the site is working fine, you’d like to be able to access individual web servers from an external network. This way, if a customer tells you your site isn’t working, you can test each server individually to try and narrow it down. Also, perhaps you’re releasing code to individual servers and would like to make sure it looks good.

This is a very common requirement for sites. Unfortunately, since your servers are using non-internet routable addresses from the 10.1.1.0 network, you can’t hit them externally.

People frequently deal with such an issue by doing one of the following:

1. Assign public IP addresses to each server and creating DNS records accordingly.

In this case, DNS might look like this: www1.sample.com=1.1.1.2, www2.sample.com=1.1.1.3, etc.

2. Create NATs on a public-facing router or Load Balancer to translate the public IPs to the server’s private ones.

In this case, DNS would look the same as above.

Unless you’re using port translation (1.1.1.2:80 = server 1, 1.1.1.2:1080 = server 2 etc,) then you’re using a Public IP address for each server you’d like to access. Since larger sites typically have far more than 5 servers, it’s each to chew up Public Addresses quickly.

Fortunately, we can use iRules to “route” requests to the proper web servers without using a single additional Public IP Address. From the DevCentral iRules Commands page, you’ll notice an event called “HTTP::host.” When a user types “www.sample.com” into their browser, their HTTP request contains an “HTTP Header” that contains the host (www.sample.com,) they requested.

If you’ll remember our layout, we have a Virtual Server at 1.1.1.1:80 served by “pool_webservers” with members 10.1.1.1:80, 10.1.1.2:80, 10.1.1.3:80, 10.1.1.4:80, and 10.1.1.5:80. http://www.sample.com points to 1.1.1.1 and is how users access the site. Now, we’d like the ability to target individual pool members from outside the network. Typically, this would require a public IP address for each web server but with iRules, we’re all set.

First, we’re going to create additional DNS records. Fortunately, they’re all going to point at the same 1.1.1.1 address as the other ones. Our DNS zone for “sample.com” now looks like this:

www IN A 1.1.1.1

www1 IN A 1.1.1.1

www2 IN A 1.1.1.1

www3 IN A 1.1.1.1

www4 IN A 1.1.1.1

www5 IN A 1.1.1.1

Now, it’s time to put together our iRule. As I was extremely inspired by Joe Pruitt’s recent post comparing iRule Control Statements, I thought I’d give multiple examples of how to accomplish our goal.

First, we’ll go with a simple “else, if” rule.

when HTTP_REQUEST {

if { [string tolower [HTTP::host]] eq “www1.sample.com” } {

pool pool_webservers member 10.1.1.1 80

} elseif { [string tolower [HTTP::host]] eq “www2.sample.com” } {

pool pool_webservers member 10.1.1.2 80

} elseif { [string tolower [HTTP::host]] eq “www3.sample.com” } {

pool pool_webservers member 10.1.1.3 80

} elseif { [string tolower [HTTP::host]] eq “www4.sample.com” } {

pool pool_webservers member 10.1.1.4 80

} elseif { [string tolower [HTTP::host]] eq “www5.sample.com” } {

pool pool_webservers member 10.1.1.5 80

}

}

Well, that was painless enough. If a user’s host header is “www1.sample.com,” we’re sending them to 10.1.1.1:80. Simply bind that iRule to our 1.1.1.1:80 Virtual Server and we’re set. You might also notice I’m using “string tolower.” That just converts the value to lowercase so I don’t have to support users inputting combinations of upper and lower case characters. Most browsers automatically convert the host header to lowercase but not all. If you read either “control statement” post above, you’ll notice that if/elses are hardly the most efficient method for doing something like this.

Now, we’ll try a “switch statement.”

when HTTP_REQUEST {

switch -glob [string tolower [HTTP::host]] {

“www1.sample.com” { pool pool_webservers member 10.1.1.1 80 }

“www2.sample.com” { pool pool_webservers member 10.1.1.2 80 }

“www3.sample.com” { pool pool_webservers member 10.1.1.3 80 }

“www4.sample.com” { pool pool_webservers member 10.1.1.4 80 }

“www5.sample.com” { pool pool_webservers member 10.1.1.5 80 }

default { pool pool_webservers }

}

}

This is a much cleaner, more efficient option. As you’ll notice, I used “-glob” with switch. Glob allows you to use wildcards and also look for patterns. If you read the above post comparing control statements, you’ll notice -glob isn’t as efficient as just using switch. Since we aren’t doing any pattern/wildcard matching here, you could easily leave off the -glob. I like to use it just in case I decide to add such enhancements later. I also used a “default” statement so requests not matching the other statements would go to our normal pool.

My personal preference is to use “classes/data groups.” A class is essentially a list that can be searched or matched. Typically, you have the field you’re matching and a value you can record should that value be matched. In version 10, the class features were greatly enhanced.

For our sample rule, our class could look like this:

class host_headers {

{

“www1.sample.com” { “10.1.1.1” }

“www2.sample.com” { “10.1.1.2” }

“www3.sample.com” { “10.1.1.3” }

“www4.sample.com” { “10.1.1.4” }

“www5.sample.com” { “10.1.1.5” }

}

}

In this case, “www1.sample.com” is what we’re matching against and “10.1.1.1” is the value we’d like to return. If simply using “class match,” we can ignore/omit the value on the right. If using “class search -value,” then we’re trying to return it. Here’s an example:

when HTTP_REQUEST {

if { [class match [string tolower [HTTP::host]] eq host_headers] } {

set hostvar [class search – value host_headers eq [string tolower [HTTP::host]]]

pool pool_webservers member $hostvar 80 }

}

The first thing we did was compare the host header to our class/datagroup called “host_headers.” If there’s a match, we set a variable called “hostvar” to the corresponding value. If the user requested “www1.sample.com,” for instance, the corresponding value in the class is “10.1.1.1.” So, now that “hostvar” = 10.1.1.1, we reference the variable in our pool command. So, the pool command essentially became “pool pool_webservers member 10.1.1.1 80.”

Joe’s “Comparing iRule Control Statements” showed that using classes was ridiculously efficient. Using classes can make it a bit more difficult to understand what an iRule does as it requires reading the rule and then reading the class contents. With that said, it’s very efficient and minimizes the amount of text within the rule. The ability to extract a value is very nice too.

To “complicate” things a bit, let’s assume you don’t want people outside of your IP space to access individual servers. If you’re releasing new code or price updates, there’s a fair chance you don’t want people hitting the system being worked on. To accomplish this, let’s create an address-type “data group/class.” containing the IP Address or Network we’d like to allow access. Let’s assume this class is called “allowed_access”

when HTTP_REQUEST {

if { [class match [string tolower [HTTP::host]] eq host_headers] and ! [class match [IP::client_addr] eq allowed_access] } {

HTTP::respond 403 “You’re not allowed!” }

else {

set hostvar [class search – value host_headers eq [string tolower [HTTP::host]]]

pool pool_webservers member $hostvar 80 }

}

Now, if a user requests one of our “specific server host-headers,” but doesn’t match the allowed IP addresses class, we’re going to respond with an HTTP 403. If they do match both conditions, the rule should operate normally.

While my examples used iRules to target specific servers using host headers, it shouldn’t stop there. Let’s say you’re administering tons of different sites similar to the following:

http://www.sample.com = main company page

http://www.domain.com = a domain registrar site you’re hosting

http://www.social.com = you’ve jumped on the social networking bandwagon and are hosting facebook variant

http://www.dating.com = self explanatory

It’s fair to assume you’d have different web servers hosting these sites. Typically, you’d have a different Virtual Server as well as the corresponding public IP as well. That’s not always necessary though. Using our switch statement from above, we can change our pool command a bit.

when HTTP_REQUEST {

switch -glob [string tolower [HTTP::host]] {

“www.sample.com” { pool pool_sample}

“www.domain.com” { pool pool_domain }

“www.social.com” { pool pool_social }

“www.dating.com” { pool pool_dating }

default { pool pool_default }

}

}

One of the more popular e-mail/forum signatures I see is “with iRules, you can.” I think this is a great example. Since LTM is a “Strategic Point of Control,” it can extract information such as the Host Header, or a Requested URI, and react to it.

Advertisements