Hello,

On Fri, Jun 24, James Brown wrote:
> +1 I am also using a fake backend with no servers and a 503 errorfile, and
> it confuses everybody who looks at the config or the metrics. Being able to
> directly emit a 429 would be fantastic.

This is my first attempt in adding deny_status to:
http-request tarpit [deny_status <status>]

First patch updates parse_http_req_cond so config parser accepts
[deny_status <status>] for http-request tarpit (and sets
rule->deny_status).

The second patch updates http_process_req_common/http_process_tarpit to
use deny_status. http_process_tarpit has a switch statement for mapping
txn->status (200<->504) to HTTP_ERR_<num>(enum). Is this reasonable ?

Do tarpitted (http-request tarpit / req(i)tarpit) requests always
go thru http_process_req_common (so txn->status is always set to
txn->status = http_err_codes[deny_status]) ?
Or is there a possibility that int deny_status could be overwritten
in http_process_req_common (with multiple deny/block/tarpit rules) ?

Both patches are against 1.7.1, but if this looks otherwise ok then
I can modify the patches against 1.8dev and add missing documentation.

I've used this minimal config for testing:
(missing defaults/global):
frontend test
        bind ipv4@127.0.0.1:8080
        http-request tarpit deny_status 429 if TRUE
        default_backend test_be

frontend test2
        bind ipv4@127.0.0.1:8081
        http-request tarpit if TRUE
        default_backend test_be

frontend test3
        bind ipv4@127.0.0.1:8082
        reqtarpit . if TRUE
        default_backend test_be

backend test_be
        server dummy 127.0.0.1:80 id 1

curl -v http://127.0.0.1:8080 should be tarpitted with status 429
and curl -v http://127.0.0.1:8081 (and :8082) with 500.

-Jarno

-- 
Jarno Huuskonen
>From 3f82309f1b96330ac75bc53a35b683cdc41ab61d Mon Sep 17 00:00:00 2001
From: Jarno Huuskonen <jarno.huusko...@uef.fi>
Date: Mon, 26 Dec 2016 13:47:30 +0200
Subject: [PATCH] parse_http_req_cond: parse deny_status code for http-request
 tarpit.
To: haproxy@formilux.org
X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4

---
 src/proto_http.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/proto_http.c b/src/proto_http.c
index aa8d997..dce1ac7 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -9007,11 +9007,15 @@ struct act_rule *parse_http_req_cond(const char **args, 
const char *file, int li
        if (!strcmp(args[0], "allow")) {
                rule->action = ACT_ACTION_ALLOW;
                cur_arg = 1;
-       } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block")) {
+       } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block") || 
!strcmp(args[0], "tarpit")) {
                int code;
                int hc;
 
                rule->action = ACT_ACTION_DENY;
+               if (!strcmp(args[0], "tarpit")) {
+                   rule->action = ACT_HTTP_REQ_TARPIT;
+                   rule->deny_status = HTTP_ERR_500;
+               }
                cur_arg = 1;
                 if (strcmp(args[cur_arg], "deny_status") == 0) {
                         cur_arg++;
@@ -9031,13 +9035,10 @@ struct act_rule *parse_http_req_cond(const char **args, 
const char *file, int li
                         }
 
                         if (hc >= HTTP_ERR_SIZE) {
-                                Warning("parsing [%s:%d] : status code %d not 
handled, using default code 403.\n",
-                                        file, linenum, code);
+                                Warning("parsing [%s:%d] : status code %d not 
handled, using default code %d.\n",
+                                        file, linenum, code, rule->action == 
ACT_ACTION_DENY ? 403: 500);
                         }
                 }
-       } else if (!strcmp(args[0], "tarpit")) {
-               rule->action = ACT_HTTP_REQ_TARPIT;
-               cur_arg = 1;
        } else if (!strcmp(args[0], "auth")) {
                rule->action = ACT_HTTP_REQ_AUTH;
                cur_arg = 1;
-- 
1.8.3.1

>From 25b5e0993e3be2af34b0c3c4957e7ddbae50b8f3 Mon Sep 17 00:00:00 2001
From: Jarno Huuskonen <jarno.huusko...@uef.fi>
Date: Sun, 1 Jan 2017 12:16:44 +0200
Subject: [PATCH] http_process_tarpit: add deny_status code/message.
To: haproxy@formilux.org
X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4

---
 src/proto_http.c | 48 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/src/proto_http.c b/src/proto_http.c
index dce1ac7..63277e3 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4347,8 +4347,10 @@ int http_process_req_common(struct stream *s, struct 
channel *req, int an_bit, s
                if (txn->flags & TX_CLDENY)
                        goto deny;
 
-               if (txn->flags & TX_CLTARPIT)
+               if (txn->flags & TX_CLTARPIT) {
+                       deny_status = HTTP_ERR_500;
                        goto tarpit;
+               }
        }
 
        /* add request headers from the rule sets in the same order */
@@ -4437,6 +4439,8 @@ int http_process_req_common(struct stream *s, struct 
channel *req, int an_bit, s
        if (s->be->cookie_name || sess->fe->capture_name)
                manage_client_side_cookies(s, req);
 
+       txn->status = http_err_codes[deny_status];
+
        req->analysers &= AN_FLT_END; /* remove switching rules etc... */
        req->analysers |= AN_REQ_HTTP_TARPIT;
        req->analyse_exp = tick_add_ifset(now_ms,  s->be->timeout.tarpit);
@@ -4843,6 +4847,7 @@ int http_process_request(struct stream *s, struct channel 
*req, int an_bit)
  */
 int http_process_tarpit(struct stream *s, struct channel *req, int an_bit)
 {
+       int http_err_code_idx;
        struct http_txn *txn = s->txn;
 
        /* This connection is being tarpitted. The CLIENT side has
@@ -4863,9 +4868,44 @@ int http_process_tarpit(struct stream *s, struct channel 
*req, int an_bit)
         */
        s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now);
 
-       txn->status = 500;
-       if (!(req->flags & CF_READ_ERROR))
-               http_reply_and_close(s, txn->status, http_error_message(s, 
HTTP_ERR_500));
+       if (!(req->flags & CF_READ_ERROR)) {
+               switch(txn->status) {
+               case 500:
+                       http_err_code_idx = HTTP_ERR_500;
+                       break;
+               case 403:
+                       http_err_code_idx = HTTP_ERR_403;
+                       break;
+               case 429:
+                       http_err_code_idx = HTTP_ERR_429;
+                       break;
+               case 504:
+                       http_err_code_idx = HTTP_ERR_504;
+                       break;
+               case 503:
+                       http_err_code_idx = HTTP_ERR_503;
+                       break;
+               case 502:
+                       http_err_code_idx = HTTP_ERR_502;
+                       break;
+               case 408:
+                       http_err_code_idx = HTTP_ERR_408;
+                       break;
+               case 405:
+                       http_err_code_idx = HTTP_ERR_405;
+                       break;
+               case 400:
+                       http_err_code_idx = HTTP_ERR_400;
+                       break;
+               case 200:
+                       http_err_code_idx = HTTP_ERR_200;
+                       break;
+               default:
+                       http_err_code_idx = HTTP_ERR_500;
+                       break;
+               }
+               http_reply_and_close(s, txn->status, http_error_message(s, 
http_err_code_idx));
+       }
 
        req->analysers &= AN_FLT_END;
        req->analyse_exp = TICK_ETERNITY;
-- 
1.8.3.1

Reply via email to