diff --git a/doc/configuration.txt b/doc/configuration.txt index e6e9727e8..54c4b5efb 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -962,11 +962,12 @@ stick match - - X X stick on - - X X stick store-request - - X X stick-table - - X X -tcp-request accept - X X - +tcp-request connection accept - X X - +tcp-request connection reject - X X - +tcp-request connection track-counters - X X - tcp-request content accept - X X - tcp-request content reject - X X - tcp-request inspect-delay - X X - -tcp-request reject - X X - timeout check X - X X timeout client X X X - timeout clitimeout (deprecated) X X X - @@ -5251,7 +5252,7 @@ stick-table type {ip | integer | string [len ] } size about time format and section 7 avoud ACLs. -tcp-request accept [{if | unless} ] +tcp-request connection accept [{if | unless} ] Accept an incoming connection if/unless a layer 4 condition is matched May be used in sections : defaults | frontend | listen | backend no | yes | yes | no @@ -5280,7 +5281,108 @@ tcp-request accept [{if | unless} ] See section 7 about ACL usage. - See also : "tcp-request reject" and "tcp-request content" + See also : "tcp-request connection reject" and "tcp-request content" + + +tcp-request connection reject [{if | unless} ] + Reject an incoming connection if/unless a layer 4 condition is matched + May be used in sections : defaults | frontend | listen | backend + no | yes | yes | no + + Immediately after acceptance of a new incoming connection, it is possible to + evaluate some conditions to decide whether this connection must be accepted + or dropped. Those conditions cannot make use of any data contents because the + connection has not been read from yet, and the buffers are not yet allocated. + This can be used to selectively and very quickly accept or drop connections + from various sources with a very low overhead. If some contents need to be + inspected in order to take the decision, the "tcp-request content" statements + must be used instead. + + This statement rejects the connection if the condition is true (when used + with "if") or false (when used with "unless"). It is important to understand + that "accept" and "reject" rules are evaluated in their exact declaration + order, so that it is possible to build complex rules from them. There is no + specific limit to the number of rules which may be inserted. + + Note that the "if/unless" condition is optional. If no condition is set on + the action, it is simply performed unconditionally. + + If no "tcp-request" rules are matched, the default action is to accept the + connection, which implies that the "tcp-request accept" statement will only + make sense when combined with another "tcp-request reject" statement. + + Rejected connections do not even become a session, which is why they are + accounted separately for in the stats, as "denied connections". They are not + considered for the session rate-limit and are not logged either. The reason + is that these rules should only be used to filter extremely high connection + rates such as the ones encountered during a massive DDoS attack. Under these + conditions, the simple action of logging each event would make the system + collapse and would considerably lower the filtering capacity. If logging is + absolutely desired, then "tcp-request content" rules should be used instead. + + See section 7 about ACL usage. + + See also : "tcp-request connection accept" and "tcp-request content" + + +tcp-request connection track-counters [table ] + [{if | unless} ] + Enable tracking of session counters if/unless a layer 4 condition is matched + May be used in sections : defaults | frontend | listen | backend + no | yes | yes | no + + Arguments : + is the criterion the tracking key will be derived from. At the + moment, only "src" is supported. With it, the key will be the + connection's source IPv4 address. + +
is an optional table to use instead of the one from the current + proxy. All the counters for the matches and updates for the key + will then be performed in that table. + + Immediately after a new incoming connection has been accepted, it is possible + to enable tracking of some of this session's counters in a table. Doing so + serves two purposes : + - feed the entry with the session's counters that are relevant to the table + being pointed. These counters are then updated as often as possible, and + also systematically when the session ends. + + - keep a pointer to the entry in the table in the session to avoid having + to perform key lookups when complex ACL rules make use of the entry, + especially when the key is expensive to compute (eg: header-based). + + It is possible to evaluate some conditions to decide whether a track-counters + statement will apply or not. In this case, only the first matching rule will + apply and the other ones will be ignored. We could for instance imagine that + some hosts which are references in a white list make use of a different + counters table, or do not get accounted for. The tracking is enabled if the + condition is true (when used with "if") or false (when used with "unless"). + There is no specific limit to the number of rules which may be declared. + + It is important to understand that "accept", "reject" and "track-counters" + rules are evaluated in their exact declaration order, so that it is possible + to build complex rules from them. For instance, the following rule rejects + too fast connections without tracking them, to that they get accepted again + after some time despite activity, while the second one will still update the + counters when rejecting a connection. + + Example: + # reject too fast connection without counting them + tcp-request connection reject if { src_conn_rate gt 10 } + tcp-request connection track-counters src + + # reject too fast connection and count them + tcp-request connection track-counters src + tcp-request connection reject if { src_conn_rate gt 10 } + + + Note that the "if/unless" condition is optional. If no condition is set on + the action, it is simply performed unconditionally. + + See section 7 about ACL usage. + + See also : "tcp-request connection accept", "tcp-request connection reject", + "tcp-request content", and "stick-table". tcp-request content accept [{if | unless} ] @@ -5337,12 +5439,12 @@ tcp-request content reject [{if | unless} ] # reject SMTP connection if client speaks first tcp-request inspect-delay 30s acl content_present req_len gt 0 - tcp-request reject if content_present + tcp-request content reject if content_present # Forward HTTPS connection only if client speaks tcp-request inspect-delay 30s acl content_present req_len gt 0 - tcp-request accept if content_present + tcp-request content accept if content_present tcp-request reject See section 7 about ACL usage. @@ -5395,106 +5497,6 @@ tcp-request inspect-delay "timeout client". -tcp-request reject [{if | unless} ] - Reject an incoming connection if/unless a layer 4 condition is matched - May be used in sections : defaults | frontend | listen | backend - no | yes | yes | no - - Immediately after acceptance of a new incoming connection, it is possible to - evaluate some conditions to decide whether this connection must be accepted - or dropped. Those conditions cannot make use of any data contents because the - connection has not been read from yet, and the buffers are not yet allocated. - This can be used to selectively and very quickly accept or drop connections - from various sources with a very low overhead. If some contents need to be - inspected in order to take the decision, the "tcp-request content" statements - must be used instead. - - This statement rejects the connection if the condition is true (when used - with "if") or false (when used with "unless"). It is important to understand - that "accept" and "reject" rules are evaluated in their exact declaration - order, so that it is possible to build complex rules from them. There is no - specific limit to the number of rules which may be inserted. - - Note that the "if/unless" condition is optional. If no condition is set on - the action, it is simply performed unconditionally. - - If no "tcp-request" rules are matched, the default action is to accept the - connection, which implies that the "tcp-request accept" statement will only - make sense when combined with another "tcp-request reject" statement. - - Rejected connections do not even become a session, which is why they are - accounted separately for in the stats, as "denied connections". They are not - considered for the session rate-limit and are not logged either. The reason - is that these rules should only be used to filter extremely high connection - rates such as the ones encountered during a massive DDoS attack. Under these - conditions, the simple action of logging each event would make the system - collapse and would considerably lower the filtering capacity. If logging is - absolutely desired, then "tcp-request content" rules should be used instead. - - See section 7 about ACL usage. - - See also : "tcp-request accept" and "tcp-request content" - - -tcp-request track-counters [table
] [{if | unless} ] - Enable tracking of session counters if/unless a layer 4 condition is matched - May be used in sections : defaults | frontend | listen | backend - no | yes | yes | no - - Arguments : - is the criterion the tracking key will be derived from. At the - moment, only "src" is supported. With it, the key will be the - connection's source IPv4 address. - -
is an optional table to use instead of the one from the current - proxy. All the counters for the matches and updates for the key - will then be performed in that table. - - Immediately after a new incoming connection has been accepted, it is possible - to enable tracking of some of this session's counters in a table. Doing so - serves two purposes : - - feed the entry with the session's counters that are relevant to the table - being pointed. These counters are then updated as often as possible, and - also systematically when the session ends. - - - keep a pointer to the entry in the table in the session to avoid having - to perform key lookups when complex ACL rules make use of the entry, - especially when the key is expensive to compute (eg: header-based). - - It is possible to evaluate some conditions to decide whether a track-counters - statement will apply or not. In this case, only the first matching rule will - apply and the other ones will be ignored. We could for instance imagine that - some hosts which are references in a white list make use of a different - counters table, or do not get accounted for. The tracking is enabled if the - condition is true (when used with "if") or false (when used with "unless"). - There is no specific limit to the number of rules which may be declared. - - It is important to understand that "accept", "reject" and "track-counters" - rules are evaluated in their exact declaration order, so that it is possible - to build complex rules from them. For instance, the following rule rejects - too fast connections without tracking them, to that they get accepted again - after some time despite activity, while the second one will still update the - counters when rejecting a connection. - - Example: - # reject too fast connection without counting them - tcp-request reject if { src_conn_rate gt 10 } - tcp-request track-counters src - - # reject too fast connection and count them - tcp-request track-counters src - tcp-request reject if { src_conn_rate gt 10 } - - - Note that the "if/unless" condition is optional. If no condition is set on - the action, it is simply performed unconditionally. - - See section 7 about ACL usage. - - See also : "tcp-request accept", "tcp-request reject", "tcp-request content", - and "stick-table". - - timeout check Set additional check timeout, but only after a connection has been already established. diff --git a/src/proto_tcp.c b/src/proto_tcp.c index e87860b5e..66b8a131e 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -799,6 +799,74 @@ int tcp_exec_req_rules(struct session *s) return result; } +/* Parse a tcp-request rule. Return a negative value in case of failure */ +static int tcp_parse_request_rule(char **args, int arg, int section_type, + struct proxy *curpx, struct proxy *defpx, + struct tcp_rule *rule, char *err, int errlen) +{ + if (curpx == defpx) { + snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections", + args[0], args[1]); + return -1; + } + + if (!strcmp(args[arg], "accept")) { + arg++; + rule->action = TCP_ACT_ACCEPT; + } + else if (!strcmp(args[arg], "reject")) { + arg++; + rule->action = TCP_ACT_REJECT; + } + else if (strcmp(args[arg], "track-fe-counters") == 0) { + int ret; + + arg++; + ret = parse_track_counters(args, &arg, section_type, curpx, + &rule->act_prm.trk_ctr, defpx, err, errlen); + + if (ret < 0) /* nb: warnings are not handled yet */ + return -1; + + rule->action = TCP_ACT_TRK_FE_CTR; + } + else if (strcmp(args[arg], "track-be-counters") == 0) { + int ret; + + arg++; + ret = parse_track_counters(args, &arg, section_type, curpx, + &rule->act_prm.trk_ctr, defpx, err, errlen); + + if (ret < 0) /* nb: warnings are not handled yet */ + return -1; + + rule->action = TCP_ACT_TRK_BE_CTR; + } + else { + snprintf(err, errlen, + "'%s %s' expects 'accept', 'reject', 'track-fe-counters' " + "or 'track-be-counters' in %s '%s' (was '%s')", + args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]); + return -1; + } + + if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) { + if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) { + snprintf(err, errlen, + "error detected in %s '%s' while parsing '%s' condition", + proxy_type_str(curpx), curpx->id, args[arg]); + return -1; + } + } + else if (*args[arg]) { + snprintf(err, errlen, + "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')", + args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]); + return -1; + } + return 0; +} + /* This function should be called to parse a line starting with the "tcp-request" * keyword. */ @@ -809,7 +877,6 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx, unsigned int val; int retlen; int warn = 0; - int pol = ACL_COND_NONE; int arg; struct tcp_rule *rule; @@ -848,70 +915,10 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx, rule = (struct tcp_rule *)calloc(1, sizeof(*rule)); arg = 1; - if (!strcmp(args[1], "content")) { + if (strcmp(args[1], "content") == 0) { arg++; - if (curpx == defpx) { - snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections", - args[0], args[1]); + if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, errlen) < 0) goto error; - } - - if (!strcmp(args[2], "accept")) { - arg++; - rule->action = TCP_ACT_ACCEPT; - } - else if (!strcmp(args[2], "reject")) { - arg++; - rule->action = TCP_ACT_REJECT; - } - else if (strcmp(args[2], "track-fe-counters") == 0) { - int ret; - - arg++; - ret = parse_track_counters(args, &arg, section_type, curpx, - &rule->act_prm.trk_ctr, defpx, err, errlen); - - if (ret < 0) /* nb: warnings are not handled yet */ - goto error; - - rule->action = TCP_ACT_TRK_FE_CTR; - } - else if (strcmp(args[2], "track-be-counters") == 0) { - int ret; - - arg++; - ret = parse_track_counters(args, &arg, section_type, curpx, - &rule->act_prm.trk_ctr, defpx, err, errlen); - - if (ret < 0) /* nb: warnings are not handled yet */ - goto error; - - rule->action = TCP_ACT_TRK_BE_CTR; - } - else { - retlen = snprintf(err, errlen, - "'%s %s' expects 'accept', 'reject', 'track-fe-counters' or 'track-be-counters' in %s '%s' (was '%s')", - args[0], args[1], proxy_type_str(curpx), curpx->id, args[2]); - goto error; - } - - pol = ACL_COND_NONE; - rule->cond = NULL; - - if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) { - if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) { - retlen = snprintf(err, errlen, - "error detected in %s '%s' while parsing '%s' condition", - proxy_type_str(curpx), curpx->id, args[arg]); - goto error; - } - } - else if (*args[arg]) { - retlen = snprintf(err, errlen, - "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')", - args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]); - goto error; - } if (rule->cond && (rule->cond->requires & ACL_USE_RTR_ANY)) { struct acl *acl; @@ -925,96 +932,47 @@ static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx, name); warn++; } - LIST_INIT(&rule->list); - LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list); - return warn; } - - /* OK so we're in front of plain L4 rules */ - - if (strcmp(args[1], "accept") == 0) { + else if (strcmp(args[1], "connection") == 0) { arg++; - rule->action = TCP_ACT_ACCEPT; - } - else if (strcmp(args[1], "reject") == 0) { - arg++; - rule->action = TCP_ACT_REJECT; - } - else if (strcmp(args[1], "track-fe-counters") == 0) { - int ret; - arg++; - ret = parse_track_counters(args, &arg, section_type, curpx, - &rule->act_prm.trk_ctr, defpx, err, errlen); + if (!(curpx->cap & PR_CAP_FE)) { + snprintf(err, errlen, "%s %s is not allowed because %s %s is not a frontend", + args[0], args[1], proxy_type_str(curpx), curpx->id); + return -1; + } - if (ret < 0) /* nb: warnings are not handled yet */ + if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, errlen) < 0) goto error; - rule->action = TCP_ACT_TRK_FE_CTR; - } - else if (strcmp(args[1], "track-be-counters") == 0) { - int ret; + if (rule->cond && (rule->cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) { + struct acl *acl; + const char *name; - arg++; - ret = parse_track_counters(args, &arg, section_type, curpx, - &rule->act_prm.trk_ctr, defpx, err, errlen); + acl = cond_find_require(rule->cond, ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY); + name = acl ? acl->name : "(unknown)"; - if (ret < 0) /* nb: warnings are not handled yet */ - goto error; - - rule->action = TCP_ACT_TRK_BE_CTR; + if (acl->requires & (ACL_USE_L6_ANY|ACL_USE_L7_ANY)) { + retlen = snprintf(err, errlen, + "'%s %s' may not reference acl '%s' which makes use of " + "payload in %s '%s'. Please use '%s content' for this.", + args[0], args[1], name, proxy_type_str(curpx), curpx->id, args[0]); + goto error; + } + if (acl->requires & ACL_USE_RTR_ANY) + retlen = snprintf(err, errlen, + "acl '%s' involves some response-only criteria which will be ignored.", + name); + warn++; + } } else { retlen = snprintf(err, errlen, - "'%s' expects 'inspect-delay', 'content', 'accept', 'reject', 'track-fe-counters' or 'track-be-counters' in %s '%s' (was '%s')", + "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (was '%s')", args[0], proxy_type_str(curpx), curpx->id, args[1]); goto error; } - if (curpx == defpx) { - snprintf(err, errlen, "%s %s is not allowed in 'defaults' sections", - args[0], args[1]); - goto error; - } - - pol = ACL_COND_NONE; - - if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) { - if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) { - retlen = snprintf(err, errlen, - "error detected in %s '%s' while parsing '%s' condition", - proxy_type_str(curpx), curpx->id, args[arg]); - goto error; - } - } - else if (*args[arg]) { - retlen = snprintf(err, errlen, - "'%s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')", - args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]); - goto error; - } - - if (rule->cond && (rule->cond->requires & (ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY))) { - struct acl *acl; - const char *name; - - acl = cond_find_require(rule->cond, ACL_USE_RTR_ANY|ACL_USE_L6_ANY|ACL_USE_L7_ANY); - name = acl ? acl->name : "(unknown)"; - - if (acl->requires & (ACL_USE_L6_ANY|ACL_USE_L7_ANY)) { - retlen = snprintf(err, errlen, - "'%s %s' may not reference acl '%s' which makes use of payload in %s '%s'. Please use '%s content' for this.", - args[0], args[1], name, proxy_type_str(curpx), curpx->id, args[0]); - goto error; - } - if (acl->requires & ACL_USE_RTR_ANY) - retlen = snprintf(err, errlen, - "acl '%s' involves some response-only criteria which will be ignored.", - name); - - warn++; - } - LIST_INIT(&rule->list); LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list); return warn;