mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-17 08:54:41 +00:00
MEDIUM: tcp: add new tcp action "silent-drop"
This stops the evaluation of the rules and makes the client-facing connection suddenly disappear using a system-dependant 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 undestand the impact of using this mechanism. All stateful equipments 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.
This commit is contained in:
parent
c870bfd0e9
commit
2d392c2c2f
@ -3409,6 +3409,7 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
|
||||
{ track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
|
||||
sc-inc-gpc0(<sc-id>) |
|
||||
sc-set-gpt0(<sc-id>) <int> |
|
||||
silent-drop |
|
||||
lua <function name>
|
||||
}
|
||||
[ { if | unless } <condition> ]
|
||||
@ -3441,7 +3442,8 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
|
||||
efficient against very dumb robots, and will significantly reduce the
|
||||
load on firewalls compared to a "deny" rule. But when facing "correctly"
|
||||
developed robots, it can make things worse by forcing haproxy and the
|
||||
front firewall to support insane number of concurrent connections.
|
||||
front firewall to support insane number of concurrent connections. See
|
||||
also the "silent-drop" action below.
|
||||
|
||||
- "auth" : this stops the evaluation of the rules and immediately responds
|
||||
with an HTTP 401 or 407 error code to invite the user to present a valid
|
||||
@ -3727,6 +3729,23 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
|
||||
|
||||
When set-src is successful, the source port is set to 0.
|
||||
|
||||
- "silent-drop" : this stops the evaluation of the rules and makes the
|
||||
client-facing connection suddenly disappear using a system-dependant 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 undestand the impact of using
|
||||
this mechanism. All stateful equipments 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.
|
||||
|
||||
There is no limit to the number of http-request statements per instance.
|
||||
|
||||
It is important to know that http-request rules are processed very early in
|
||||
@ -3796,6 +3815,7 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
|
||||
set-var(<var-name>) <expr> |
|
||||
sc-inc-gpc0(<sc-id>) |
|
||||
sc-set-gpt0(<sc-id>) <int> |
|
||||
silent-drop |
|
||||
lua <function name>
|
||||
}
|
||||
[ { if | unless } <condition> ]
|
||||
@ -4014,6 +4034,23 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
|
||||
designated by <sc-id>. If an error occurs, this action silently fails and
|
||||
the actions evaluation continues.
|
||||
|
||||
- "silent-drop" : this stops the evaluation of the rules and makes the
|
||||
client-facing connection suddenly disappear using a system-dependant 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 undestand the impact of using
|
||||
this mechanism. All stateful equipments 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.
|
||||
|
||||
There is no limit to the number of http-response statements per instance.
|
||||
|
||||
It is important to know that http-response rules are processed very early in
|
||||
@ -8516,6 +8553,24 @@ tcp-request connection <action> [{if | unless} <condition>]
|
||||
an error occurs, this action silently fails and the actions evaluation
|
||||
continues.
|
||||
|
||||
- "silent-drop" :
|
||||
This stops the evaluation of the rules and makes the client-facing
|
||||
connection suddenly disappear using a system-dependant 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 undestand the impact
|
||||
of using this mechanism. All stateful equipments 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.
|
||||
|
||||
Note that the "if/unless" condition is optional. If no condition is set on
|
||||
the action, it is simply performed unconditionally. That can be useful for
|
||||
"track-sc*" actions as well as for changing the default action to a reject.
|
||||
@ -8588,6 +8643,7 @@ tcp-request content <action> [{if | unless} <condition>]
|
||||
- set-gpt0(<sc-id>) <int>
|
||||
- lua <function>
|
||||
- set-var(<var-name>) <expr>
|
||||
- silent-drop
|
||||
|
||||
They have the same meaning as their counter-parts in "tcp-request connection"
|
||||
so please refer to that section for a complete description.
|
||||
@ -8808,6 +8864,24 @@ tcp-response content <action> [{if | unless} <condition>]
|
||||
an error occurs, this action silently fails and the actions evaluation
|
||||
continues.
|
||||
|
||||
- "silent-drop" :
|
||||
This stops the evaluation of the rules and makes the client-facing
|
||||
connection suddenly disappear using a system-dependant 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 undestand the impact
|
||||
of using this mechanism. All stateful equipments 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.
|
||||
|
||||
Note that the "if/unless" condition is optional. If no condition is set on
|
||||
the action, it is simply performed unconditionally. That can be useful for
|
||||
for changing the default action to a reject.
|
||||
|
106
src/proto_tcp.c
106
src/proto_tcp.c
@ -25,6 +25,8 @@
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <common/cfgparse.h>
|
||||
#include <common/compat.h>
|
||||
@ -38,6 +40,7 @@
|
||||
#include <types/global.h>
|
||||
#include <types/capture.h>
|
||||
#include <types/server.h>
|
||||
#include <types/connection.h>
|
||||
|
||||
#include <proto/acl.h>
|
||||
#include <proto/action.h>
|
||||
@ -49,6 +52,7 @@
|
||||
#include <proto/log.h>
|
||||
#include <proto/port_range.h>
|
||||
#include <proto/protocol.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/proto_tcp.h>
|
||||
#include <proto/proxy.h>
|
||||
#include <proto/sample.h>
|
||||
@ -1416,6 +1420,65 @@ int tcp_exec_req_rules(struct session *sess)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response} */
|
||||
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);
|
||||
|
||||
if (!conn)
|
||||
goto out;
|
||||
|
||||
if (!conn_ctrl_ready(conn))
|
||||
goto out;
|
||||
|
||||
conn_sock_drain(conn);
|
||||
#ifdef TCP_QUICKACK
|
||||
/* re-enable quickack if it was disabled to ack all data and avoid
|
||||
* retransmits from the client that might trigger a real reset.
|
||||
*/
|
||||
setsockopt(conn->t.sock.fd, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
|
||||
#endif
|
||||
/* lingering must absolutely be disabled so that we don't send a
|
||||
* shutdown(), this is critical to the TCP_REPAIR trick. When no stream
|
||||
* is present, returning with ERR will cause lingering to be disabled.
|
||||
*/
|
||||
if (strm)
|
||||
strm->si[0].flags |= SI_FL_NOLINGER;
|
||||
|
||||
#ifdef TCP_REPAIR
|
||||
if (setsockopt(conn->t.sock.fd, SOL_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.
|
||||
*/
|
||||
#ifdef IP_TTL
|
||||
setsockopt(conn->t.sock.fd, SOL_IP, IP_TTL, &one, sizeof(one));
|
||||
#endif
|
||||
out:
|
||||
/* kill the stream if any */
|
||||
if (strm) {
|
||||
channel_abort(&strm->req);
|
||||
channel_abort(&strm->res);
|
||||
strm->req.analysers = 0;
|
||||
strm->res.analysers = 0;
|
||||
strm->be->be_counters.denied_req++;
|
||||
if (!(strm->flags & SF_ERR_MASK))
|
||||
strm->flags |= SF_ERR_PRXCOND;
|
||||
if (!(strm->flags & SF_FINST_MASK))
|
||||
strm->flags |= SF_FINST_R;
|
||||
}
|
||||
|
||||
sess->fe->fe_counters.denied_req++;
|
||||
if (sess->listener->counters)
|
||||
sess->listener->counters->denied_req++;
|
||||
|
||||
return ACT_RET_STOP;
|
||||
}
|
||||
|
||||
/* Parse a tcp-response rule. Return a negative value in case of failure */
|
||||
static int tcp_parse_response_rule(char **args, int arg, int section_type,
|
||||
struct proxy *curpx, struct proxy *defpx,
|
||||
@ -1953,6 +2016,17 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parse a "silent-drop" action. It takes no argument. 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,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->action_ptr = tcp_exec_action_silent_drop;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* All supported sample fetch functions must be declared here */
|
||||
@ -2299,6 +2373,33 @@ static struct bind_kw_list bind_kws = { "TCP", { }, {
|
||||
{ NULL, NULL, 0 },
|
||||
}};
|
||||
|
||||
|
||||
static struct action_kw_list tcp_req_conn_actions = {ILH, {
|
||||
{ "silent-drop", tcp_parse_silent_drop },
|
||||
{ /* END */ }
|
||||
}};
|
||||
|
||||
static struct action_kw_list tcp_req_cont_actions = {ILH, {
|
||||
{ "silent-drop", tcp_parse_silent_drop },
|
||||
{ /* END */ }
|
||||
}};
|
||||
|
||||
static struct action_kw_list tcp_res_cont_actions = {ILH, {
|
||||
{ "silent-drop", tcp_parse_silent_drop },
|
||||
{ /* END */ }
|
||||
}};
|
||||
|
||||
static struct action_kw_list http_req_actions = {ILH, {
|
||||
{ "silent-drop", tcp_parse_silent_drop },
|
||||
{ /* END */ }
|
||||
}};
|
||||
|
||||
static struct action_kw_list http_res_actions = {ILH, {
|
||||
{ "silent-drop", tcp_parse_silent_drop },
|
||||
{ /* END */ }
|
||||
}};
|
||||
|
||||
|
||||
__attribute__((constructor))
|
||||
static void __tcp_protocol_init(void)
|
||||
{
|
||||
@ -2308,6 +2409,11 @@ static void __tcp_protocol_init(void)
|
||||
cfg_register_keywords(&cfg_kws);
|
||||
acl_register_keywords(&acl_kws);
|
||||
bind_register_keywords(&bind_kws);
|
||||
tcp_req_conn_keywords_register(&tcp_req_conn_actions);
|
||||
tcp_req_cont_keywords_register(&tcp_req_cont_actions);
|
||||
tcp_res_cont_keywords_register(&tcp_res_cont_actions);
|
||||
http_req_keywords_register(&http_req_actions);
|
||||
http_res_keywords_register(&http_res_actions);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user