mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-01 14:38:28 +00:00
MEDIUM: tcp-act: add parameter rst-ttl to silent-drop
The silent-drop action was extended with an additional optional parameter, [rst-ttl <ttl> ], causing HAProxy to send a TCP RST with the specified TTL towards the client. With this behaviour, the connection state on your own client- facing middle-boxes (load balancers, firewalls) will be purged, but the client will still assume the TCP connection is up because the TCP RST packet expires before reaching the client.
This commit is contained in:
parent
a0abec8bc0
commit
d9b7174d99
@ -6443,7 +6443,7 @@ http-request <action> [options...] [ { if | unless } <condition> ]
|
||||
- set-var(<var-name>[,<cond> ...]) <expr>
|
||||
- set-var-fmt(<var-name>[,<cond> ...]) <fmt>
|
||||
- send-spoe-group <engine-name> <group-name>
|
||||
- silent-drop
|
||||
- silent-drop [ rst-ttl <ttl> ]
|
||||
- strict-mode { on | off }
|
||||
- tarpit [ { status | deny_status } <code>] ...
|
||||
- track-sc0 <key> [table <table>]
|
||||
@ -7398,24 +7398,27 @@ http-request set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unless } <condi
|
||||
http-request set-var(req.my_var) req.fhdr(user-agent),lower
|
||||
http-request set-var-fmt(txn.from) %[src]:%[src_port]
|
||||
|
||||
http-request silent-drop [ { if | unless } <condition> ]
|
||||
http-request silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
client from being notified. The effect it then that the client still sees an
|
||||
established connection while there's none on HAProxy. The purpose is to
|
||||
achieve a comparable effect to "tarpit" except that it doesn't use any local
|
||||
resource at all on the machine running HAProxy. It can resist much higher
|
||||
loads than "tarpit", and slow down stronger attackers. It is important to
|
||||
understand the impact of using this mechanism. All stateful equipment placed
|
||||
between the client and HAProxy (firewalls, proxies, load balancers) will also
|
||||
keep the established connection for a long time and may suffer from this
|
||||
action.
|
||||
On modern Linux systems running with enough privileges, the TCP_REPAIR socket
|
||||
option is used to block the emission of a TCP reset. On other systems, the
|
||||
socket's TTL is reduced to 1 so that the TCP reset doesn't pass the first
|
||||
router, though it's still delivered to local networks. Do not use it unless
|
||||
you fully understand how it works.
|
||||
This stops the evaluation of the rules and removes the client-facing
|
||||
connection in a configurable way: When called without the rst-ttl argument,
|
||||
we try to prevent sending any FIN or RST packet back to the client by
|
||||
using TCP_REPAIR. If this fails (mainly because of missing privileges),
|
||||
we fall back to sending a RST packet with a TTL of 1.
|
||||
|
||||
The effect is that the client still sees an established connection while
|
||||
there is none on HAProxy, saving resources. However, stateful equipment
|
||||
placed between the HAProxy and the client (firewalls, proxies,
|
||||
load balancers) will also keep the established connection in their
|
||||
session tables.
|
||||
|
||||
The optional rst-ttl changes this behaviour: TCP_REPAIR is not used,
|
||||
and a RST packet with a configurable TTL is sent. When set to a
|
||||
reasonable value, the RST packet travels through your own equipment,
|
||||
deleting the connection in your middle-boxes, but does not arrive at
|
||||
the client. Future packets from the client will then be dropped
|
||||
already by your middle-boxes. These "local RST"s protect your resources,
|
||||
but not the client's. Do not use it unless you fully understand how it works.
|
||||
|
||||
http-request strict-mode { on | off } [ { if | unless } <condition> ]
|
||||
|
||||
@ -7849,7 +7852,7 @@ http-response set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unless } <cond
|
||||
inline. Please refer to "http-request set-var" and "http-request set-var-fmt"
|
||||
for a complete description.
|
||||
|
||||
http-response silent-drop [ { if | unless } <condition> ]
|
||||
http-response silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
@ -12650,7 +12653,7 @@ tcp-request connection set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unles
|
||||
scopes. Please refer to "http-request set-var" and "http-request set-var-fmt"
|
||||
for a complete description.
|
||||
|
||||
tcp-request connection silent-drop [ { if | unless } <condition> ]
|
||||
tcp-request connection silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
@ -12972,7 +12975,7 @@ tcp-request content set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unless }
|
||||
inline. Please refer to "http-request set-var" and "http-request set-var-fmt"
|
||||
for a complete description.
|
||||
|
||||
tcp-request content silent-drop [ { if | unless } <condition> ]
|
||||
tcp-request content silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
@ -13230,7 +13233,7 @@ tcp-request session set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unless }
|
||||
inline. Please refer to "http-request set-var" and "http-request set-var-fmt"
|
||||
for a complete description.
|
||||
|
||||
tcp-request session silent-drop [ { if | unless } <condition> ]
|
||||
tcp-request session silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
@ -13400,7 +13403,7 @@ tcp-response content set-var-fmt(<var-name>[,<cond> ...]) <fmt> [ { if | unless
|
||||
inline. Please refer to "http-request set-var" and "http-request set-var-fmt"
|
||||
for a complete description.
|
||||
|
||||
tcp-response content silent-drop [ { if | unless } <condition> ]
|
||||
tcp-response content silent-drop [ rst-ttl <ttl> ] [ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and makes the client-facing connection
|
||||
suddenly disappear using a system-dependent way that tries to prevent the
|
||||
|
@ -261,11 +261,25 @@ static enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response} */
|
||||
/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response}.
|
||||
* If rule->arg.act.p[0] is 0, TCP_REPAIR is tried first, with a fallback to
|
||||
* sending a RST with TTL 1 towards the client. If it is [1-255], we will skip
|
||||
* TCP_REPAIR and prepare the socket to send a RST with the requested TTL when
|
||||
* the connection is killed by channel_abort().
|
||||
*/
|
||||
static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct proxy *px,
|
||||
struct session *sess, struct stream *strm, int flags)
|
||||
{
|
||||
struct connection *conn = objt_conn(sess->origin);
|
||||
unsigned int ttl __maybe_unused = (uintptr_t)rule->arg.act.p[0];
|
||||
char tcp_repair_enabled __maybe_unused;
|
||||
|
||||
if (ttl == 0) {
|
||||
tcp_repair_enabled = 1;
|
||||
ttl = 1;
|
||||
} else {
|
||||
tcp_repair_enabled = 0;
|
||||
}
|
||||
|
||||
if (!conn)
|
||||
goto out;
|
||||
@ -298,22 +312,27 @@ static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct
|
||||
HA_ATOMIC_OR(&fdtab[conn->handle.fd].state, FD_LINGER_RISK);
|
||||
|
||||
#ifdef TCP_REPAIR
|
||||
if (setsockopt(conn->handle.fd, IPPROTO_TCP, TCP_REPAIR, &one, sizeof(one)) == 0) {
|
||||
/* try to put socket in repair mode if sending a RST was not requested by
|
||||
* config. this often fails due to missing permissions (CAP_NET_ADMIN capability)
|
||||
*/
|
||||
if (tcp_repair_enabled && (setsockopt(conn->handle.fd, IPPROTO_TCP, TCP_REPAIR, &one, sizeof(one)) == 0)) {
|
||||
/* socket will be quiet now */
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
/* either TCP_REPAIR is not defined or it failed (eg: permissions).
|
||||
* Let's fall back on the TTL trick, though it only works for routed
|
||||
* network and has no effect on local net.
|
||||
|
||||
/* Either TCP_REPAIR is not defined, it failed (eg: permissions), or was
|
||||
* not executed because a RST with a specific TTL was requested to be sent.
|
||||
* Set the TTL of the client connection before the connection is killed
|
||||
* by channel_abort and a RST packet will be emitted by the TCP/IP stack.
|
||||
*/
|
||||
#ifdef IP_TTL
|
||||
if (conn->src && conn->src->ss_family == AF_INET)
|
||||
setsockopt(conn->handle.fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
|
||||
setsockopt(conn->handle.fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
|
||||
#endif
|
||||
#ifdef IPV6_UNICAST_HOPS
|
||||
if (conn->src && conn->src->ss_family == AF_INET6)
|
||||
setsockopt(conn->handle.fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
|
||||
setsockopt(conn->handle.fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
|
||||
#endif
|
||||
out:
|
||||
/* kill the stream if any */
|
||||
@ -480,15 +499,41 @@ static enum act_parse_ret tcp_parse_set_tos(const char **args, int *cur_arg, str
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Parse a "silent-drop" action. It takes no argument. It returns ACT_RET_PRS_OK on
|
||||
* success, ACT_RET_PRS_ERR on error.
|
||||
/* Parse a "silent-drop" action. It may take 2 optional arguments to define a
|
||||
* "rst-ttl" parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR
|
||||
* on error.
|
||||
*/
|
||||
static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *orig_arg, struct proxy *px,
|
||||
static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *cur_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
unsigned int rst_ttl = 0;
|
||||
char *endp;
|
||||
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = tcp_exec_action_silent_drop;
|
||||
|
||||
if (strcmp(args[*cur_arg], "rst-ttl") == 0) {
|
||||
if (!*args[*cur_arg + 1]) {
|
||||
memprintf(err, "missing rst-ttl value\n");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
rst_ttl = (unsigned int)strtoul(args[*cur_arg + 1], &endp, 0);
|
||||
|
||||
if (endp && *endp != '\0') {
|
||||
memprintf(err, "invalid character starting at '%s' (value 1-255 expected)\n",
|
||||
endp);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
if ((rst_ttl == 0) || (rst_ttl > 255) ) {
|
||||
memprintf(err, "valid rst-ttl values are [1-255]\n");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
*cur_arg += 2;
|
||||
}
|
||||
|
||||
rule->arg.act.p[0] = (void *)(uintptr_t)rst_ttl;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user