diff --git a/doc/configuration.txt b/doc/configuration.txt index f21add221..1aa7cdd8e 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -3409,6 +3409,7 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | { track-sc0 | track-sc1 | track-sc2 } [table ] | sc-inc-gpc0() | sc-set-gpt0() | + silent-drop | lua } [ { if | unless } ] @@ -3441,7 +3442,8 @@ http-request { allow | deny | tarpit | auth [realm ] | redirect | 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 ] | redirect | 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 | set-nice | set-var() | sc-inc-gpc0() | sc-set-gpt0() | + silent-drop | lua } [ { if | unless } ] @@ -4014,6 +4034,23 @@ http-response { allow | deny | add-header | set-nice | designated by . 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 [{if | unless} ] 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 [{if | unless} ] - set-gpt0() - lua - set-var() + - 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 [{if | unless} ] 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. diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 9c0dc37d6..f6988898e 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include @@ -38,6 +40,7 @@ #include #include #include +#include #include #include @@ -49,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -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); }