Forum Discussion

Scott_Wozny_894's avatar
Scott_Wozny_894
Icon for Nimbostratus rankNimbostratus
Jun 13, 2012

Find and replace hostname strings in http payload

Dear iRule gurus,

 

 

First and foremost, I apologize for the length of this post. I've done a bunch of work on my own to get to this point and I just want to be sure all the relevant details are included. If, based upon the summary, you think you have a suggestion on multiple string replace in response payload, please feel free to post and I'll take it from there. :)

 

 

From the beginning, I have a situation where we need to upgrade our RSA server to 7.1. There is a web-based token management app on that server on TCP/7004 and I need to grant access over the Internet to my business partners and end users for token approval, deployment and activation. As I can’t rely on my business partners or users IT departments to allow Internet connections over a non-standard port, I have set up an externally accessible VS that responds over HTTPS with the public name’s cert and key installed and the pool member (the RSA server) is set to service TCP/7004. This works to get to the server itself and when I send a request for an invalid URL the 404 I get back is just fine so I know I’m getting through to the server. However, when I use the external name to get to one of the server’s applicaiton pages, the server immediately sends me a redirect to another page but uses the internal server name in the redirect which is not resolvable to the outside world and the connection breaks. BTW, I have begged RSA to let me install my publicly available cert/key on the server and change the name to the publicly resolvable name so I could do this all with a simple port translation but they have told me it’s not a supported configuration so now I need something to proxy the connections which is what I want to use the F5 for.

 

 

So, the next step was to fix the headers and switch out the external for internal names in each direction. I wrote an iRule that, on requests, swaps out the external for internal name and custom port (I tried it originally with just swapping out the name, but some pages wouldn’t load and when I tested internally, it looks like some requests need the port so I put it on all inbound requests whether it needs it or not and this seems to work fine). Then I wrote 2 additional events to replace the location in a redirect with my external name, allowing the browser to turn the request back around using the external name and letting the F5 switch that name back out for the internal name by the time it gets back to the RSA server. I look for locations with the port number in them first and then the ones with no port number. If I were to do it in the other order, the redirect location on some responses would include the TCP/7004 port number and the page load would break. Also, as there are no Location elements in a 200/OK (that I can see) I think I’ve done all I need to do with the headers.

 

 

Now here is the problem and why I’m reaching out to the forums to see if anyone else has gotten around this. When the RSA server produces the pages, inside the page payload many times the HREFs only refer to the URI with no server name and that works just fine, but there are also many locations in the payload where the internal name is referred in the HTML being sent back to the browser (terrible coding practice, I know, but RSA won't let me touch the code). When anything that refers to that name is clicked on, the browser returns a PNF because it can’t resolve that name. What I need is a way in iRules to replace every instance of the internal server name and port in a response (the responses never exceed 1MB) with the external server name before returning it to the user’s browser. I have found documentation showing how to replace the entire payload with some static data, or portions of the payload past a certain point, but I can’t find any way to do string based replacements on multiple instances inside the payload.

 

 

Has anyone run into this before? I can’t imagine I’m the first but I’ve dug through as much of the iRule documentation and these forums as I can find, but I can’t find a way to do this. I’ve included the iRule code I’ve written / borrowed thus far to handle the headers in case it’s relevant. Any suggestions would be appreciated. Also, this server is not worked very hard (on the order of about 10 sessions a day on a busy day) so efficiency is not a high priority. I just need something that works.

 

 

Thanks,

 

 

Scott

 

 

-----BEGIN CODE HERE-----

 

Translate the hostname of inbound requests from external name to

 

internal name and port

 

 

when HTTP_REQUEST {

 

if { [HTTP::host] equals "rsa.tst.publicdomain.com"} {

 

log local0. "Replacing Host header [HTTP::host] with internalservername.internaldomain.com:7004"

 

HTTP::header replace "Host" "internalservername.internaldomain.com:7004"

 

}

 

}

 

 

Translate the redirect location from internal name and port to

 

external name

 

 

This one goes first because doing the name and not the port (if

 

it's there) breaks the session

 

 

when HTTP_RESPONSE priority 100 {

 

if { [HTTP::header is_redirect]} {

 

HTTP::header replace Location [string map -nocase {internalservername.internaldomain.com:7004 rsa.tst.publicdomain.com} [HTTP::header value Location]]

 

log local0. "Replacing Location header internalservername.internaldomain.com:7004 with rsa.tst.publicdomain.com"

 

}

 

}

 

 

Translate the redirect location from internal name to external name

 

if no port is indicated

 

 

This one goes second to catch any Locations that don't have the port

 

number included

 

 

when HTTP_RESPONSE priority 200 {

 

if { [HTTP::header is_redirect]} {

 

HTTP::header replace Location [string map -nocase {internalservername.internaldomain.com rsa.tst.publicdomain.com} [HTTP::header value Location]]

 

log local0. "Replacing Location header internalservername.internaldomain.com with rsa.tst.publicdomain.com"

 

}

 

}

 

 

-----END CODE HERE-----

2 Replies

  • Hi Scott,

     

     

    Nice work in getting that far. Here's an example which should do what you want in rewriting the internal hostname to an external hostname for response headers and content and the reverse for requests.

     

     

    https://devcentral.f5.com/wiki/iRules.Proxy-Pass-Lite.ashx

     

     

    Note that you can combine the two replacements in a string map command:

     

    string map -nocase {internalservername.internaldomain.com:7004 rsa.tst.publicdomain.com internalservername.internaldomain.com rsa.tst.publicdomain.com} [HTTP::header value Location]]

     

     

    And you can do this same using STREAM::expression:

     

    https://devcentral.f5.com/wiki/iRules.stream__expression.ashx

     

     

    STREAM::expression {@internalservername\.internaldomain\.com:7004@rsa.tst.publicdomain.com@ @internalservername\.internaldomain\.com@rsa.tst.publicdomain.com@}

     

     

    or this might work too:

     

    STREAM::expression {@internalservername\.internaldomain\.com(:7004)?@rsa.tst.publicdomain.com@}

     

     

    Aaron
  • Hi Aaron,

     

     

    Thanks very much for the advice. It looks like the app is working through the F5 now. The techs still need to test it, but I was able to get everywhere in the app I could find so I think this was the answer. Much obliged.

     

     

    Scott

     

     

    In case anyone runs into this problem in the future, here is the final code for the iRule.

     

     

    -----BEGIN IRULE CODE HERE-----

     

    Translate the hostname of inbound requests from external name to

     

    internal name and port

     

    when HTTP_REQUEST {

     

    if { [HTTP::host] equals "www.rsa.externaldomain.com"} {

     

    log local0. "Replacing Host header [HTTP::host] with internalservername.internaldomain.com:7004"

     

    HTTP::header replace "Host" "internalservername.internaldomain.com:7004"

     

    }

     

    }

     

     

    when HTTP_RESPONSE {

     

    Translate the redirect location from internal name (and port, if

     

    applicable) to external name

     

    if { [HTTP::header is_redirect]} {

     

    HTTP::header replace Location [string map -nocase {internalservername.internaldomain.com:7004 www.rsa.externaldomain.com internalservername.internaldomain.com www.rsa.externaldomain.com} [HTTP::header value Location]]

     

    log local0. "Replacing Location header internalservername.internaldomain.com with www.rsa.externaldomain.com"

     

    }

     

     

    Translate payload of response from internal name (and port, if

     

    applicable) to external name

     

    if { [HTTP::header "Content-Type"] starts_with "text/" } {

     

    STREAM::expression "@internalservername.internaldomain.com:7004@www.rsa.externaldomain.com@"

     

    STREAM::enable

     

    log local0. "[IP::client_addr]:[TCP::client_port]: Enabled stream filter for internalservername.internaldomain.com:7004 -> www.rsa.externaldomain.com"

     

    }

     

    }

     

    -----END IRULE CODE HERE-----