]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: http: add a global setting to decide on warnings for http rules
authorWilly Tarreau <w@1wt.eu>
Thu, 12 Nov 2026 10:33:23 +0000 (11:33 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 12 Nov 2026 17:12:23 +0000 (18:12 +0100)
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.

doc/configuration.txt
src/http_rules.c

index 9eafe88fccbdf2552899844ad3384f6ad533340c..65a8afd5c5cd9ce5870a209cdb8bac246880bd58 100644 (file)
@@ -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 <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 <pidfile>
   Writes PIDs of all daemons into file <pidfile> when daemon mode or writes PID
   of master process into file <pidfile> when master-worker mode. This option is
@@ -16283,6 +16298,8 @@ auth [realm <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 } <code> ] [ content-type <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 <name> <match-regex> <replace-fmt>
@@ -17804,6 +17825,8 @@ silent-drop [ rst-ttl <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 }
index e75bf34841308ea3ff39cd91e6c1a419843d8b50..da8fe145d060467fe90f226fd38007c5b899e15c 100644 (file)
@@ -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 <accepted>')."
-                                         " 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 <accepted>')."
+                                           " 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