Forum Discussion

uni's avatar
uni
Icon for Altostratus rankAltostratus
Apr 11, 2012

Persistent SNAT address based on session cookie

I have a client whose application is sensitive to the client IP address changing within a session. In their environment, there is some selective SNATting (internal clients are SNATted, public IPs are not).

 

 

Because the snatpool allows the client IP to change unpredictably, I have come up with the following rule. It tracks the value of a server-set session cookie, and saves the SNAT address used. When a client connects with the same cookie value, the SNAT address is retrieved and used.

 

 

Can someone give me some feedback about whether this is a feasible approach?

 

 

rule stg-test-rule {

 

when RULE_INIT {

 

set static::stgcookie "stgCOOKIE"

 

set static::stgtimeout 3600

 

}

 

 

when HTTP_RESPONSE {

 

if { [HTTP::cookie exists $static::stgcookie] } {

 

if { not ([serverside {IP::local_addr}] equals [clientside {IP::remote_addr}]) } {

 

table add [HTTP::cookie value $static::stgcookie] [serverside {IP::local_addr}] $static::stgtimeout

 

}

 

}

 

}

 

 

when HTTP_REQUEST {

 

if { [HTTP::cookie exists $static::stgcookie] } {

 

set snatip [table lookup [HTTP::cookie value $static::stgcookie]]

 

if { not ( $snatip equals "" ) } {

 

snat $snatip

 

}

 

}

 

}

 

}

 

 

  • Hi Stephen,

     

     

    You said that your Client's Application does not handle the Client IP Address changing within a session. Will the application work with an X-Forwarded-For Header? If so, then the application can pull the client IP Address out of the header and log/utilize it. You can see where to enable the X-Forwarded-For Header in this post.

     

     

    If not, then you could create a SNAT Pool with a single IP Address in it and SNAT to it (depending on the number of concurrent users your application will have you may need more than one. SNAT IP Addresses do have a connection limit per IP Address. See SOL7820: Overview of SNAT features for additional details). This would make your SNAT IP Address constant if nothing else (but you may need to engage a OneConnect Profile to keep the TCP Sessions load balanced properly).

     

     

    Hope this helps.
  • uni's avatar
    uni
    Icon for Altostratus rankAltostratus
    Thanks Michael. I don't know if the app respects the x-forwardrd-for header, and we don't have access to change it. We can't usea single address in the snatpool as the app does it's own source ip based load balancing.

     

     

    I wonder if instead you could comment on the feasibility of my proposed solution and rule? I know an alternative mythos is to insert my own cookie with the snat address, which has the advantage of avoiding using a table, and is independent of the app's cookie, but the principle idea is the same.
  • Hi Stephen,

     

     

    I think the iRule looks great for your scenario. Another option for ensuring the SNAT IP is consistent across connections is to use a hash. It's slightly more complicated but supports non-HTTP protocols and doesn't require storing any records on LTM or the client.

     

     

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

     

     

    Aaron
  • By the way, if you'd like to see SNAT pool member persistence supported in default config, you can open a case with F5 Support and reference BZ384117.

     

     

    Thanks, Aaron
  • uni's avatar
    uni
    Icon for Altostratus rankAltostratus
    Thanks Hoolio. I decided to go with the cookie insert method. It means my rule remains independent of the application and avoids the session table updates. It also remains independent of the actual snatpool, unlike the hashing method.

     

     

    This is the code I ended up with:

     

     

    rule snat-cookie-persistence-rule {
    timing on
       when RULE_INIT {
          set static::stgcookie "STGSESSION"
          set static::aes_key "AES 128 0123456789abcdef0123456789abcdef"
       }
       
       when HTTP_REQUEST {
          set cookieexists [HTTP::cookie exists $static::stgcookie] 
          if { $cookieexists } {
             if {not ([catch {AES::decrypt $static::aes_key [b64decode [HTTP::cookie value $static::stgcookie]]} snatip])}{
               log local0. "setting snat address $snatip"
                snat $snatip
             }
          }
       }
       
       when HTTP_RESPONSE {
          if { not $cookieexists } {
             if { [serverside {IP::local_addr}] ne [clientside {IP::remote_addr}] } {
               log local0. "inserting cookie value [serverside {IP::local_addr}]"
                HTTP::cookie insert name $static::stgcookie value [b64encode [AES::encrypt $static::aes_key [serverside {IP::local_addr}]]]
             }
          }
       }
    }
  • That makes sense. I also submitted an RFE for getting the SNAT pool members from an iRule:

     

     

    BZ381099 - ability to enumerate the list of members of a snatpool from an iRule

     

     

    If you want to see either this or default SNAT pool persistence you could open a case with F5 Support referencing these BZ IDs.

     

     

    Aaron