BUG/MEDIUM: log: fix lf_expr_postcheck() behavior with default section

Since 7a21c3a4ef ("MAJOR: log: implement proper postparsing for logformat
expressions"), logformat expressions stored in a default section are not
postchecked anymore. This is because the REGISTER_POST_PROXY_CHECK() only
evaluates regular proxies. Because of this, proxy options which are
automatically enabled on the proxy depending on the logformat expression
features in use are not set on the default proxy, which means such options
are not passed to the regular proxies that inherit from it (proxies that
and will actually be running the logformat expression during runtime).

Because of that, a logformat expression stored inside a default section
and executed by a regular proxy may not behave properly. Also, since
03ca16f38b ("OPTIM: log: resolve logformat options during postparsing"),
it's even worse because logformat node options postresoving is also
skipped, which may also alter logformat expression encoding feature.

To fix the issue, let's add a special case for default proxies in
parse_logformat_string() and lf_expr_postcheck() so that default proxies
are postchecked on the fly during parsing time in a "relaxed" way as we
cannot assume that the features involved in the logformat expression won't
be compatible with the proxy actually running it since we may have
different types of proxies inheriting from the same default section.

This bug was discovered while trying to address GH #2597.

It should be backported to 3.0 with 7a21c3a4ef and 03ca16f38b.

(cherry picked from commit e4f122f3f4)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
This commit is contained in:
Aurelien DARRAGON 2024-06-11 10:15:36 +02:00 committed by Christopher Faulet
parent 310c0dd14e
commit d1a7330086

View File

@ -870,15 +870,17 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy,
if (!ret) if (!ret)
goto fail; goto fail;
if (!(curproxy->flags & PR_FL_CHECKED)) { if (!(curproxy->cap & PR_CAP_DEF) &&
!(curproxy->flags & PR_FL_CHECKED)) {
/* add the lf_expr to the proxy checks to delay postparsing /* add the lf_expr to the proxy checks to delay postparsing
* since config-related proxy properties are not stable yet * since config-related proxy properties are not stable yet
*/ */
LIST_APPEND(&curproxy->conf.lf_checks, &lf_expr->list); LIST_APPEND(&curproxy->conf.lf_checks, &lf_expr->list);
} }
else { else {
/* probably called during runtime or with proxy already checked, /* default proxy, or regular proxy and probably called during
* perform the postcheck right away * runtime or with proxy already checked, perform the postcheck
* right away
*/ */
if (!lf_expr_postcheck(lf_expr, curproxy, err)) if (!lf_expr_postcheck(lf_expr, curproxy, err))
goto fail; goto fail;
@ -948,11 +950,17 @@ static int lf_expr_postcheck_node_opt(struct lf_expr *lf_expr, struct logformat_
* compatible with logformat expression, but once the proxy is checked, we fail * compatible with logformat expression, but once the proxy is checked, we fail
* as soon as we face incompatibilities) * as soon as we face incompatibilities)
* *
* If the proxy is a default section, then allow the postcheck to succeed:
* the logformat expression may or may not work properly depending on the
* actual proxy that effectively runs it during runtime, but we have to stay
* permissive since we cannot assume it won't work.
*
* It returns 1 on success and 0 on error, <err> will be set in case of error. * It returns 1 on success and 0 on error, <err> will be set in case of error.
*/ */
int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err) int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err)
{ {
struct logformat_node *lf; struct logformat_node *lf;
int default_px = (px->cap & PR_CAP_DEF);
if (!(px->flags & PR_FL_CHECKED)) if (!(px->flags & PR_FL_CHECKED))
px->to_log |= LW_INIT; px->to_log |= LW_INIT;
@ -987,7 +995,8 @@ int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err)
px->to_log |= LW_REQ; px->to_log |= LW_REQ;
} }
else if (lf->type == LOG_FMT_ALIAS) { else if (lf->type == LOG_FMT_ALIAS) {
if (lf->alias->mode == PR_MODE_HTTP && px->mode != PR_MODE_HTTP) { if (lf->alias->mode == PR_MODE_HTTP &&
!default_px && px->mode != PR_MODE_HTTP) {
memprintf(err, "format alias '%s' is reserved for HTTP mode", memprintf(err, "format alias '%s' is reserved for HTTP mode",
lf->alias->name); lf->alias->name);
goto fail; goto fail;
@ -1006,7 +1015,7 @@ int lf_expr_postcheck(struct lf_expr *lf_expr, struct proxy *px, char **err)
if (!lf_expr_postcheck_node_opt(lf_expr, lf, err)) if (!lf_expr_postcheck_node_opt(lf_expr, lf, err))
goto fail; goto fail;
} }
if ((px->to_log & (LW_REQ | LW_RESP)) && if (!default_px && (px->to_log & (LW_REQ | LW_RESP)) &&
(px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG))) { (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG))) {
memprintf(err, "logformat expression not usable here (at least one node depends on HTTP mode)"); memprintf(err, "logformat expression not usable here (at least one node depends on HTTP mode)");
goto fail; goto fail;