Skip to content
Anton Matvienko edited this page Jul 7, 2023 · 61 revisions

Frang security limits enforcing module

Frang is an internal Tempesta module for enforcing security limits to prevent Web attacks and mitigate HTTP(S) (D)DoS attacks. It uses static limiting and checking of ingress HTTP requests.

Learn how to configure limits when migrating from NGINX.

Frang limits are split into two parts: connection level limits and message level limits. The rules how the limits can be defined and the ways how malicious traffic can be blocked differ for both of the types.

The main portion of it's logic is on the HTTP layer, so it's recommended that ip_block option (enabled by default) is used to block malicious users at IP layer.

The limits are described inside section frang_limits. The section may appear:

  • at top level - to configure connection level limits and to override default values for message level limits for vhosts below this directive section. May appear multiple times. Implicit default vhost will use the last effective set of the message level limits.

  • inside vhost section - to override defaults for locations of the current vhost below this directive section. May appear multiple times. Implicit default location of the vhost will use the last effective set of the limits.

  • inside location section - to configure message level limits for the current location.

Even if section frang_limits is not stated in the configuration file, the whole Frang subsystem is enabled with defaults.

The global Frang limits must be specified before all vhosts, which must inherit the global limits.

See examples below how the limits can be configured and overridden.

The Frang limits are calculated inside sliding windows, mostly window size equals to 1 second, but some limits have configurable window size.

Accounting history

Some of the limits (all *_burst limits and request_rate) work using a history ring buffer of 8 slots each of 125ms, so the tracking history is 1 second. This implies the property of amnesia: if a client has issued say 1000 requests immediately with only 10 requests per second in a rate limit, then after 1 second it still can be normally serviced unless ip_block on was specified.

Minor bursts also can actually exceed the specified limit, but not more than 2 times. This may happen if the actual burst is tracked in 2 consequent slots in the history buffer instead of one. Keep this in mind during configuration and troubleshooting.

Connection level limits

Connection level limits are applied at very early processing stages, when there is not enough data to determine target vhost. E.g. when a client establishes connections or until full set of request headers is received. Thus connection level limits are configured only globally.

Limits of this type can not work with block_action, which sends HTTP error messages, since a connection under inspection of a limit from this section is either not yet established or is going to be terminated, i.e. there is nowhere to send an error message.

If the limit is reached, then Tempesta FW resets a connection (sends TCP RST segment). A client can try to reestablish a connection and it can be serviced if it satisfies all the limits this time and unless the ip_block on is specified.

I.e. with wrong configuration:

frang_limits {
    ip_block off;
    connection_burst 2;
}
block_action attack reply;

all the rate limited connections are just reset with TCP RST and the block_action statement is just ignored.


ip_block [off|on] If disabled client connection(-s) are closed of security events. If enabled client connections are closed and client will be blocked by it's IP address. Defaults: off - disabled.


request_rate [NUM] Maximum number of requests per second from a single client across all it's connections. 0 to disable the limit. Defaults: 0 (disabled).


request_burst [NUM] Maximum burst of request rate on short periods of time (125 ms). The option can be used independently from request_rate limit. 0 to disable the limit. Defaults: 0 (disabled).


connection_rate [NUM] Maximum number of new connection openings per second from a single client. 0 to disable the limit. Defaults: 0 (disabled).


connection_burst [NUM] Maximum burst of new connection openings rate on short periods of time (125 ms) from a single client. Having that browsers may issue about 6 connections simultaneously to a host, a reasonable value for the limit is 10-20. The option can be used independently from connection_rate limit. 0 to disable the limit. Defaults: 0 (disabled).


concurrent_connections [NUM] Maximum number of new concurrent connections from a single client. 0 to disable the limit. Defaults: 0 (disabled).


client_header_timeout [TIME] Maximum time in seconds to receive the whole HTTP headers set of incoming request.0 to disable the limit. Defaults: 0 (disabled).


client_body_timeout [TIME] Maximum time in seconds to receive the whole HTTP body of incoming request.0 to disable the limit. Defaults: 0 (disabled).


http_header_chunk_cnt [NUM] HTTP messages can be sent by parts. The option controls maximum number of parts (chunks) in the request header part.0 to disable the limit. Defaults: 0 (disabled).


http_body_chunk_cnt [NUM] HTTP messages can be sent by parts. The option controls maximum number of parts (chunks) in the request body part.0 to disable the limit. Defaults: 0 (disabled).


tls_connection_rate [NUM] Maximum number of new TLS sessions established with a single client per second. Establishing a new TLS connection (handshake processing) requires much more computations on server side than on resuming a previous one. Malicious clients can DDoS server constantly establishing thousands new TLS connection. With the option Tempesta blocks new connections with a client, if it establishes new TLS connections too often. Defaults: 0 (disabled).


tls_connection_burst [NUM] Maximum burst of new TLS sessions rate on short periods of time (125 ms) from a single client. The option can be used independently from tls_connection_rate limit. 0 to disable the limit. Defaults: 0 (disabled).


tls_incomplete_connection_rate [NUM] Block client if it fails more than NUM TLS handshakes per second. Defaults: 0 (disabled).

Message level limits

Message level limits are accounted on later processing stages when target vhost and location are already known. Such request can be configured globally as defaults, per vhost and per location.

These limits can trigger both the blocking on the IP layer (with ip_block option) or sending an HTTP error message (with block_action). However, bear in mind how both of the options interact with each other, i.e. if both of them are enabled like in the wrong configuration:

frang_limits {
    ip_block on;
    http_ct_required true;
}
block_action attack reply;

If the http_ct_required rule is triggered, then Frang immediately calls the IP filter, which

  1. resets the client TCP connection with RST segment and
  2. blocks the client IP on the Netfilter layer. This means that we can not send any data, including an error HTTP response, to the client. In other words, in this configuration block_action statement for triggered http_ct_required rule is just ignored.

http_uri_len [NUM] Maximum length of URI part in a request.0 to disable the limit. Defaults: 0 (disabled).


http_field_len [NUM] Maximum length of a single HTTP header field in an incoming request. This limit is helpful to prevent HTTP Response Splitting and other attacks using arbitrary injections in HTTP headers. 0 to disable the limit. Defaults: 0 (disabled).


http_body_len [NUM] Maximum length of HTTP message body of incoming request. 0 to disable the limit. Defaults: 1073741824 (1 Gb).


http_header_cnt [NUM] Maximum number of HTTP headers in a HTTP message. 0 to disable the limit. Pseudo header from HTTP2 are ignored. Defaults: 0 (disabled).


http_strict_host_checking [true|false] Apply additional "SHOULD" conditions to authority information from RFC 9112/9113:

  • Enforce Host and :authority equality (HTTP/2)
  • Enforce equality host=... part of Forwarded header and authority information (HTTP/1.1, HTTP/2)
  • Enforce equality of absoluteURI host part and Host header (HTTP/1.1)

Defaults: false.

Notice While mismatching of SNI and virtual host names is the common source of vhost confusion Tempesta FW does not validate SNI against virtual host name at run time. Instead, it validates Subject Alternative Names (SAN) on certificate loading for the vhost and later, in run time, validates SNI against the available SANs.


http_ct_required [true|false] Require presence of Content-Type header in a request. Ignored when http_ct_vals is present. Defaults: false.


http_trailer_split_allowed [true|false] Allow the same header appear in both request header part and chunked trailer part. Defaults: false.


http_methods METHOD [METHOD]... The list of accepted HTTP methods (see OWASP recommendations). Defaults: limit disabled (all methods are allowed). Example: http_methods get post head;


http_ct_vals ["CONTENT_TYPE"]... The list of accepted values for Content-Type header. Note that the full types must be specified, i.e. if you need to allow text/html and text/plain, then you should specify http_ct_vals "text/plain" "text/html", http_ct_vals "text/*" won't match the required values. Sets the value to true and ignore false for http_ct_required. Defaults: limit disabled (all values are allowed). Example: http_ct_vals "text/plain" "text/html";


http_resp_code_block RESPONSE_CODE [RESPONSE_CODE]... LIMIT TIME_FRAME_IN_SECONDS Block client if it cause to many errors on backend server in the selected time frame (responses from the Tempesta cache is not counted for this limit). See Password crackers section. RESPONSE_CODE - status code in the response from backend server. LIMIT - responses count. TIME_FRAME_IN_SECONDS - size of sliding window to count the limit. Defaults: limit disabled. Example: http_resp_code_block 403 404 502 20 5;


http_method_override_allowed [true|false] The option controls how the requests with method override headers (X-Http-Method, X-Method-Override, X-Http-Method-Override) are processed. Defaults: false. If the option is disabled (defaults) such requests are blocked. If the option is enabled, the overridden method is used as primary request method inside request processing logic. The overridden method must be allowed by http_methods directive, otherwise it will be blocked. Keep in mind that in all cases unsafe method cannot override safe method (GET, HEAD, OPTIONS, TRACE, PROPFIND).

Configuration examples

Example 1. Configure all Frang limits globally for all vhosts.

frang_limits {
    request_rate 20;
    request_burst 15;
    connection_rate 8;
    connection_burst 6;
    concurrent_connections 8;
    client_header_timeout 20;
    client_body_timeout 10;
    http_uri_len 1024;
    http_field_len 256;
    http_ct_required false;
    http_methods get post head;
    http_ct_vals "text/plain" "text/html";
    http_header_chunk_cnt 10;
    http_body_chunk_cnt 0;
    http_resp_code_block 403 404 502 20 5;
}

# The `frang_limits` section is listed before vhosts definitions, thus the
# vhosts and all their locations will be using the limits defined above.
vhost crm.example.com {
    ...
}
vhost example.com {
    ...
}
...

Example 2. Global/connection-level vs per-vhost/message limits.

listen 192.168.100.4:443 proto=https;

srv_group default {
	server 127.0.0.1:8080 conns_n=4;
}

vhost default {
	tls_certificate /root/tempesta/etc/tfw-root.crt;
	tls_certificate_key /root/tempesta/etc/tfw-root.key;

	resp_hdr_set Strict-Transport-Security "max-age=31536000; includeSubDomains";

	frang_limits {
		http_methods GET;
		http_uri_len 512;
		http_resp_code_block 400 403 404 3 10;
	}

	proxy_pass default;
}

cache 0;

frang_limits {
	client_header_timeout 20;
	client_body_timeout 10;
	http_header_chunk_cnt 10;
	http_body_chunk_cnt 0;
}

block_action attack reply;

http_chain {
	-> default;
}

Example 3. Overriding Frang limits default values.

# Any limits can be overridden, update just one to keep the example short.
frang_limits {
    http_uri_len 1024;
}

vhost crm.example.com {
    # No `frang_limits` section in this vhost, effective Frang configuration is:
    # http_uri_len 1024;
    ...
}

frang_limits {
    http_uri_len 2048;
}
# Frang configuration was updated, current defaults is:
# http_uri_len 2048;

vhost example.com {
    location prefix "/img/" {
        frang_limits {
            http_uri_len 3096;
        }
        # Frang configuration was updated inside location, current defaults is:
        # http_uri_len 3096;
    }
    location prefix "/video/" {
        # Frang configuration was not updated inside location, keep using
        # global defaults. Effective configuration is:
        # http_uri_len 2048;
    }

    frang_limits {
        http_uri_len 1024;
    }
    # Frang configuration was updated, current defaults for other locations is:
    # http_uri_len 1024;

    location prefix "/docs/" {
        # Frang configuration was not updated inside location, keep using
        # global defaults. Effective configuration is:
        # http_uri_len 1024;
    }

    # Frang limits for implicit default location is:
    # http_uri_len 1024;
}

vhost test-net.com {
    # Frang settings was updated multiple times in vhost `example.com` section,
    # but that changes doesn't affect global defaults. Effective configuration
    # is:
    # http_uri_len 2048;
}
...

Custom character sets

Following configuration options define allowed character sets in various HTTP fields:

  • http_uri_brange - URI path. Note that Accept and Referer headers are also verified by the characters set.

  • http_token_brange - each header field value defined with 'token' by RFC 7230, e.g. unknown for Tempesta FW HTTP methods and HTTP headers, Connection or Transfer-Encoding extension values, Cookie name (note that value is processed with its own alphabet http_cookie_brange), E-Tag value.

  • http_qetoken_brange - 'token' with DQUOTE and '=', e.g. Cache-Control, Pragma, and Keep-Alive headers extension values.

  • http_nctl_brange - 'non-control characters' for generic filed values defined by RFC 7230 Apendix B and RFC 5234 Apendix B.1. Currently used for HTTP date headers, such as Expires, Date, Last-Modified, and If-Modified-Since, extension values only.

  • http_xff_brange - X-Forwarded-For Node ID defined by RFC 7239.

  • http_etag_brange - ETag accepted characters set (RFC 7232 2.3).

  • http_cookie_brange - Cookie header values.

  • http_ctext_vchar_brange - 'ctext | VCHAR' headers, e.g. User-Agent.

Syntax for the directives is:

http_XXX_brange RANGES

, where RANGES is a list of space separated ranges (integers in range [0-255]) or integers for single characters. Hex and decimal encodings are accepted. Example:

http_uri_brange 0x2f 0x61-0x7a 48 0x2A;

HTTP Strict Transport Security (HSTS)

HTTP Strict Transport Security (HSTS) is defined in (RFC 6797) and can be implemented just by adding Strict-Transport-Security header to a required vhost or location:

resp_hdr_set Strict-Transport-Security "max-age=31536000; includeSubDomains"

Filtering

Let's see a simple example to understand Tempesta filtering.

Run Tempesta FW with Frang configured and put some load onto the system to make Frang generate a blocking rule:

$ dmesg | grep frang
[tempesta] Warning: frang: connections max num. exceeded for 192.168.0.1: 9 (lim=8)

Frang's rate limiting calls the filter module that stores the blocked IPs in Tempesta DB, so now we can run some queries on the database (you can read more about tdbq):

# ./tdbq -a info

Tempesta DB version: 0.1.14
Open tables: filter

INFO: records=1 status=OK zero-copy

The table filter contains all blocked IP addresses.

Clone this wiki locally