Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

iRule to replace content of the location header on HTTP Responses

I'm trying to create an iRule that will detect if a web server is sending it's private IP address in the location header on it's responses. The logic is to read the location header in the responses, detect if the private IP addresses of the servers are included on them and replace the content of the location header to the website's URL, leaving everything else intact. This is the initial code I came up with. Should this work? Any better suggestions?

when HTTP_RESPONSE {
if { ( [HTTP::header Location] contains "10.0.0.11" ) || ( [HTTP::header Location] contains "10.0.0.12" ) } {
    [HTTP::header Location] replace "www.mywebsite.com" }
    }
0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER

One of the potential issues I see with your current "contains" approach is that there is the possibility of a match on IP addresses other than 10.0.0.11 and 10.0.0.12, such as 10.0.0.110, 10.0.0.120, 110.0.0.121, 210.0.0.119, etc. All of these "contain" one or the other of the strings you're comparing against. (The likelihood of these other IP addresses actually appearing is probably remote, so you may be just fine.) To completely avoid an unanticipated match, you can make the "contains" strings more specific - maybe incorporate slashes into your matching strings - "/10.0.0.11/" and "/10.0.0.12/" (or maybe even something like "https://10.0.0.11/";) - but you would want to make sure you understand how the application always formats the Location header when it responds with an IP address instead of a host name.

0
Comments on this Answer
Comment made 02-Apr-2018 by wlopez 339

Following your recommendations I made the if it a bit more specific.

I was doing some curl commands and I noticed the application is only sending out the location header on 302 redirect responses.

I also noticed that when the response does include the location header it does so with a the private IP address as the hostname and a URI path.

Since the application is doing ssl offloading on BigIP, I want to see some basic curl command tests against the virtual server directly from the BigIP.

Is there a way to replace only the hostname on the location header responses while keeping the URI path?

That is, if the response includes the location following header:

Location: http://10.0.0.11/abc

Replace only the hostname in the response so the client receives the following:

Location: https://www.mywebsite.com/abc

[root@bigip:Active:Changes Pending] ~ # curl -I -k https://VS_IP/abc

HTTP/1.1 302 Found

Date: Mon, 02 Apr 2018 20:36:34 GMT

Location: http://VS_IP/abc

Content-Type: text/plain

Content-Language: en

[root@bigip:Active:Changes Pending] ~ # curl -I http://10.0.0.11/abc

HTTP/1.1 302 Found

Date: Mon, 02 Apr 2018 20:36:34 GMT

Location: http://10.0.0.11/abc

Content-Type: text/plain

Content-Language: en

when HTTP_RESPONSE {
        if { ( [HTTP::header Location] contains "/10.0.0.11" ) || ( [HTTP::header Location] contains "10.0.0.12" ) } {
            [HTTP::header Location] replace "www.mywebsite.com" }
}
0
Comment made 02-Apr-2018 by crodriguez

To replace just the IP address portion in the Location header, there are a variety of techniques you could employ. For example, you could use the TCL "string map" function as shown below. Note: I split the IF condition into an IF-ELSEIF to target the appropriate string map "from string" - there are other ways you could do this (for example, with a regex) but I find this straightforward and easy to maintain:

when HTTP_RESPONSE {
    if { [HTTP::header Location] contains "/10.0.0.11" } {
        HTTP::header replace Location [string map {"/10.0.0.11" "/www.mywebsite.com/"} [HTTP::header Location]]
    } elseif { [HTTP::header Location] contains "/10.0.0.12" } {
        HTTP::header replace Location [string map {"/10.0.0.12" "/www.mywebsite.com/"} [HTTP::header Location]]
    }
}
0
Comment made 02-Apr-2018 by wlopez 339

Thanks.

That looks simple enough.

Didn't know about string map.

I'll try setting up a test environment to try it out.

Once I test it, I'll let you know the results.

0
Comment made 03-Apr-2018 by wlopez 339

I noticed I wasn't covering the possibility of the web server's private IP addresses in the Content-Location header.

I extended the iRule to cover this. I also changed the elseif by if as I want to cover the possibility of multiple matches on a single response.

This is what it looks like now:

when HTTP_RESPONSE {
if { [HTTP::header Location] contains "/10.0.0.11" } {
    HTTP::header replace Location [string map {"/10.0.0.11" "/www.mywebsite.com"} [HTTP::header Location]]
}
if { [HTTP::header Location] contains "/10.0.0.12" } {
    HTTP::header replace Location [string map {"/10.0.0.12" "/www.mywebsite.com"} [HTTP::header Location]]
}
if { [HTTP::header Content-Location] contains "/10.0.0.11" } {
    HTTP::header replace Content-Location [string map {"/10.0.0.11" "/www.mywebsite.com"} [HTTP::header Content-Location]]
}
if { [HTTP::header Content-Location] contains "/10.0.0.12" } {
    HTTP::header replace Content-Location [string map {"/10.0.0.12" "/www.mywebsite.com"} [HTTP::header Content-Location]]
}}

Should this code work?

0
Comment made 03-Apr-2018 by crodriguez

I might go with something a little different to see if we can make it more efficient. Your method above causes 4 IF checks on every connection. I think we could just check for a 302 response code and the presence of "/10.0.0.1" first to let most traffic bypass the rest of the tests. You could time various code samples to see which was most efficient. If you're running BIG-IP v13.1, there's also the new iRule Tracer Tool to help with performance testing.

when HTTP_REQUEST {
    # If server response is 302 and either HTTP Location or HTTP Content-Length headers
    # contain part of the server's IP addresses (10.0.0.11 or 10.0.0.12), check for and
    # make replacements as necessary to remove IP addresses. Otherwise ignore.
    if { [HTTP::status] == 302 && ( [HTTP::header Location] contains "/10.0.0.1" || [HTTP::header Content-Location] contains "/10.0.0.1" ) } {
        if { [HTTP::header Location] contains "/10.0.0.11" } {
            HTTP::header replace Location [string map {"/10.0.0.11" "www.mywebsite.com"} [HTTP::header Location]]
        } elseif { [HTTP::header Location] contains "/10.0.0.12" } {
            HTTP::header replace Location [string map {"/10.0.0.12" "www.mywebsite.com"} [HTTP::header Location]]
        }
        if { [HTTP::header Content-Location] contains "/10.0.0.11" } {
            HTTP::header replace Content-Location [string map {"/10.0.0.11" "www.mywebsite.com"} [HTTP::header Content-Location]]
        } elseif { [HTTP::header Content-Location] contains "/10.0.0.12" } {
            HTTP::header replace Content-Location [string map {"/10.0.0.12" "www.mywebsite.com"} [HTTP::header Content-Location]]
        }
    }
}
0
Comment made 03-Apr-2018 by wlopez 339

Just created the iRule.

This is what it looks like:

when HTTP_RESPONSE {
# If server response is 302 and either HTTP Location or HTTP Content-Length headers
# contain part of the server's IP addresses (10.0.0.11 or 10.0.0.12), check for and
# make replacements as necessary to remove IP addresses. Otherwise ignore.
if { [HTTP::status] == 302 && ( ( [HTTP::header Location] contains "/10.0.0.1" ) || ( [HTTP::header Content-Location] contains "/10.0.0.1" ) ) } {
    if { [HTTP::header Location] contains "/10.0.0.11" } {
        HTTP::header replace Location [string map {"/10.0.0.11" "/www.mywebsite.com" } [HTTP::header Location] ]
    } elseif { [HTTP::header Location] contains "/10.0.0.12" } {
        HTTP::header replace Location [string map {"/10.0.0.12" "/www.mywebsite.com" } [HTTP::header Location] ]
    }
    if { [HTTP::header Content-Location] contains "/10.0.0.11" } {
        HTTP::header replace Content-Location [string map {"/10.0.0.11" "/www.mywebsite.com" } [HTTP::header Content-Location] ]
    } elseif { [HTTP::header Content-Location] contains "/10.0.0.12" } {
        HTTP::header replace Content-Location [string map {"/10.0.0.12" "/www.mywebsite.com" } [HTTP::header Content-Location] ]
    }
}}

Doing a short Analytics capture on the virtual server I see both 302 and 304 response codes on the application.

Are the Location and Content-Location headers only sent on 302 responses?

0
Comment made 03-Apr-2018 by crodriguez

You'll have to check the server's response to see what headers are sent for the 302 and 304 response codes and adjust the iRule accordingly. This is the part of writing iRules that is "application specific." :-)

0
Comment made 04-Apr-2018 by wlopez 339

I just dis a simple edit adding and or for the two 30x response codes I see constantly on the Analytics report for the application.

Hopefully I'll be able to test it today.

This is what it looks like now:

when HTTP_RESPONSE {
# If server response is 302 or 304 and either HTTP Location or HTTP Content-Length headers
# contain part of the server's IP addresses (10.0.0.11 or 10.0.0.12), check for and
# make replacements as necessary to remove IP addresses. Otherwise ignore.
if { [HTTP::status] == ( 302 || 304 ) && ( ( [HTTP::header Location] contains "/10.0.0.1" ) || ( [HTTP::header Content-Location] contains "/10.0.0.1" ) ) } {
    if { [HTTP::header Location] contains "/10.0.0.11" } {
        HTTP::header replace Location [string map {"/10.0.0.11" "/www.mywebsite.com" } [HTTP::header Location] ]
    } elseif { [HTTP::header Location] contains "/10.0.0.12" } {
        HTTP::header replace Location [string map {"/10.0.0.12" "/www.mywebsite.com" } [HTTP::header Location] ]
    }
    if { [HTTP::header Content-Location] contains "/10.0.0.11" } {
        HTTP::header replace Content-Location [string map {"/10.0.0.11" "/www.mywebsite.com" } [HTTP::header Content-Location] ]
    } elseif { [HTTP::header Content-Location] contains "/10.0.0.12" } {
        HTTP::header replace Content-Location [string map {"/10.0.0.12" "/www.mywebsite.com" } [HTTP::header Content-Location] ]
    }
}}
0