Monday, 12 August 2019

Cookie Persistence

The BIG-IP system can make use of cookies to ensure the client is reconnected or persisted to the same pool member that they were originally connected to.

There are four different flavours of cookie persistence options, all available under the Local Traffic  ››  Profiles : Persistence menu:

  • HTTP Cookie Insert
  • HTTP Cookie Rewrite
  • HTTP Cookie Passive
  • Cookie Hash

HTTP Cookie Insert

Probably the more frequently used (in my experience) of the options above as it requires the least amount of effort to setup. The BIG-IP system inserts a cookie into the client response using the following format:

BIGipServer<pool_name>=<pool_member_address>.<port>.<0000>; expires=<date, time>; <path>

Where:

  • BIGipServer = the name of the cookie created by the BIG-IP system.
  • pool_name = the literal name of the pool that serviced  the request.
  • pool_member_address = the encoded value (in decimal format) of the pool member that serviced the request.
  • port = the encoded port number (in decimal format) of the pool member.
  • 0000 = a special reserved field for future use.
  • expires = the date and time this cookie expires. This is either session based or on a configurable setting.
  • path = the specific URL the cookies applies to.
Note that you also have the option to set your own cookie name.

HTTP Cookie Rewrite

This method requires a bit more elbow grease to setup as you will need to liaise with the team that either manages the web servers or the application code itself. This method requires a blank cookie in a very specific format to be created on the web server and sent in the response. The BIG-IP system then intercepts this cookie and rewrites it to include the value just like the insert method above.

I use an Apache web server and to set this up I had to do the following:

1. Enable mod_headers:

sudo a2enmod headers

2. Modify the virtual hosts file to include the Set-Cookie attribute:

<VirtualHost X.X.X.X:80>

...
Header set Set-Cookie BIGipCookie=0000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000

In case you're wondering those are 120 zeroes. This is required.

3. Restart the Apache2 service:

sudo service apache2 restart

HTTP Cookie Passive

This method requires even more greasing of those elbows. In this method the BIG-IP system does not create, modify or intercept the cookie sent from the web server. Rather it passively passes it through to the client. What needs to happen is each back-end server must be configured to set the cookie itself but formatted in a very specific way to include the all the name/values described in the cookie insert method above. Thus, when the client returns traffic to the LTM it can read the cookie and determine which pool member the client must be sent back to.
In my lab environment I have three back-end web servers using the following IP addresses:
  • Web server 1 = 10.128.2.150
  • Web server 2 = 10.128.2.160
  • Web server 3 = 10.128.3.170
These are part of a pool named "HTTP_Web_Server_Pool_1".

We first need to encode the IP addresses by representing the IP address of the pool members as a.b.c.d then using the following formula to derive the decimal format:

a + b*256 + c*(256^2) + d*(256^3)

10 + 128*(256) + 2*(256^2) + 150(256^3)

= 2516746250

To work out the encoded port we can use the following set of instructions:

  1. Convert the decimal port value to the equivalent 2-byte hexadecimal value.
  2. Reverse the order of the 2 hexadecimal bytes.
  3. Convert the resulting 2-byte hexadecimal value to its decimal equivalent.
As I'm using port 80 I need to be aware that anything less than 256 must be padded with 0x00 at the beginning, so for port 80 this would be:
  1. Decimal to Hex = 80 = 0x0050
  2. Reverse order = 0x5000
  3. Hex to decimal = 20480
If I repeat for the other two servers I can go ahead and create a cookie for each server. Note that there are other probably better ways to set cookies on web servers. This example is provided just to show the basic concepts:

Web server 1 cookie:

Header set Set-Cookie "BIGipServerHTTP_Web_Server_Pool_1=2516746250.20480.0000;path=/;Expires=Sun, Apr 30 2017 19:00:00 GMT"

Web server 2 cookie:

Header set Set-Cookie "BIGipServerHTTP_Web_Server_Pool_1=2684518410.20480.0000;path=/;Expires=Sun, Apr 30 2017 19:00:00 GMT"

Web server 3 cookie:

Header set Set-Cookie "BIGipServerHTTP_Web_Server_Pool_1=2852290570.20480.0000;path=/;Expires=Sun, Apr 30 2017 19:00:00 GMT"

Now each of these cookies must be set on the back-end server just as we did with the cookie rewrite method.

Cookie Hash

The final method available is interesting because it is the only method which actually stores a persistence record in memory. With this method the cookie must also be set on the web server, however, you are not restricted to the format as per the other methods described above. What you do need to ensure is that whatever you have named the cookie on the web server you must also enter into the cookie hash profile, they must match. The BIG-IP system will then:
  1. Parse the response from the web server for the Set-Cookie header
  2. Uses the Hash Offset & Hash Length settings defined in the profile to compose a value. This value is stored in the persistence table, but not in a hashed form.
  3. Any subsequent request from the client will force the BIG-IP system to parse the header for a cookie and then uses the same offset/length values to determine if this matches one stored in the persistence table. If there is a match it sends the request to the pool member.

Cookie Security

As with anything, before implementing cookie persistence on the BIG-IP system you need to understand and take into account security as it applies to cookies.

Encrypting the cookies would be a good start. The BIG-IP encrypts using cookies using a 192-bit AES cipher.

If encryption cannot be used you need to take into account the fact that you can quite easily enumerate the back-end pool member IP just by looking at the returned cookie. This can help an attacker perform reconnaissance on your network. 

By default the cookie name itself quite clearly spells out BIGIP and will make it very clear that you are using a BIG-IP system, which further helps them understand the topology of your network. Changing the cookie name would therefore be the preferable mitigation to this.

    Configuration

    HTTP Cookie Insert

    We are presented with a few options when configuring this method:
    • Cookie Method: fairy self-explanatory this one, choose which cookie method you want.
    • Cookie Name: a user-defined name for the cookie to override the default.
    • Always Send Cookie: this setting specifies whether the system sends a cookie with every response or just the first. This setting only applies if the expiration is set to 'Session Cookie'.
    • Expiration: allows a user-defined expiration of the cookie. If the 'Session Cookie' setting is enabled you cannot configure an expiration.
    • Cookie Encryption Use Policy: introduced in v11.5, this setting effectively turns on or off encryption. The three settings offered are:
      • Preferred: the system generates an encrypted cookie but will accept from clients, an unencrypted cookie.
      • Required: the system generates an encrypted cookie and will only accept an encrypted cookie.
      • Disabled: turns of cookie encryption altogether. 
    • Override Connection Limit: used to override any connection limits set at the pool member level for clients that are eligible to be persisted.
    Here I have the config. In this example I have not used the Session Cookie option or enabled encryption:
    The resulting cookie provided by the BIG-IP system:

    HTTP Cookie Rewrite

    The settings for this method are the same as the insert method. However, we will go ahead and create the blank cookie on the back-end web server.
    We can see the web server return the blank cookie to the BIG-IP system:
    And then the BIG-IP rewriting the blank cookie to the appropriate format as it sends it to the client:

    HTTP Cookie Passive

    The settings for this method are further stripped down. A key setting to note here is the Cookie Name field. This must be set and must match the cookie name configured on the web server. You can also encrypt this cookie as per the other methods.
    We can see now the back-end web server return its configured cookie to the BIG-IP, which then passes it onto the client:
    Any subsequent request from the client within the timeout period carries the cookie presented to the BIG-IP:
    The BIG-IP system then sends the traffic to the same pool member thus achieving our persistence goal.

    Cookie Hash

    The final method introduces some further configuration options:
    • Hash Offset: specifies at which point in the cookie value the system begins the hash. For example, if the cookie value is 'cookie' and the offset is 1, then the BIG-IP system will hash the value 'ookie'.
    • Hash Length:  specifies the number of characters in the cookie value to use when calculating the hash value. For example using the above cookie value if we set the length to 3 this will hash the value 'ook'.
    • Timeout: specifies the duration of the hash persistence entry in the persistence table.
    • Mirror Persistence: specifies whether to mirror the persistence record to the standby peer.
    • Match Across Services: attempts to send all persistent connection requests received from the same client, within the persistence time limit, to the same node only when the virtual server hosting the second connection has the same virtual address as the virtual server hosting the initial persistent connection. Both VSs must use the same IP address and backend pool members. The service port can be different on each VS.
    • Match Across Virtual Servers: similar to Match Across Services, but it does not require the virtual servers to share the same IP address. This configuration allows clients to access different virtual servers, regardless of their IP address, and still access the same pool member. The VSs must still use the same backend pool members.
    • Match Across Pools: allows the BIG-IP system to use a pool that contains a persistence record for a specific client. If you have an iRule that determines which pool member to send the request to and the client uses a different URI in their request, using this option may bypass the iRule logic and potentially break the application.
    Here we set the value of the cookie and we'll use the hash values as per the description above and shorten the timeout to 30 seconds:
    I have also configured a very basic cookie on the web servers:

    Header set Set-Cookie Test_Cookie=Cookie

    Now when I make a connection the BIG-IP system will perform a hash as per the configuration on the cookie value "Cookie". We can verify a persistence record has been created with the following command which also helps us to verify the Hash Offset and Length working:

    #show ltm persistence persist-records all-properties
    Sys::Persistent Connections
    cookie - 10.128.3.120:80 - 10.128.2.160:80
    ----------------------------------------------
      TMM           1
      Mode          cookie
      Value         ook
      Age (sec.)    11
      Virtual Name  /Common/HTTP_Web_Server_vs_1
      Virtual Addr  10.128.3.120:80
      Node Addr     10.128.2.160:80
      Pool Name     /Common/HTTP_Web_Server_Pool_1
      Client Addr   10.128.3.1
      Owner entry

    Summary

    Cookie persistence does require some careful thought practically speaking and from a security perspective but works well and can help with your persistence requirements. All the above can of course be achieved using an iRule which can provide more flexibility.











    No comments:

    Post a Comment

    iRule

      iRule: -- o iRule is a powerful and flexible feature within the BIG-IP local traffic management (LTM). o IRule is a powerful & flexibl...