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> ]
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
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> ]
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> ]
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;
}
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;
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;