Forum Discussion

SalishSeaSecurity's avatar
Jan 12, 2010

Specifying node kills cookie persistence

I have a virtural server with a pool of four web servers. Load balancing is by least connection; oneconnect with a mask of 0.0.0.0 is enabled; persistence is by cookie insert (custom profile with non-standard cookie name). The web admins have asked for the ability to select a specific web server manually. There's plenty of code samples posted here to accomplish that, and below is what I put together (some lines of excised for readibility)

 

 

 

when HTTP_REQUEST {

 

if { [ findstr [string tolower [HTTP::query]] "node=" 5 2 ] != "" } {

 

log local0. "Found [ findstr [string tolower [HTTP::query]] "node=" 0 7 ]"

 

 

need to remove any stale cookies to prevent web server confusion

 

if {[HTTP::cookie exists "runId"]} {

 

HTTP::cookie remove "runId"

 

}

 

 

......

 

 

 

switch [ findstr [string tolower [HTTP::query]] "node=" 5 2] {

 

01 { set nodeTo 192.168.100.2 }

 

02 { set nodeTo 192.168.100.5 }

 

03 { set nodeTo 192.168.100.8 }

 

04 { set nodeTo 192.168.100.11 }

 

}

 

 

remove the ?query from the URI to prevent web server confusion

 

HTTP::path "/"

 

node $nodeTo 80

 

log local0. "Setting pool member node to $nodeTo"

 

 

}

 

}

 

 

 

Naturally I'm looking for a URL that looks like: http://www.mywebsite.com/?node=02

 

This works: the initial connection is sent to the appropriate server. But it also creates another problem: the cookie is not inserted into the initial HTTP response. If I comment out the line "node $nodeTo 80", then the cookie will be inserted into the initial HTTP response. So why would specifying the node impact the cookie insertion? Manually specifying persistence after the node selection ( persist cookie insert "mycookiename") has no effect.

 

 

Am I missing some other code or configuration?
  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    I think use of the node command prevents persistence from being used as there isn't a pool to associate the persistence record with. Can you try using the pool command instead of node:

     

     

    pool $my_pool member $nodeTo 80

     

     

    Aaron

     

     

  • I started with that. Then I remembered this doc:

     

    https://support.f5.com/kb/en-us/solutions/public/9000/800/sol9815.html

     

     

    The issue in the doc isn't entirely relevant, but I thought to be safe, I'd try 'node'. If I recall correctly, won't 'node' use the default pool for the virtual server? In any event, both 'node' and 'pool' create the desired the effect: the initial HTTP request is directed to the desired node. It's just that pesky cookie won't get set. For what it's worth, the cookie IS inserted on the second GET. But by then the connection is usually re-load balanced, and I wind up on the wrong server. At one time I did try a fallback persistence profile using source IP for a few seconds -- enough for the second GET to hit the same server and then receive the cookie. This, I think, is rather kludgy, and should be unnecessary.
  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    I don't think that using the node command would assume any pool info as the node command could be used to specify an arbitrary destination IP:port.

    I tried testing this on 9.4.8 and see what you're describing. When testing with a cookie insert persist profile, using the pool or node command stops LTM from sending a persistence cookie. I'm not sure why this is the case.

    Does anyone else have any ideas on this?

    Here is a sample iRule which I tried with/without manually calling 'persist cookie insert...' in HTTP_REQUEST and/or HTTP_RESPONSE.

     
     when HTTP_REQUEST { 
      
        if {[HTTP::uri] starts_with "/node1"}{ 
           log local0. "selecting pool test_www_http_pool member 10.41.0.22 80" 
           pool test_www_http_pool member 10.41.0.22 80 
           persist cookie insert persist_cookie 
        } 
     } 
     when LB_SELECTED { 
        log local0. "[IP::client_addr]:[TCP::client_port]: Pool info: [LB::server]" 
     } 
     when SERVER_CONNECTED { 
        log local0. "[IP::client_addr]:[TCP::client_port]: Server info: [IP::server_addr]:[TCP::server_port]" 
     } 
     when HTTP_RESPONSE { 
      
        foreach a_cookie [HTTP::cookie names] { 
           log local0. "$a_cookie: [HTTP::cookie value $a_cookie]" 
        } 
     } 
     

    pool member specified and no persist cookie set in the response

    Jan 12 18:40:15 : selecting pool test_www_http_pool member 10.41.0.21 80

    Jan 12 18:40:15 : 172.31.42.3:2296: Pool info: test_www_http_pool 10.41.0.21 80

    Jan 12 18:40:15 : 172.31.42.3:2296: Server info: 10.41.0.21:80

    pool member not specified and a persist cookie set in the response

    Jan 12 18:40:31 : 172.31.42.3:2297: Pool info: test_www_http_pool 10.41.0.21 80

    Jan 12 18:40:31 : 172.31.42.3:2297: Server info: 10.41.0.21:80

    Jan 12 18:40:31 : persist_cookie: 352332042.20480.0000

    Thanks,

    Aaron
  • Aaron,

     

     

    Thank you for doing that. I'm relieved to see the problem is reproducible. For the record I'm running 10.0.1.

     

     

    -Jason
  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    If you don't get a conclusive response here, you might try opening a support case.

     

     

    If all else fails, you could manually insert a persistence cookie in the response based on the manually selected server. The encoding algorithm (or the inverse, actually) is implemented in this Codeshare example

     

     

    http://devcentral.f5.com/Wiki/default.aspx/iRules/Persistence_Cookie_Logger.html

     

     

    And described in this AskF5 solution:

     

     

    SOL6917: Overview of BIG-IP persistence cookie encoding

     

    https://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html

     

     

    You'd probably want to do this once in RULE_INIT or per TCP connection for all the pool members to avoid performing the calculations on the response to every manually selected pool member request.

     

     

    Aaron
  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    Actually, I found an old post from UnRuley and Joe where they provided a sample iRule to encode an IP:port in the persistence cookie format:

     
      From: http://devcentral.f5.com/Default.aspx?tabid=53&forumid=5&tpage=1&view=topic&postid=9756 
      Generate a persistence cookie for a sample IP:port 
     when HTTP_RESPONSE { 
        set addr_port "10.10.10.10:80" 
        scan $addr_port "%u.%u.%u.%u:%u" a b c d e 
        set cookie "[format %u [expr ($d<<24)|($c<<16)|($b<<8)|$a]].[expr 256*$e].0000" 
        HTTP::cookie insert name "BIGipServerFP_pool" value $cookie path "/" 
     } 
     

    Aaron
  • Aaron,

     

     

    Thank you for the info. I knew about the cookie encoding doc, and figured as a last resort I would have to bake my own cookies (groan....couldn't resist).

     

     

    I do intend to open a support case.

     

     

    -- Jason
  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    Hi Jason,

     

     

    Did you end up getting any more info on this? If so, can you share? It looks like another poster is running into a similar issue:

     

     

    http://devcentral.f5.com/Default.aspx?tabid=53&forumid=5&tpage=1&view=topic&postid=11678261167826

     

     

    Thanks,

     

    Aaron