For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

sjy2025's avatar
sjy2025
Icon for Nimbostratus rankNimbostratus
Jul 22, 2025
Solved

iRule, Traffic Policy or Re-Write Policy

 

Hi,

I have created an iRule that maps source addresses to a particular pool

when CLIENT_ACCEPTED {
if {[class match [IP::client_addr] equals clients1] } {
     pool POOL_1
   } elseif {[class match [IP::client_addr] equals clients2] } {
     pool POOL_2
   } elseif {[class match [IP::client_addr] equals clients3] } {
     pool POOL_3
   } elseif {[class match [IP::client_addr] equals clients4] } {
     pool POOL_4
   } elseif {[class match [IP::client_addr] equals clients5] } {
     pool POOL_5
   }
   else {
      pool POOL_6
     }
}

I have a re-write policy that changes the uri

https://example.com to https://examples.com/test

 

When the connection completes to the backend servers, they respond with their hostname in the browser.

 

I want to keep https://example.com on the client side but I'm not sure how to achieve this. I thought I could do a response within my re-write policy but this fails.

I then looked at traffic policies but already have an ASM policy attached

 

Should I try and achieve everything under one irule (if so how, might I do this)

  1. It replaces the original uri with a new uri (going to the server)
  2. Have my current source to pool mapping
  3. Replace the server hostname with the original uri https://example.com

Thanks for any pointers

  • you may see  https://hostnameA.test.com:666 in the browser for many reasons
    thats why I am asking if there is a redirect response (301 or 302) or there is something else

    anyway give this a try

    when HTTP_REQUEST 
    {
        STREAM::disable
        HTTP::header remove "Accept-Encoding"
    
    	if {[IP::addr [IP::client_addr] equals 192.168.1.1] } 
    	{
    		pool POOL_A
    		set selected_hostname "hostnameA.test.com"
    		HTTP::header replace Host $selected_hostname
    	} 
    	elseif {[IP::addr [IP::client_addr] equals 192.168.2.1] }
    	{
    		pool POOL_B
    		set selected_hostname "hostnameB.test.com"
    		HTTP::header replace Host $selected_hostname
    	} 
    	elseif {[IP::addr [IP::client_addr] equals 192.168.3.1] }
    	{
    		pool POOL_C
    		set selected_hostname "hostnameC.test.com"
    		HTTP::header replace Host $selected_hostname
    	}
    }
    
    when HTTP_RESPONSE 
    {
        if {[HTTP::header exists "Location"] && [info exists selected_hostname]}
        {
            set loc [HTTP::header "Location"]
            if {[string match "https://$selected_hostname:666*" $loc]} 
            {
                HTTP::header replace "Location" [string map "https://$selected_hostname:666 https://example.com" $loc]
            }
        }
        if {[HTTP::header "Content-Type"] contains "text" && [info exists selected_hostname]}
        {
    		STREAM::expression "@https://$selected_hostname@https://example.com@"
    		STREAM::enable
        }
    }

     

11 Replies

  • I will try this out tomorrow, thank you for your input, its very much appreciated

    • sjy2025's avatar
      sjy2025
      Icon for Nimbostratus rankNimbostratus

      Hi,

      Thought I would feedback, as I couldn't get the response to work.

      I have got the source > pool > to re-write working which is great, although it does still show the backend server, its a test environment and is reducing my admin, so thank you

      I found your iRule really useful for understanding as well

       

      I think the response isn't working as there is a unusual setup, which does the following

       

      client to uri = https://example.com

      F5 rewrites to https://hostnameA.test.com:666

      Then there is another redirects that sends to the

      F5 rewrites to https://other.test.com:443 (this is returned in the browser)

      I'm still looking at it

      Thanks

       

       

       

       

      • Injeyan_Kostas's avatar
        Injeyan_Kostas
        Icon for Nacreous rankNacreous

        You mention Keycloak,
        Do you use as IDP?

        You have to check what urls are defined in the config. Either it uses SAML or OAuth

  • There is a redirect

    The solution is partially working on a single test network.

    Client connects to uri > f5 has a re-write profile > that changes the uri to be the name of the back end server (Keycloak)

    https://hostnameA.test.com:666 

    In the browser of the client I get the https://hostnameA.test.com:666 which exposes the server name

    I'd like the client to not see this.

     

    I have this on multiple test environments, each with their own dedicated server 

     

     

     

    ....

    • Injeyan_Kostas's avatar
      Injeyan_Kostas
      Icon for Nacreous rankNacreous

      you may see  https://hostnameA.test.com:666 in the browser for many reasons
      thats why I am asking if there is a redirect response (301 or 302) or there is something else

      anyway give this a try

      when HTTP_REQUEST 
      {
          STREAM::disable
          HTTP::header remove "Accept-Encoding"
      
      	if {[IP::addr [IP::client_addr] equals 192.168.1.1] } 
      	{
      		pool POOL_A
      		set selected_hostname "hostnameA.test.com"
      		HTTP::header replace Host $selected_hostname
      	} 
      	elseif {[IP::addr [IP::client_addr] equals 192.168.2.1] }
      	{
      		pool POOL_B
      		set selected_hostname "hostnameB.test.com"
      		HTTP::header replace Host $selected_hostname
      	} 
      	elseif {[IP::addr [IP::client_addr] equals 192.168.3.1] }
      	{
      		pool POOL_C
      		set selected_hostname "hostnameC.test.com"
      		HTTP::header replace Host $selected_hostname
      	}
      }
      
      when HTTP_RESPONSE 
      {
          if {[HTTP::header exists "Location"] && [info exists selected_hostname]}
          {
              set loc [HTTP::header "Location"]
              if {[string match "https://$selected_hostname:666*" $loc]} 
              {
                  HTTP::header replace "Location" [string map "https://$selected_hostname:666 https://example.com" $loc]
              }
          }
          if {[HTTP::header "Content-Type"] contains "text" && [info exists selected_hostname]}
          {
      		STREAM::expression "@https://$selected_hostname@https://example.com@"
      		STREAM::enable
          }
      }

       

      • sjy2025's avatar
        sjy2025
        Icon for Nimbostratus rankNimbostratus

        This has solved my issue, specifically the HTTP_REQUEST section. The response has been dealt with by another means. Thanks for your help

  • Thats correct, a single uri to servers on different subnets

     I have a number of pools, each has a specfic node in it, listening on a port - 666

    Each pool has a remote test client that connects to the pool

     

    I'd like all test clients to hit the same uri of https://example.com then be redirected to a specific server (based on their source IP)

    client 1 uses 192.168.1.1

        goes to pool A and https://example.com redirects to hostnameA.test.com:666

    client 2 uses 192.16821

       goes to pool B and https://example.com redirects to hostnameB.test.com:666

    client 3 uses 192.16821

       goes to pool C and https://example.com redirects to hostnameC.test.com:666

     

    I want the node name to remain hidden from client, currently I see the server name in the response, I just want the client to only ever see https://example.com if possible

    Thanks again

    • Injeyan_Kostas's avatar
      Injeyan_Kostas
      Icon for Nacreous rankNacreous

      it's not clear to me yet

      Is the pool B server (for example) responds with a redirect or you mean that you need a host header rewrite because server listens only to hostnameB.test.com?

  • Thank you for the irule. I've only just been able to get back to this project due to other work. 

     

    when HTTP_REQUEST

    {

        STREAM::disable

        HTTP::header remove "Accept-Encoding" 

        set original_uri [HTTP::uri]

        HTTP::uri "/test${original_uri}"

    }

     

    Apologises for my understanding of this but my iRules knowledge is basic

     

    So when there is an HTTP request 

    set original_uri [HTTP::uri]   >>  set the original_uri means keep the original uri and store it as a variable? This would be  https://example.com

    HTTP::uri "/test${original_uri}" } >> this is the replacement, so the $ gets the variable and appends the test to it https://example.com/test

     

    Is it possible to make the following happen?

    https://example.com to https://different.name.com:666

     

    {

        STREAM::disable

        HTTP::header remove "Accept-Encoding" 

        set original_uri [HTTP::uri]

        HTTP::uri "https://different.name.com:666"

    }

     

    Within the response, do I just substitute in as follows

    when HTTP_RESPONSE

    {

    if {[string match "https://different.name.com:666*" $loc]}

    HTTP::header replace "Location" [string map {"https://different.name.com:666" "https://example.com"} $loc]

     

    Would the stream profile be like this?

     

      Thanks

    • Injeyan_Kostas's avatar
      Injeyan_Kostas
      Icon for Nacreous rankNacreous

      Hi sjy2025​ 

      So when there is an HTTP request 

      set original_uri [HTTP::uri]   >>  set the original_uri means keep the original uri and store it as a variable? This would be  https://example.com

      HTTP::uri "/test${original_uri}" } >> this is the replacement, so the $ gets the variable and appends the test to it https://example.com/test

      that's correct

       

      Is it possible to make the following happen?

      https://example.com to https://different.name.com:666

      in this case, different.name.com is the host not the uri 

      if understand correct you want client request https://example.com but you send to the server https://different.name.com:666

      Fisrtly you have to define the port in pool itslelf, so just create a pool with the ip of the server and port 666. Suppose you name the pool 666_pool

      then you can use below irule as an example

      when HTTP_REQUEST
      {
      	if { ([string tolower [HTTP::host]] equals "example.com") }
      	{
      		HTTP::header replace Host "different.name.com"
      		pool 666_pool
      	}
      }

       

  • Hi sjy2025​ 

    the best solution would be to config application use relative paths

    for now you can try this irule, you will need to apply stream profile also

    when CLIENT_ACCEPTED 
    {
        STREAM::disable
        if {[class match [IP::client_addr] equals clients1] } 
        {
            pool POOL_1
        } 
        elseif {[class match [IP::client_addr] equals clients2] } 
        {
            pool POOL_2
        } elseif {[class match [IP::client_addr] equals clients3] }
        {
            pool POOL_3
        } elseif {[class match [IP::client_addr] equals clients4] } 
        {
            pool POOL_4
        } elseif {[class match [IP::client_addr] equals clients5] } 
        {
            pool POOL_5
        }
        else {
            pool POOL_6
        }
    }
    
    
    when HTTP_REQUEST 
    {
        STREAM::disable
        HTTP::header remove "Accept-Encoding"
        set original_uri [HTTP::uri]
        HTTP::uri "/test${original_uri}"
    }
    
    when HTTP_RESPONSE 
    {
        STREAM::disable
        if {[HTTP::header exists "Location"]} 
        {
            set loc [HTTP::header "Location"]
            if {[string match "https://examples.com*" $loc]} 
            {
                HTTP::header replace "Location" [string map {"https://examples.com" "https://example.com"} $loc]
            }
        }
        if {[HTTP::header "Content-Type"] contains "text"} 
        {
            STREAM::expression {@https://examples.com@https://example.com@}
            STREAM::enable
        }
    }