Forum Discussion

TMcGov_92811's avatar
TMcGov_92811
Icon for Nimbostratus rankNimbostratus
Jan 31, 2014

Replacing hostname in HTTP RESPONSE header using stream profile and/or Irule

We are trying to allow access from the internet into an internal webserver running on port 8010. The webserver is coded with its internal hostname (host15.mycompany.com:8010) which is only resolvable within the company. We have assigned a public DNS name for the LTM Virtual Server (public.mycompany.com:443) which will terminate SSL and send traffic to the internal pool member of host15.mycompany.com:8010. Since the webserver's HTTP responses all refer to the internal hostname, external users on the internet are unable to resolve the name. Based on my research, it appears that we will need both a STREAM profile for the hostname find/replace as well as an HTTP profile (for SSL, etc). With a simple STREAM profile with the find/replace in the target field and no HTTP profile, the response to the client seems to be properly replaced, but generates a chunking error at the client. Applying an HTTP profile with the Rechunk option, the find/replace stops working, probably due to the fact that the entire TCP segment is no longer being scanned (only the HTTP payload). Is this assumption correct and if so, what are our options ? We have seen several iRule examples and applied the one below along with a HTTP profile with Rechunking, but the client gets a 504 errror despite the fact that the hostame replace seems to be successful. Any ideas ? This is version 10.2

when HTTP_RESPONSE {

 set internal_host "host15.mycompany.com:8010" 
 set external_host "public.mycompany.com:443"
 Disable the stream filter by default 
STREAM::disable 

 Check if we're potentially rewriting this response 
if {$internal_host ne ""}{ 

    Rewrite the Location header for redirects 
   if {[HTTP::is_redirect]}{ 

      if {[HTTP::header Location] contains "$internal_hostname"}{ 
         HTTP::header replace Location [string map "$internal_hostname $external_hostname" [HTTP::header Location]] 
 } 
   } 
    Rewrite the response content using a stream profile if it is text 
   if {[HTTP::header Content-Type] contains "text"}{ 

       Set the stream expression with the find/replace strings 
      STREAM::expression "@$internal_hostname@$external_hostname@" 

       Enable the stream filter 
      STREAM::enable 
   } 
} 

}

5 Replies

  • Maybe just a typo, but I notice the first two variable assignments in the code above show internal_host and external_host whereas most of the variable references that follow instead refer to internal_hostname and external_hostname.

     

  • A few additional things:

    1. You definitely need an HTTP profile for this, as the HTTP header replace logic requires it, and you specifically only want to capture and replace HTTP traffic anyway (not anything below HTTP).

    2. I would leave the request chunking option at Preserve.

    3. Would users actually reference the URL with the included port (ie. https://public.mycompany.com:443 vs. https://public.mycompany.com)?

      when HTTP_REQUEST {
           Disable the stream filter for requests
          STREAM::disable
      
           Remove this header to prevent server from compression response
          HTTP::header remove Accept-Encoding
      }
      when HTTP_RESPONSE {
          set internal_host "host15.mycompany.com:8010" 
          set external_host "public.mycompany.com"
      
           Rewrite the Location header for redirects 
          if { [HTTP::header exists Location] }{ 
              HTTP::header replace Location [string map "$internal_host $external_host" [HTTP::header Location]] 
          } 
      
           Rewrite the response content using a stream profile if it is text 
          if { [HTTP::header Content-Type] contains "text" } { 
      
               Set the stream expression with the find/replace strings 
              STREAM::expression "@$internal_host@$external_host@" 
      
               Enable the stream filter 
              STREAM::enable 
          } 
      } 
      
  • Maybe a minor modification is in order:

    when HTTP_REQUEST {
         Disable the stream filter for requests
        STREAM::disable
    
         Remove this header to prevent server from compression response
        HTTP::header remove Accept-Encoding
    }
    when HTTP_RESPONSE {
         Rewrite the Location header for redirects 
        if { [HTTP::header exists Location] }{ 
            HTTP::header replace Location [string map {"http://xyz.company.com:8010" "https://abc.company.com"} [HTTP::header Location]] 
        } 
    
         Rewrite the response content using a stream profile if it is text 
        if { [HTTP::header Content-Type] contains "text" } { 
    
             Set the stream expression with the find/replace strings 
            STREAM::expression "@xyz.company.com:8010@abc.company.com@" 
    
             Enable the stream filter 
            STREAM::enable 
        } 
    } 
    
  • Add multiple conditions to your string map:

    [string map {"old-1" "new" "old-2" "new" "old-3" "new"} [HTTP::header Location]]
    

    Add multiple conditions to your STREAM evaluation:

    STREAM::expression "@old-1@new@ @old-2@new@ @old-3@new@" 
    
  • Hello , Was there ever a solution for this? We are currently trying to mask an public domain (i.e. "support.com") between browser and the f5, but serve content from the back-end server with "myqadcloud.com" (from f5 to back-end server); and finally on each response from back-end server should ensure all domains used are "support.com" domain. Long story short, in theory, this method would prevent cross domain errors from IFrames within parent windows that contains a different domain name. So in a sense we are fooling the browser that the parent and iframe domain are the same, but the iframe will server content from a different back-end server (myqadcloud.com).

     

    Thoughts on how that can be done?

     

    Thanks in advance.

     

    Traolly Xiong