mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-17 02:56:51 +00:00
MEDIUM: http-rules: Support an optional error message in http deny rules
It is now possible to set the error message to use when a deny rule is executed. It may be a specific error file, adding "errorfile <file>" : http-request deny deny_status 400 errorfile /etc/haproxy/errorfiles/400badreq.http It may also be an error file from an http-errors section, adding "errorfiles <name>" : http-request deny errorfiles my-errors # use 403 error from "my-errors" section When defined, this error message is set in the HTTP transaction. The tarpit rule is also concerned by this change.
This commit is contained in:
parent
473e880a25
commit
554c0ebffd
@ -4412,12 +4412,17 @@ http-request del-map(<file-name>) <key fmt> [ { if | unless } <condition> ]
|
||||
It takes one argument: "file name" It is the equivalent of the "del map"
|
||||
command from the stats socket, but can be triggered by an HTTP request.
|
||||
|
||||
http-request deny [deny_status <status>] [ { if | unless } <condition> ]
|
||||
http-request deny [deny_status <status>] [ { errorfile | errorfiles } <err> ]
|
||||
[ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and immediately rejects the request
|
||||
and emits an HTTP 403 error, or optionally the status code specified as an
|
||||
argument to "deny_status". The list of permitted status codes is limited to
|
||||
those that can be overridden by the "errorfile" directive.
|
||||
those that can be overridden by the "errorfile" directive. A specific error
|
||||
message may be specified. It may be an error file, using the "errorfile"
|
||||
keyword followed by the file containing the full HTTP response. It may also
|
||||
be an error from an http-errors section, using the "errorfiles" keyword
|
||||
followed by the section name.
|
||||
No further "http-request" rules are evaluated.
|
||||
|
||||
http-request disable-l7-retry [ { if | unless } <condition> ]
|
||||
@ -4912,7 +4917,8 @@ http-request strict-mode { on | off }
|
||||
the frontend, the default mode is restored when HAProxy starts the backend
|
||||
rules evaluation.
|
||||
|
||||
http-request tarpit [deny_status <status>] [ { if | unless } <condition> ]
|
||||
http-request tarpit [deny_status <status>] [ { errorfile | errorfiles } <err> ]
|
||||
[ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and immediately blocks the request
|
||||
without responding for a delay specified by "timeout tarpit" or
|
||||
@ -4925,7 +4931,11 @@ http-request tarpit [deny_status <status>] [ { if | unless } <condition> ]
|
||||
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.
|
||||
firewall to support insane number of concurrent connections. A specific error
|
||||
message may be specified. It may be an error file, using the "errorfile"
|
||||
keyword followed by the file containing the full HTTP response. It may also
|
||||
be an error from an http-errors section, using the "errorfiles" keyword
|
||||
followed by the section name.
|
||||
See also the "silent-drop" action.
|
||||
|
||||
http-request track-sc0 <key> [table <table>] [ { if | unless } <condition> ]
|
||||
@ -5107,12 +5117,17 @@ http-response del-map(<file-name>) <key fmt> [ { if | unless } <condition> ]
|
||||
It takes one argument: "file name" It is the equivalent of the "del map"
|
||||
command from the stats socket, but can be triggered by an HTTP response.
|
||||
|
||||
http-response deny [deny_status <status>] [ { if | unless } <condition> ]
|
||||
http-response deny [deny_status <status>] [ { errorfile | errorfiles } <err> ]
|
||||
[ { if | unless } <condition> ]
|
||||
|
||||
This stops the evaluation of the rules and immediately rejects the response
|
||||
and emits an HTTP 502 error, or optionally the status code specified as an
|
||||
argument to "deny_status". The list of permitted status codes is limited to
|
||||
those that can be overridden by the "errorfile" directive.
|
||||
those that can be overridden by the "errorfile" directive. A specific error
|
||||
message may be specified. It may be an error file, using the "errorfile"
|
||||
keyword followed by the file containing the full HTTP response. It may also
|
||||
be an error from an http-errors section, using the "errorfiles" keyword
|
||||
followed by the section name.
|
||||
No further "http-response" rules are evaluated.
|
||||
|
||||
http-response redirect <rule> [ { if | unless } <condition> ]
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <types/http_htx.h>
|
||||
|
||||
extern struct buffer http_err_chunks[HTTP_ERR_SIZE];
|
||||
extern struct list http_errors_list;
|
||||
|
||||
struct htx_sl *http_get_stline(struct htx *htx);
|
||||
int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full);
|
||||
|
@ -123,6 +123,10 @@ struct act_rule {
|
||||
struct list fmt; /* log-format compatible expression */
|
||||
struct my_regex *re; /* used by replace-header/value/uri/path */
|
||||
} http; /* args used by some HTTP rules */
|
||||
struct {
|
||||
int status; /* status code */
|
||||
struct buffer *errmsg; /* HTTP error message, may be NULL */
|
||||
} http_deny; /* args used by HTTP deny rules */
|
||||
struct redirect_rule *redir; /* redirect rule or "http-request redirect" */
|
||||
struct {
|
||||
char *ref; /* MAP or ACL file name to update */
|
||||
|
@ -785,52 +785,118 @@ static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, str
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
||||
/* Check an "http-request deny" action when an http-errors section is referenced.
|
||||
*
|
||||
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
||||
* filled.
|
||||
*/
|
||||
static int check_http_deny_action(struct act_rule *rule, struct proxy *px, char **err)
|
||||
{
|
||||
struct http_errors *http_errs;
|
||||
int status = (intptr_t)(rule->arg.act.p[0]);
|
||||
int ret = 1;
|
||||
|
||||
list_for_each_entry(http_errs, &http_errors_list, list) {
|
||||
if (strcmp(http_errs->id, (char *)rule->arg.act.p[1]) == 0) {
|
||||
free(rule->arg.act.p[1]);
|
||||
rule->arg.http_deny.status = status;
|
||||
rule->arg.http_deny.errmsg = http_errs->errmsg[http_get_status_idx(status)];
|
||||
if (!rule->arg.http_deny.errmsg)
|
||||
ha_warning("Proxy '%s': status '%d' referenced by http deny rule "
|
||||
"not declared in http-errors section '%s'.\n",
|
||||
px->id, status, http_errs->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (&http_errs->list == &http_errors_list) {
|
||||
memprintf(err, "unknown http-errors section '%s' referenced by http deny rule",
|
||||
(char *)rule->arg.act.p[1]);
|
||||
free(rule->arg.act.p[1]);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse "deny" or "tarpit" actions for a request rule or "deny" action for a
|
||||
* response rule. It may take 2 optional arguments to define the status code. It
|
||||
* returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
|
||||
* response rule. It may take optional arguments to define the status code, the
|
||||
* error file or the http-errors section to use. It returns ACT_RET_PRS_OK on
|
||||
* success, ACT_RET_PRS_ERR on error.
|
||||
*/
|
||||
static enum act_parse_ret parse_http_deny(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
int code, hc, cur_arg;
|
||||
int default_status, status, hc, cur_arg;
|
||||
|
||||
|
||||
cur_arg = *orig_arg;
|
||||
if (rule->from == ACT_F_HTTP_REQ) {
|
||||
if (!strcmp(args[cur_arg-1], "tarpit")) {
|
||||
rule->action = ACT_HTTP_REQ_TARPIT;
|
||||
rule->arg.http.i = HTTP_ERR_500;
|
||||
default_status = status = 500;
|
||||
}
|
||||
else {
|
||||
rule->action = ACT_ACTION_DENY;
|
||||
rule->arg.http.i = HTTP_ERR_403;
|
||||
default_status = status = 403;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rule->action = ACT_ACTION_DENY;;
|
||||
rule->arg.http.i = HTTP_ERR_502;
|
||||
rule->action = ACT_ACTION_DENY;
|
||||
default_status = status = 502;
|
||||
}
|
||||
rule->flags |= ACT_FLAG_FINAL;
|
||||
|
||||
if (strcmp(args[cur_arg], "deny_status") == 0) {
|
||||
cur_arg++;
|
||||
if (!*args[cur_arg]) {
|
||||
memprintf(err, "missing status code.\n");
|
||||
memprintf(err, "'%s' expects <status_code> as argument", args[cur_arg-1]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
code = atol(args[cur_arg]);
|
||||
status = atol(args[cur_arg]);
|
||||
cur_arg++;
|
||||
for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
|
||||
if (http_err_codes[hc] == code) {
|
||||
rule->arg.http.i = hc;
|
||||
if (http_err_codes[hc] == status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hc >= HTTP_ERR_SIZE)
|
||||
memprintf(err, "status code %d not handled, using default code %d",
|
||||
code, http_err_codes[rule->arg.http.i]);
|
||||
if (hc >= HTTP_ERR_SIZE) {
|
||||
memprintf(err, "status code '%d' not handled, using default code '%d'",
|
||||
status, default_status);
|
||||
status = default_status;
|
||||
hc = http_get_status_idx(status);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(args[cur_arg], "errorfile") == 0) {
|
||||
cur_arg++;
|
||||
if (!*args[cur_arg]) {
|
||||
memprintf(err, "'%s' expects <file> as argument", args[cur_arg-1]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
rule->arg.http_deny.errmsg = http_load_errorfile(args[cur_arg], err);
|
||||
if (!rule->arg.http_deny.errmsg)
|
||||
return ACT_RET_PRS_ERR;
|
||||
cur_arg++;
|
||||
}
|
||||
else if (strcmp(args[cur_arg], "errorfiles") == 0) {
|
||||
cur_arg++;
|
||||
if (!*args[cur_arg]) {
|
||||
memprintf(err, "'%s' expects <http_errors_name> as argument", args[cur_arg-1]);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
/* Must be resolved during the config validity check */
|
||||
rule->arg.act.p[0] = (void *)((intptr_t)status);
|
||||
rule->arg.act.p[1] = strdup(args[cur_arg]);
|
||||
rule->check_ptr = check_http_deny_action;
|
||||
cur_arg++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rule->arg.http_deny.status = status;
|
||||
|
||||
out:
|
||||
*orig_arg = cur_arg;
|
||||
return ACT_RET_PRS_OK;
|
||||
}
|
||||
|
@ -2896,13 +2896,17 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis
|
||||
|
||||
case ACT_ACTION_DENY:
|
||||
txn->flags |= TX_CLDENY;
|
||||
txn->status = http_err_codes[rule->arg.http.i];
|
||||
txn->status = rule->arg.http_deny.status;
|
||||
if (rule->arg.http_deny.errmsg)
|
||||
txn->errmsg = rule->arg.http_deny.errmsg;
|
||||
rule_ret = HTTP_RULE_RES_DENY;
|
||||
goto end;
|
||||
|
||||
case ACT_HTTP_REQ_TARPIT:
|
||||
txn->flags |= TX_CLTARPIT;
|
||||
txn->status = http_err_codes[rule->arg.http.i];
|
||||
txn->status = rule->arg.http_deny.status;
|
||||
if (rule->arg.http_deny.errmsg)
|
||||
txn->errmsg = rule->arg.http_deny.errmsg;
|
||||
rule_ret = HTTP_RULE_RES_DENY;
|
||||
goto end;
|
||||
|
||||
@ -3073,7 +3077,9 @@ resume_execution:
|
||||
|
||||
case ACT_ACTION_DENY:
|
||||
txn->flags |= TX_CLDENY;
|
||||
txn->status = http_err_codes[rule->arg.http.i];
|
||||
txn->status = rule->arg.http_deny.status;
|
||||
if (rule->arg.http_deny.errmsg)
|
||||
txn->errmsg = rule->arg.http_deny.errmsg;
|
||||
rule_ret = HTTP_RULE_RES_DENY;
|
||||
goto end;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user