F5 iRule – User Agent


when HTTP_REQUEST {
set HOST [string tolower [HTTP::host]]

if { ($HOST eq "domain.com" || $HOST eq "www.domain.com" ) and 
         ([HTTP::header User-Agent] contains "Chrome/33.0.175") and
            ([HTTP::header User-Agent] contains "(X11; Linux x86_64)") } {
                reject
               }
}

The above iRule will enable the F5 to send a TCP RST (reset) to the client for a specific domain and specific User-Agent information within the incoming HTTP Request.

F5 Monitor Status Code

F5 performs health-checks or monitors by utilizing the status code or the content in the response from the servers to mark the server as UP or DOWN.

It is relatively easy to mark the server UP/DOWN by using a single status code in the “Receive String” attribute within the Monitor:

200 OK

If you want to mark a server up for multiple status code like using 200-209, you can use this:

HTTP/1.[01] [2]0[0-9]

In order to mark the server UP for status code 200-209 or 401

HTTP/1.[01] ([2]0[0-9]|401)

F5_Monitor_Status

 

F5 iRule – URI Encoded


when HTTP_REQUEST {

if { [active_members [LB::server pool]] < 1 } {

set STATIC_SORRY [URI::decode "%3C!DOCTYPE%20HTML%3E%0A%3Chtml%3E%0A%3Chead%3E%0A%3Cmeta%20charset%3D%22UTF-8%22%3E%0A%3Ctitle%3EOops!%20We%27ll%20be%20back%20shortly%3C%2Ftitle%3E%0A%3Cstyle%3E%0A*%20%7B%09margin%3A0%3Bpadding%3A%200%3B%7D%0Ahtml%2C%20body%7B%20height%3A100%25%3B%20color%3A%20%23333%3B%20font-family%3AHelvetica%2C%20Arial%2C%20sans-serif%3B%7D%0A.wrapper%20%7Bmax-width%3A%20960px%3Bwidth%3A100%25%3Bmargin%3A0%20auto%3Bdisplay%3Atable%3Bheight%3A100%25%3B%7D%0A.container%20%7Bdisplay%3A%20table-cell%3Bvertical-align%3A%20middle%3Btext-align%3Acenter%3B%7D%0Ah1%20%7Bfont-size%3A60px%3B%7D%0Ah2%20%7Bfont-weight%3Anormal%3Bfont-size%3A60px%3Bmargin-bottom%3A0.36em%3B%7D%0Ap%20%7Bline-height%3A1.4em%3Bmargin%3A0%200%201.3em%200%3B%7D%0A%3C%2Fstyle%3E%0A%3C%2Fhead%3E%0A%3Cbody%3E%0A%3Cdiv%20class%3D%22wrapper%22%3E%0A%3Cdiv%20class%3D%22container%22%3E%0A%3Ch1%3EOops!%3C%2Fh1%3E%0A%3Ch2%3EWe%27ll%20be%20back%20shortly.%3C%2Fh2%3E%0A%3Cp%3ESorry%20for%20the%20inconvnience%2C%20please%20try%20again%20in%20a%20few%20minutes%3C%2Fp%3E%0A%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E"]

HTTP::respond 200 content $STATIC_SORRY
}
}

The above iRule can be utilized in order to serve a sorry page when all the pool members in the default pool (pool attached to the Virtual Server) are down.

The “STATIC_SORRY” is a variable that contains the URI Encoded version of the Sorry Page. Encoding the sorry page would help is the normal HTML page is not accepted by the F5. F5 can be finicky and not accept any HTML page that contains non-English characters. Hence, URI Encoded page can be of help in these cases.

This page can be utilized to encode the Sorry Page: URI Encoder/Decoder

F5 iRule – Serving Sorry Image

There are 3 steps to creating the F5 iRule to serve the image of a sorry page. Serving image of sorry page is utilized when you need to serve non-English characters which may not be compatible with F5. Of course, you can use it even for simple sorry pages with English characters.

There are 3 main steps:

  • Create a base64 image of the original Sorry Page Image
  • Create a datagroup that references the base64 Sorry Page Image
  • Create an iRule that reference the datagroup and attach this to the Virtual Server that requires the Sorry Page iRule.

 

Create a base64 image of the original Sorry Page Image:

  1. Copy the image (SORRY_IMAGE.png – this should be less than 46KB in size) onto F5’s directory /var/tmp/SORRY_IMAGE.png
  2. The following command should be copied to the F5 shell as a single line in order to create the base64 image
    echo \"SORRYIMAGE\" \:\= \"`base64 /var/tmp/SORRY_IMAGE.png | xargs echo | sed 's/ //g'`\"\, > /var/class/SORRY.class
  3. Check /var/class/SORRY.class
    [root@f5-ltm:Active] config # head /var/class/SORRY.class
    "SORRYIMAGE" := "iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAMAAACNZOU/A ...... <<remaining output omitted>>

 

Create a datagroup that references the base64 Sorry Page Image via GUI:

Name: SORRY_PAGE
Partition: Common
Type: (External File)
Path / Filename: /var/class/SORRY.class
File Contents: String
Key/Value Pair Separator: :=
Access Mode: Read/Write

 

Create an iRule:

when HTTP_REQUEST {
  if { ([active_members [LB::server pool]] < 1)} {
      HTTP::respond 503 content [b64decode [class element -value 0 SORRY_PAGE]]
      event disable
     }
}

iRule references the datagroup SORRYPAGE which references the file /var/class/SORRY.class and this contains the base64 image of the sorry page. SORRY_IMAGE.png which is utilized to create the base64 image should not be more than 46KB. This is a limitation on the F5 LTM.

 

iRule – Persistence Across HTTP Method


when HTTP_REQUEST {
set HOST [string tolower [HTTP::host]]

#Persistence is enabled for specific domain
if { $HOST equals "domain.com" }{

#Persistence for GET Request
if { [HTTP::method] equals "GET" } {
set SESSIONID [findstr [HTTP::uri] "uuid=" 5 20]
set KEY [crc32 $HOST$SESSIONID]

persist hash $KEY 30

#Collect Data upto Content Length or 1048
} elseif { (([HTTP::method] equals "POST") and ([HTTP::uri] contains "/ptm/tentxml.jsp")) } {

if {[HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048 } {
HTTP::collect $CONTENT
}
}
}

}

#Persist based on information available in the POST data
when HTTP_REQUEST_DATA {
if { (([HTTP::method] equals "POST") and ([HTTP::uri] contains "/ptm/tentxml.jsp")) } {

set HOST [string tolower [HTTP::host]]
set SESSIONID [findstr [HTTP::payload] "uuid=" 5 20]
set KEY [crc32 $HOST$SESSIONID]

persist hash $KEY 30
}
}

In the above iRule, persistence is maintained across HTTP GET & POST Methods. The Session id is present within:

URI of GET Request

POST Data

Persistence key is a combination of the domain & session id – set KEY [crc32 $HOST$SESSIONID]

In order to identify the Session id within the POST data, we will be collecting the data of size defined by the “Content-Length” header or default of 1048 bytes.

The iRULE would have to be utilized within an UIE persistence profile with “Match Across Services” for persistence across HTTP & HTTPS Virtual Servers.

 

F5 Cookie Persistence

This is  a good read on how to configure Cookie Persistence on F5 LTM: Cookie Persistence – F5

Cookie Insert:

When you create a normal cookie persistence profile using GUI:

lbal4(Active)(tmos.ltm)# list persistence cookie PROF-COOKIE-INSERT

ltm persistence cookie PROF-COOKIE-INSERT {
defaults-from cookie
}

When you update the existing session cookie via GUI:

ltm persistence cookie PROF-COOKIE-INSERT {
cookie-name BIGIP-F5
defaults-from cookie
expiration 10:0
method insert
timeout 180
}

In the above example, expiration timer is set to 10 minutes. The expiration timer will refresh as long as new requests are sent to the F5 within the 10 minute interval.

The “timeout 180” is just an addition that is seen but doesn’t hold any meaning for cookie persistence (Insert, Passive & Rewrite) as outlined in this document: SOL13069

Expiration:

Specifies the cookie expiration date in the format d:h:m:s, h:m:s, m:s or seconds. Hours 0-23, minutes 0-59, seconds 0-59. The
time period must be less than 24856 days. You can use “session-cookie” (0 seconds) to indicate that the cookie expires when the
browser closes.

F5 LTM Caching 10.x Code

An overview on F5 LTM Caching for 10.x code version

 

F5 LTM Cache

  • F5 LTM RAM Cache is used to cache HTTP objects in the F5’s RAM.
  • High Demand HTTP objects / Static Content can be stored on the F5 and served till the expiration period for the object.
  • RAM Cache feature in F5 is compliant with RFC 2616.

 

F5 LTM Cache Eligibility

  • Any response with the following status code 200, 203, 206, 300, 301 and 410 to a GET request, is cached by default.
  • Any non-GET method request to URI can be cached by adding the URI to the “Include List” or “Pin List” Difference between “Include List” and “Pin List”
  • Both Lists are used to store objects that are NOT cached by default.
  • “Pin List” will store the object, ignoring the expiry information provided by the header. The object will be cached even after the expiry timer – like a permanent cache. “Include List” will store the object, honoring the Cache headers.

Exclude List

  • Any object that can be stored by the F5 LTM cache, by default (GET Request + relevant status code) can be “excluded” from being cached by adding it to this list.

If you want to enable or disable caching, use an iRULE along with     HTTP Caching profile:


     
when HTTP_REQUEST {    

                  if { [HTTP::uri] starts_with "/static" } {          

                          CACHE::disable

        }

    }

With Caching enabled on the HTTP profile, the above iRULE will cache any valid object (defined by HTTP profile) but will NOT cache requests with “/static” uri, even if they satisfy the cache requirement.

 

NOTE

When you use “CACHE::enable” for any iRule, it will cache the relevant object even if the response headers prevent it from being cached. I would recommend using reverse logic – Use “Cache::disable” for any object that should NOT be cached and any object that should be cached will be taken care by the HTTP Caching profile based on the response headers.

Cache Control Headers

HTTP headers that are given importance by F5 LTM as per RFC:

  • Expires
  • Last-Modified
  • Cache-Control

Cache-Control

HTTP 1.1 introduced a new class of headers, Cache-Control response headers, to give Web publishers more control over their content, and to address the limitations of Expires.

Useful Cache-Control response headers include:

max-age=(seconds) — specifies the maximum amount of time that an representation will be considered fresh. Similar to Expires, this directive is relative to the time of the request, rather than absolute. Seconds is the number of seconds from the time of the request you wish the representation to be fresh for.

s-maxage=(seconds) — similar to max-age, except that it only applies to shared (e.g., proxy) caches.

public — allows caches (shared and private) to store the response.

private — allows caches that are specific to one user (e.g., in a browser) to store the response; shared caches (e.g., in a proxy) may not.

no-cache — forces caches to submit the request to the origin server for validation before releasing a cached copy, every time. This is useful to assure that authentication is respected (in combination with public), or to maintain rigid freshness, without sacrificing all of the benefits of caching.

no-store — instructs caches not to keep a copy of the representation under any conditions.

must-revalidate — tells caches that they must obey any freshness information you give them about a representation. HTTP allows caches to serve stale representations under special conditions; by specifying this header, you’re telling the cache that you want it to strictly follow your rules.

proxy-revalidate — similar to must-revalidate, except that it only applies to proxy caches.

For example:

Cache-Control: max-age=3600, must-revalidate

When both Cache-Control and Expires are present, Cache-Control takes precedence.

 This is a good document about Caching – Caching Tutorial

 

F5 RAM Cache Setting

In this case, default “HTTP” profile is used as a parent profile for the “PROF_HTTP_CACHE” custom profile.

F5-LTM-Cache-2

In this example, a snapshot of F5 Cache Settings is provided:

F5-LTM-Cache-1

RAM Cache: This needs to be Enabled for Caching to be implemented.

Maximum Cache Size: Total memory that is used for caching by this profile.

Maximum Entries: Number of HTTP objects that is cached.

Maximum Age: This is the duration till which the HTTP object is cached. Regardless of this value, RAM Cache will flush any object that is considered to be “expired” based on the Cache Control Headers.

Minimum Object Size: Any object that is equal to or greater than this value can be cached, if its headers allow it to be cached.

Maximum Object Size: Any object that is equal to or lesser than this value can be cached, if its headers allow it to be cached.

Ignore Headers: These are specific Headers that are provided by the Client-side, if Client requries “fresh/non-cached” objects. By default, it is set to ignore these Client Side headers (Ignore Headers : ALL). In effect, even if the Client requires a “non-cached” object, F5 can serve a cached object, provided such an object exists in its RAM Cache. If you want F5 to obey client-side cache-control headers, set “Ignore Headers : None”.

An Example

  • HTTP Server has to be configured to serve the HTTP objects with the correct Header for caching.

This link can be used to test out the “Cacheability” of a site:

http://redbot.org/

For any cache-able resource this MUST exist in order to honor caching:

  • “Expires” or “Cache-Control” header MUST exist
  • “Last-Modified” or “ETag” header MUST exist

In the above Header list, “ETag” is not considered by F5 LTM. So, for caching on F5:

  • “Expires” or “Cache-Control”
  • “Last-Modified”

Caching will occur even without the above headers but in that case, it is tough to control the expiry time and each proxy has its own algorithm to determine the cache-ability of an object and this is not optimum. While troubleshooting, make sure that the server’s response contains the headers specified.

If you use a specific HTTP link, the response provides information on the Header:

https://redbot.org/?uri=http%3A%2F%2Fwww.yahoo.com

You can move the cursor on the specific headers to get more information on the specific headers.

Cached Header Response

In this example, the “Date” and “Age” headers were inserted by the F5 to indicate the “Date” at which the F5 received the HTTP request and the “Age” of the cached object that was served. If Client should not see these values, then you can disable it under the “Insert Age Header” variable in the F5’s HTTP profile that is used for enabling caching.

$ curl -vk http://domain.com

> GET / HTTP/1.1

> User-Agent: curl/7.18.2 (x86_64-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g        zlib/1.2.3.3 libidn/1.10

> Host: blog.corporationwiki.com

> Accept: */*

< HTTP/1.1 200 OK

< Cache-Control: max-age=3600, must-revalidate

< Content-Type: text/html; charset=UTF-8

< Last-Modified: Thu, 12 Jan 2012 20:07:56 GMT

< Vary: Accept-Encoding, User-Agent< Server: Microsoft-IIS/7.5

< X-Powered-By: PHP/5.2.17

< WP-Super-Cache: Served supercache file from PHP

< X-Powered-By: ASP.NET< Accept-Ranges: bytes

< Connection: Keep-Alive

< Date: Thu, 12 Jan 2014 21:07:01 GMT <<<—–

< Age: 289 <<<< ——

< Content-Length: 37089

Vary Header

In the above example, Vary header has the following information:

Vary: Accept-Encoding, User-Agent

This tells the cache that the cached object is specific to the “User-Agent”. If you open up a browser (say, FF) and access the URL with HTTP objects that can be cached, it will be cached by the F5 LTM. If you access the same URL, from the same User-Agent (FF), you will be served with the cached HTTP objects.

On the other hand, if you open up a browser (say, FF) and access the URL with HTTP objects that can be cached, it will be cached by the F5 LTM. Now, if you access the same URL but from a different User-Agent (say, IE), the cached object will NOT be served. The cached objects are specific to FF (User-Agent) and there is no cache object for IE, for the specific URL requested and the request will be directed to the server. Basically, the content cached by F5 is unique to each User-Agent because of the Vary Header.

NOTE: F5 LTM honors only Accept-Encoding & User-Agent as Vary Header values. Any other value will be ignored.

This is a good blog to understand Vary Header and Caching

F5 iRule – JSession ID

The following is a simple iRule that provides persistence based on JSessionID that may be present in the incoming URI or within the Cookie:


when HTTP_REQUEST {

# Check if the JSESSIONID cookie is present
if { [HTTP::cookie "JSESSIONID"] ne "" }{
persist uie [HTTP::cookie "JSESSIONID"]

} else {

# Cookie wasn't set or didn't have a value, so check for the session ID in the URI
set JSESS [findstr [HTTP::uri] "JSESSIONID" 11 ";"]
if { $JSESS != "" } {
persist uie $JSESS
}
}
}

when HTTP_RESPONSE {

# Check if the JSESSIONID cookie is present in the response and has a non-null value
if { [string map {\" ""} [HTTP::cookie "JSESSIONID"]] ne "" }{
#log local0. "JSessionID in Response: [HTTP::cookie "JSESSIONID"]"
#log local0. "Set-Cookie: [HTTP::header values Set-Cookie]"

# Persist on the JSESSIONID cookie value for X seconds
persist add uie [HTTP::cookie "JSESSIONID"]
}
}

F5_JSessionID

The “string map” command is utilized in the HTTP_RESPONSE event as the value for the JSESSION ID may contain the quotes “” instead of just an empty string:

Timestamp: Rule jsessionid_persist_v2_rule : Set-Cookie: {JSESSIONID=""; Domain=host.domain.com; Expires=Thu, 01-Jan-1970 00} 00 {10 GMT; Path=/; Secure}

Name the above iRule and add it to the Universal Profile as shown here:

JSessionID_Profile

“Match Across Services” is enabled when you have two Virtual Servers for HTTP & HTTPS traffic and you require persistence across them – SOL5837

Please, note that for any persistence that involves the header of the incoming packet, we would have to terminate the SSL Certificate & Key on the F5 in order to enable it to read & manipulate the encrypted header.

Reference:

JSession ID

F5 CLI – TMSH & Bash

F5 has multiple command line access:

  • TMSH
  • Bash

From 11.x code version, F5 decided to focus future development only on tmsh. So, if you are trying to learn one of them, concentrate on tmsh. The cli is useful when we have to execute multiple commands within a short span of time like during a maintenance window.

Moving from TMSH to Bash:

root@lbal1(Active)(tmos)# run util bash

Moving from Bash to TMSH:

[root@lbal1:Active] log #tmsh

TMSH has the “(tmos)” in the prompt and the Bash CLI looks more like Linux prompt.

Reference:

K10272