Skip to content
Constantine edited this page Sep 10, 2024 · 35 revisions

HTTP Session management

HTTP cookies are used as HTTP session identifiers. Tempesta FW reads the Cookie: request header and matches the cookies to list of known sessions to provide load balancing stickiness, i.e. all requests from the same HTTP agent are forwarded to the same backend server.

There is two ways to set the session cookie ether learn the cookies advertised by backend servers or create them on Tempesta FW's side. While learned cookies are good for load balancing stickiness, the own Tempesta FW's cookies can provide extra DDoS mitigation possibilities.

Session management options are configured per-vhost inside sticky block.

sticky {
    cookie [name=<COOKIE_NAME>] [enforce] [max_misses=<LIMIT>];
    cookie_options [OPTIONS];
    learn name=<NAME>;
    secret <SECRET_PHRASE>;
    js_challenge delay_min=<TIME> delay_range=<TIME> [resp_code=<CODE>]
                 [SCRIPT_TEMPLATE];
    sticky_sessions [allow_failover];

}

The sections at the below describe each directive in details.

A sticky block outside of any vhost modifies session management defaults for vhosts below and doesn't affect configuration of previously defined vhosts. Default values can be modified multiple times, but every time a new sticky section is added, results of the previous one are negated. Example:

# Vhost description has no 'sticky' section, thus session management is disabled.
vhost static_content.com {
}

# Vhost description has 'sticky' section, session management is configured as
# defined there.
vhost example.com {
    sticky {
        cookie name=__tfw;
    }
}

# Modify session management defaults for all vhosts below. Directives not listed
# in this section will follow default values from the documentation.
sticky {
    cookie name=__tfw;
}

# Vhost description without 'sticky' section follows modified defaults:
# directive 'cookie name=__tfw' is inherited here.
vhost inherit.com {
}

# Vhost description with explicit 'sticky' directive will use the modified
# defaults unless other values are described in 'sticky' section: 'cookie'
# directive will be inherited, while 'sticky_sessions' is redefined here.
vhost inherit-n-update.com {
    sticky {
        sticky_sessions;
    }
}

# Modify session management defaults once again. All the previous
# modifications are removed.
sticky {
    ...
}

The only exception is the cookie, js_challenge and cookie_options directives, which can be inherited together only. Since js_challenge and cookie_options are highly depended on cookie configuration, they can be inherited only with cookie directive. If the cookie directive is explicitly set in a vhost, then the js_challenge and cookie_options directives are not derived.

Detailed description of every directive is at the below, but there is also an article on migration from NGINX in our wiki.

Sticky cookie

Sticky cookie is a special HTTP cookie that is generated by Tempesta FW and advertised to client. It allows for unique identification of each client and can be used as challenge cookie for simple L7 DDoS mitigation when bots are unable to process cookies. It can be even more fortified with the JavaScript challenge discussed below.

When used, Tempesta FW sticky cookie is expected in incoming HTTP requests. Otherwise, Tempesta FW will respond with redirect and the Set-Cookie: header. By default the sticky cookie is disabled.

Normal browsers supports cookies and follows redirects:

Bots not supporting cookies will be filtered:

Configure Sticky Cookie

The use and behaviour of Tempesta FW sticky cookies is controlled by a single configuration directive that can have several parameters. The full form of the directive and parameters is as follows:

sticky {
    cookie [name=<COOKIE_NAME>] [enforce] [max_misses=<LIMIT>];
}

name parameter specifies a custom Tempesta FW sticky cookie name COOKIE_NAME for use in HTTP requests. It is expected that it is a single word without whitespaces. If not specified explicitly, a default name __tfw is used.

enforce parameter demands that Tempesta FW sticky cookie is present in each HTTP request. If it is not present in a request, a client receives HTTP 302 response from Tempesta FW that redirects the client to the same URI, and prompts that Tempesta FW sticky cookie is set in requests from the client.

max_misses parameter sets the maximum count of redirected requests (with no cookie or with incorrect cookie value). This option is applicable only if 'enforce' mode is enabled. If configured limit is exhausted, the corresponding client will be blocked. LIMIT is non-negative integer (default value is 1).

cookie_options parameter sets additional cookie options. If it is not explicitly set Path='/' and Max-Age='session lifetime' are added to cookie options. If Expires is set in cookie options and Max-Age is not Tempesta FW doesn't add Max-Age to cookie options. Exact meanings of the options can be found in MDN

Below are examples of Tempesta FW sticky cookie directive.


sticky {
    cookie;
}

Enable Tempesta FW sticky cookie. Default cookie name is used. Tempesta FW expects that Tempesta FW sticky cookie is present in each HTTP request. If it is not present, then Tempesta FW includes Set-Cookie header field in an HTTP response, which prompts that Tempesta FW sticky cookie with default name is set in requests from the client.


sticky {
    cookie enforce;
}

Enable Tempesta FW sticky cookie. Default cookie name is used. Tempesta FW expects that Tempesta FW sticky cookie is present in each HTTP request. If it is not present, Tempesta FW sends HTTP 302 response that redirects the client to the same URI and includes Set-Cookie header field, which prompts that Tempesta FW sticky cookie with default name is set in requests from the client.


sticky {
    cookie name="__cookie__";
}

Enable Tempesta FW sticky cookie. The name of the cookie is __cookie__. Tempesta FW expects that Tempesta FW sticky cookie is present in each HTTP request. If it is not present, then Tempesta FW includes Set-Cookie header field in an HTTP response, which prompts that Tempesta FW sticky cookie with the name __cookie__ is set in requests from the client.


sticky {
    cookie name="__cookie__" enforce max_misses=10;
}

Enable Tempesta FW sticky cookie. The name of the cookie is __cookie__. Tempesta FW expects that Tempesta FW sticky cookie is present in each HTTP request. If it is not present, Tempesta FW sends HTTP 302 response that redirects the client to the same URI and includes Set-Cookie header field, which prompts that Tempesta FW sticky cookie with the name __cookie__ is set in requests from the client. If the number of HTTP requests without sticky cookie will exceed 10 such client will be blocked.


sticky {
    cookie;
    cookie_options Domain=example.com Secure HttpOnly
}

The Set-Cookie: header will be formatted with extra values and will look like Set-Cookie: __tfw=<HMAC_VALUE> Domain=example.com ; Secure ; HttpOnly; Path=/; Max-Age=4294967295;. It is important to note that Tempesta FW by default adds Path and Max-Age cookie options if they are not explicitly set. Exact meanings of the options can be found in MDN.


sticky {
    cookie;
    cookie_options Max-Age=3600;
}

The expire time in seconds is defined for the cookie in seconds, transforming the cookie form the session to permanent for HTTP agent. Client will continue to use the cookie until it's expired.

Cookie generation

Sticky cookie value is calculated on top of client IP, User-Agent, session timestamp and the secret used as a key for HMAC.

secret <SECRET_PHRASE>; The option sets the secret string used for HMAC calculation. It's desirable to keep this value in secret to prevent automatic cookies generation on attacker side. Defaults: a new random value on every start. This means that all user HTTP sessions are invalidated on Tempesta FW restart. <SECRET_PHRASE>: quoted secret string, maximum length is 20 bytes. Example:

secret "f00)9eR59*_/22";

Sticky Cookie rate limits

Client too often trying to reach resource with incorrect Sticky Cookie value will be blocked. Client too often trying to request a new sticky cookie will be blocked. Client trying to acquire too lot of simultaneous sessions will be blocked.

The feature is still under development and is planned for upcoming 0.7 release.

JavaScript Challenge

While the Sticky Cookie option is a good solution to protect backend servers from the simplest bots, JavaScript Challenge options enhances the protection to filter bots which are capable to follow redirects and support cookies.

The challenge mitigates application-level (L7) DDoS attacks by

  • allowing only JavaScript-enabled clients (browsers) to access the protected website
  • and requiring all clients to slow down their request rate.

This challenge isn't assumed to prevent a targeted attack, which was developed precisely against a particular website. Also it isn't assumed to prevent low-rate bot attacks like scrappers, carders and so on. Please check Tempesta FW page or contact us for Tempesta FW enterprise version providing advanced bots protection.

When a new client tries to access the protected resource, Tempesta FW responds with 503 status code, Set-Cookie: header and JavaScript code in message body. JavaScript code will force client to reload resource at allowed time frame. After the client reloads resource at allowed time frame it will be granted to have access to the protected resource. JavaScript challenge won't be sent to the client once again in this session. After the sticky cookie assigned to the client will expire, a new JS challenge will be performed.

If client tries to reach resource outside of allowed time frame, it's requests will be treated as Sticky Cookie violation and new sticky cookie will be sent to such client. If client violates cookies too often (exceeded max-misses limit) such client will be blocked.

If bot is a full web stack client (full featured web browser), it can process JavaScript challenge and proper configuration of Sticky Cookie rate limits is required.

Not all requests can be challenged with JavaScript. Browsers don't run JavaScript for responses to images, multimedia and non-GET requests. But when a user loads page in his browser (following a link, opening bookmark or manually inputting address in the address bar), the very first request is used to get and build DOM. Such request always contain text/html token in Accept header and browser evaluates JS code even on direct links to images. Thus only the very first request is subject for JS challenge. But if HTTP client sends non-challengable request until the session is confirmed, it receives response from cache if it is present or 503 response with Retry-After: a hint.

Configuration

The JavaScript challenge requires explicit sticky cookie configuration, so cookie option with at least enforce attribute is required for the JS challenge sticky configuration.

sticky {
    cookie enforce;
    js_challenge delay_min=<TIME> delay_range=<TIME> [resp_code=<CODE>]
                 [SCRIPT_TEMPLATE];
}

delay_min - milliseconds, the minimum delay when a client is allowed to repeat its request with Sticky Cookie set. Value is used in JS challenge code and to be sent to client.

delay_range - milliseconds, the period of time during which a client is allowed to send request with Sticky Cookie set. Value is used in JS challenge code and to be sent to client.

When a client receives the JS challenge it must wait (delay) for delay_min + <random> milliseconds (<random> is between 0 and delay_range) and send the redirected request. When Tempesta FW receives the redirected request it ensures that it is received not earlier than delay_min (to mitigate a DDoS attack by slowing down the request rate).

resp_code - status code for response with JS challenge. Optional parameter, default is 503.

<SCRIPT_TEMPLATE> - path to response template with JS challenge script. Optional parameter, default is /etc/tempesta/js_challenge.html, see Custom JS challenge template chapter.

NOTE: During start process tempesta.sh uses grep to update <SCRIPT_TEMPLATE> file with values described in js_challenge directive, so line breaks are not allowed inside the js_challenge and sticky directives.

Examples:

sticky {
    cookie name=my_js_cookie enforce max_misses=3;
    js_challenge resp_code=503 delay_min=1000 delay_range=1000 /etc/ddos_redirect.html;
}
sticky {
    cookie enforce;
    js_challenge delay_min=1000 delay_range=1000;
}

Note that browsers automatically retry error responses such as 3xx or 5xx. It's recommended to use resp_code=503 with the JavaScript challenge since a browser should display a 5xx response body by RFC 7231 6.6 (i.e. don't ignore the response body). The retry timeout is crucial for the JavaScript challenge, so any earlier retries from a browser side may lead to full client blocking. At the moment we do no send Retry-After due to vague state of all the browser implementations. Also see the Mozilla bug report. Please file us a bug report if you face any issues with the redirection timeouts.

Learn how to configure JavaScrip challenge when migrating from NGINX and testcookie-nginx-module.

Custom JS challenge template

It's recommended to modify default JS challenge response template file js_challenge.tpl located in configuration files directory (/etc/tempesta/ by default) rather than provide a brand new template file since JS challenge code must be synchronised with TempestaFW configuration file. Attribute name of sticky directive, attributes delay_min and delay_range of js_challenge directive must be passed to JS challenge code template.

Session Stickiness

By default Sticky cookie only provide challenge to mitigate DDoS attacks and doesn't imply session persistence. To pin the session to the backend server additional directive sticky_sessions should be used.

When client passes sticky cookie challenge, his session is confirmed but not assigned to any backend server. First request in the session is scheduled to server groups assigned to the vhost as usual. All the following requests will be forwarded to that server.

The situation, how a request is handled if the pinned server goes down depends in allow_failover option. If the option is set, the session is re-pinned to other server and stays there even when the original server goes back online. Otherwise client will receive 502 response code until backend comes back online, no re-pinning will happen.

sticky_sessions [allow_failover]; If the directive is set, the session is pinned to backend server. allow_failover option allows to re-pin session to other server, if original goes down.

Learn how to configure session persistence when migrating from NGINX.

Learned sessions

Learned sessions can't be used to mitigate DDoS attacks. It doesn't involve any challenges to clients before accessing backends and requests are forwarded to backends immediately.

TempestaFW reads Set-Cookie: header from backend server response and creates a new session entry pinned to that backend server. All the requests with the same cookie will be forwarded to that server.

Unlike native TempestaFW sticky cookie the learned cookie doesn't cryptographically confirm the exact cookie owner - client with it's own unique IP-address and User-Agent, so malicious clients may try to steal the session and try to bypass the load balancer and exhaust singe backend server. Thus the option should be used with caution.

If clients doesn't set the cookie advertised by backend, no pinning is performed and all the requests are forwarded to backend servers with load balancing rules.

Configuration

To configure the learned session directive learn should be used.

sticky {
    learn name=<NAME>;
}

Where NAME - name of the cookie advertised by backend server and used by clients.

Directives cookie, js_challenge conflicts with the learn directives. Directive secret has no affect on learn directive.

Session Lifetime

Session lifetime is different from cookie lifetime. Cookie lifetime is configured in Set-Cookie: response header and controls how long clients will keep the cookie and try to use it. Session lifetime controls how long the session entry is stored in TempestaFW database.

If the sticky cookie session is expired then client is forced to restart the cookie challenge. If the learned session is expired, the request will be forwarded to any configured backed server and a new session will be learned from its response.

sess_lifetime <SECONDS>: HTTP session lifetime in seconds. For sticky cookie sessions - total session lifetime, for learned - lifetime from last request using the session. Defaults: 0, i.e. unlimited life time. Example:

sess_lifetime 900;

Session lifetime is used to set Max-Age cookie option if it is not explicitly set by administrator.

Sessions database

Sessions are stored in TempestaDB. The following configuration options controls the location and size of database.

sessions_db <PATH_TO_DB>: Path to a sessions database files. Defaults: /opt/tempesta/db/sessions.tdb The PATH must be absolute and the directory must exist. The database file must end with .tdb.

sessions_tbl_size <SIZE>: Size of sessions database file in bytes. Defaults: 16M. Can use suffixes K, M, G. The size must be multiple of 2MB (Tempesta DB extent size).

Clone this wiki locally