-
Notifications
You must be signed in to change notification settings - Fork 103
HTTP security
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. Implicitdefault
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.
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) - this is true for all connection level limits.
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.
ip_block [off|on]
If disabled, client connection(-s) are closed of security events (the way of closing
depends on block_action attack
section). If enabled, all client connections with
this IP are aborted (with RST packet) and new clients 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. This directive can be used against DDoS
HTTP/2 Rapid Reset.
In HTTP/2 counts the number of HEADERS frames (with and without END_STREAM, END_HEADERS flags).
The option can be used independently from request_burst
limit.
0
to disable the limit. Defaults: 0
(disabled).
request_burst [NUM]
Maximum burst of request rate on short periods of time (125 ms). This directive can be
used against DDoS HTTP/2 Rapid Reset.
In HTTP/2 counts the number of HEADERS frames (with and without END_STREAM, END_HEADERS flags).
The option can be used independently from request_rate
limit.
0
to disable the limit. Defaults: 0
(disabled).
tcp_connection_rate [NUM]
Maximum number of new connection openings (both TLS and non TLS) per second from a single client.
0
to disable the limit. Defaults: 0
(disabled).
tcp_connection_burst [NUM]
Maximum burst of new connection openings rate (both TLS and non TLS) 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 tcp_connection_rate
limit.
0
to disable the limit. Defaults: 0
(disabled).
concurrent_tcp_connections [NUM]
Maximum number of new concurrent connections (both TLS and non TLS) 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.
0
to disable the limit. 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.
0
to disable the limit. Defaults: 0
(disabled).
Some of the limits (all *_burst
and *_rate
limits) 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.
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
- resets the client TCP connection with
RST
segment and - 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 triggeredhttp_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 ofForwarded
header and authority information (HTTP/1.1, HTTP/2) - Enforce equality of absoluteURI host part and
Host
header (HTTP/1.1)
Defaults: true
.
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).
There is also one special method (unknown) which makes any method accepted.
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).
To clarify possible combinations of block_action
and ip_block
let's
consider all the combinations:
-
ip_block off
,block_action attack reply
: this configuration doesn't impact connection level limits, so if such a limit is triggered, then just TCPRST
is sent to a malicious client. If a message level limit is triggered, then a TCP connection closed gracefully (FIN
) and HTTP error response is sent. -
ip_block off
,block_action attack drop
: triggering a limit on any level leads to a TCP connection reset (RST
) and no HTTP responses. -
ip_block on
,block_action attack drop
: triggering a limit on any level leads to a TCP connection reset (RST
), client IP blocking and no HTTP responses. -
ip_block on
,block_action attack reply
: this is equivalent to the previous one for connection level limits (TCPRST
, IP blocking and no HTTP responses), but for the message level limits this is a misconfiguration sincereply
doesn't work together with blocking IP (TCP can not operate with a blocked peer on the IP layer).
Example 1. Configure all Frang limits globally for all vhosts.
frang_limits {
request_rate 20;
request_burst 15;
tcp_connection_rate 8;
tcp_connection_burst 6;
concurrent_tcp_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;
}
...
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)
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"
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.