From: Willy Tarreau Date: Thu, 12 Nov 2026 10:33:23 +0000 (+0100) Subject: MINOR: http: add a global setting to decide on warnings for http rules X-Git-Tag: v3.5-dev12~7 X-Git-Url: https://git.kaiwu.me/%7B@url%7D?a=commitdiff_plain;h=09e3f130578476760034e48d3d45c8b72b6776fb;p=haproxy.git MINOR: http: add a global setting to decide on warnings for http rules The default choice of "notice" as level to emit a warning for an http-request rule relying on unreliable processing may not always be the best choice. This commit adds a new global "on-unreliable-rules" directives which takes 3 parameters, "notice" (the default), "warn" and "ignore". Choosing "warn" will emit a warning instead of a notice, so that a config using "zero-warning" will detect them. This might be helpful after a config was sanitized in order to make sure no new warnings are introduced. And "ignore" may be used to silence all messages when migrating a config. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 9eafe88fc..65a8afd5c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1868,6 +1868,7 @@ The following keywords are supported in the "global" section : - ocsp-update.mindelay - ocsp-update.httpproxy - ocsp-update.mode + - on-unreliable-rules - pidfile - pp2-never-send-local - presetenv @@ -3401,6 +3402,20 @@ ocsp-update.timeout of this document. It defaults to 10s. See option "ocsp-update" for more information about the auto update mechanism. +on-unreliable-rules { notice | warn | ignore } + Sets how to report the "http-request" access control rules which are easy to + bypass because their condition relies on a match on the path, the URL, the + query string or the parameters they convey (e.g. "deny if { path_beg /x }"). + With "notice", which is the default, a notice message will be emitted for + each such rule. With "warn", a warning is emitted for each of them, which + when combined with "zero-warning", may allow to detect them before deploying + the configuration. Finally, "ignore" may be used to silence all of them, for + example to reduce the noise when migrating from an old configuration. The + default could change to "warn" in a future version. Rules marked with the + "no-warn" keyword are assumed to be well understood and are never reported. + See section 7.5 "Secure filtering of HTTP requests" for more information, + and how to write reliable rules. + pidfile Writes PIDs of all daemons into file when daemon mode or writes PID of master process into file when master-worker mode. This option is @@ -16283,6 +16298,8 @@ auth [realm ] [no-warn] If this rule is used in an http-request statement conditionned by an ACL involving inputs that may be subject to aliasing, a warning will be emitted unless the "no-warn" option is placed before the "if" or "unless" keyword. + The warning may be globally adjusted using the global "on-unreliable-rules" + directive. Example: acl auth_ok http_auth_group(L1) G1 @@ -16434,6 +16451,8 @@ deny [ { status | deny_status } ] [ content-type ] If this rule is used in an http-request statement conditionned by an ACL involving inputs that may be subject to aliasing, a warning will be emitted unless the "no-warn" option is placed before the "if" or "unless" keyword. + The warning may be globally adjusted using the global "on-unreliable-rules" + directive. dgram-drop @@ -16898,6 +16917,8 @@ reject [no-warn] If this rule is used in an http-request statement conditionned by an ACL involving inputs that may be subject to aliasing, a warning will be emitted unless the "no-warn" option is placed before the "if" or "unless" keyword. + The warning may be globally adjusted using the global "on-unreliable-rules" + directive. replace-header @@ -17804,6 +17825,8 @@ silent-drop [ rst-ttl ] [no-warn] If this rule is used in an http-request statement conditionned by an ACL involving inputs that may be subject to aliasing, a warning will be emitted unless the "no-warn" option is placed before the "if" or "unless" keyword. + The warning may be globally adjusted using the global "on-unreliable-rules" + directive. strict-mode { on | off } @@ -29893,9 +29916,10 @@ query string and the parameters they convey, as returned by the "base", of any converter applied to them. Only a term which makes the condition true when it matches is considered, i.e. a non-negated term after "if", or a negated one after "unless". This means that the allow-lists above are not reported. A -notice message will indicate affected rules. When such a rule is only meant as -an extra filter, it may be excluded from this report by placing the "no-warn" -keyword right before "if" or "unless". It also serves as a reminder for whoever +notice or warning message will indicate affected rules, depending on the +"on-unreliable-rules" setting. When such a rule is only meant as an extra +filter, it may be excluded from this report by placing the "no-warn" keyword +right before "if" or "unless". It also serves as a reminder for whoever modifies the rule later that it is not reliable by itself : http-request deny no-warn if { path_sub -i %2e %2f } diff --git a/src/http_rules.c b/src/http_rules.c index e75bf3484..da8fe145d 100644 --- a/src/http_rules.c +++ b/src/http_rules.c @@ -50,6 +50,14 @@ struct action_kw_list http_after_res_keywords = { .list = LIST_HEAD_INIT(http_after_res_keywords.list) }; +/* How to report unreliable access control rules (see "on-unreliable-rules") */ +enum { + UNRELIABLE_RULES_NOTICE = 0, /* one notice per rule (default) */ + UNRELIABLE_RULES_WARN, /* one warning per rule */ + UNRELIABLE_RULES_IGNORE, /* say nothing (not recommended) */ +}; +static int on_unreliable_rules = UNRELIABLE_RULES_NOTICE; + void http_req_keywords_register(struct action_kw_list *kw_list) { act_add_list(&http_req_keywords.list, kw_list); @@ -175,20 +183,23 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li } rule->cond = cond; - if ((custom->flags & KWF_ACCESS_CTRL) && !no_warn) { + if (on_unreliable_rules != UNRELIABLE_RULES_IGNORE && + (custom->flags & KWF_ACCESS_CTRL) && !no_warn) { const struct acl *acl; const char *kw; if (acl_cond_find_positive_use(cond, SMP_USE__MAY_ALIAS, &acl, &kw)) { - ha_notice("parsing [%s:%d] : 'http-request %s' applies when '%s'%s%s%s matches a pattern," - " which may be bypassed since HTTP allows many equivalent forms of a same request" - " (case, %%-encoding, '//', '/./', '/../', '\\', etc). Only rejecting what was not" - " explicitly accepted is reliable (e.g. 'http-request %s unless ')." - " If this rule is only an extra filter, add 'no-warn' before '%s' to silence this" - " warning. See 'Secure filtering of HTTP requests' in the configuration manual.\n", - file, linenum, args[0], kw, - *acl->name ? " (from ACL '" : "", acl->name, *acl->name ? "')" : "", - args[0], args[cur_arg]); + ((on_unreliable_rules == UNRELIABLE_RULES_WARN) ? + ha_warning : + ha_notice)("parsing [%s:%d] : 'http-request %s' applies when '%s'%s%s%s matches a pattern," + " which may be bypassed since HTTP allows many equivalent forms of a same request" + " (case, %%-encoding, '//', '/./', '/../', '\\', etc). Only rejecting what was not" + " explicitly accepted is reliable (e.g. 'http-request %s unless ')." + " If this rule is only an extra filter, add 'no-warn' before '%s' to silence this" + " warning. See 'Secure filtering of HTTP requests' in the configuration manual.\n", + file, linenum, args[0], kw, + *acl->name ? " (from ACL '" : "", acl->name, *acl->name ? "')" : "", + args[0], args[cur_arg]); } } } @@ -584,6 +595,34 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st return NULL; } +/* parse the global "on-unreliable-rules" keyword */ +static int cfg_parse_on_unreliable_rules(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (too_many_args(1, args, err, NULL)) + return -1; + + if (strcmp(args[1], "warn") == 0) + on_unreliable_rules = UNRELIABLE_RULES_WARN; + else if (strcmp(args[1], "notice") == 0) + on_unreliable_rules = UNRELIABLE_RULES_NOTICE; + else if (strcmp(args[1], "ignore") == 0) + on_unreliable_rules = UNRELIABLE_RULES_IGNORE; + else { + memprintf(err, "'%s' expects either 'warn', 'notice' or 'ignore' as argument.", args[0]); + return -1; + } + return 0; +} + +static struct cfg_kw_list cfg_kws = {ILH, { + { CFG_GLOBAL, "on-unreliable-rules", cfg_parse_on_unreliable_rules }, + { 0, NULL, NULL }, +}}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); + /* * Local variables: * c-indent-level: 8