From: Willy Tarreau Date: Tue, 10 Nov 2026 10:41:32 +0000 (+0100) Subject: MINOR: http-fetch: add {fs,bs}.hdng.{found,fixed} to report hardening results X-Git-Tag: v3.5-dev12~3 X-Git-Url: https://git.kaiwu.me/%7B@url%7D?a=commitdiff_plain;h=0671d23c7f44856261545f5d753a06ca794636c1;p=haproxy.git MINOR: http-fetch: add {fs,bs}.hdng.{found,fixed} to report hardening results These new sample fetches return, for the frontend or backend mux stream, the mask of HTTP hardening conditions that were met (found) and of the rules that were enforced (fixed). They rely on the new mux stream controls MUX_SCTL_GET_HDNG_FOUND and MUX_SCTL_GET_HDNG_FIXED, and fail when the mux doesn't support them. The main purpose is to log them, e.g.: log-format "... cli_hdng=%[fs.hdng.found,htonl,hex] ..." Note that a request rejected before the stream is created cannot be reported this way since it is handled and logged by the mux alone. The doc also mentions that the bit numbers are subject to change. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 65a8afd5c..8cf9bae07 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -27164,10 +27164,14 @@ Summary of sample fetch methods in this section and their respective types: 51d.all([,*]) string bs.aborted boolean bs.debug_str([]) string +bs.hdng.fixed integer +bs.hdng.found integer bs.id integer bs.rst_code integer fs.aborted boolean fs.debug_str([]) string +fs.hdng.fixed integer +fs.hdng.found integer fs.id integer fs.rst_code integer ssl_bc boolean @@ -27318,7 +27322,33 @@ bs.debug_str([]) : string typical use case is to concatenate these information at the very end of a log-format, along with fs.debug_str(). Example: - log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>" + log-format "$HAPROXY_HTTP_LOG_FMT \ + fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>" + +bs.hdng.fixed : integer + Returns the mask of the HTTP hardening rules that were enforced on the + traffic received from the server for the current stream, i.e. the elements + that were rejected or deleted (see section 12.10). Each rule is designated + by one bit, whose position starting at 1 is reported by "haproxy -dKcfg" and + "show http-hardening". Note that bit numbers are subject to change between + major versions and that new options may occasionally be introduced in minor + versions. The sample fetch fails if the server side does not use an HTTP + multiplexer supporting HTTP hardening (H1, H2, H3). Example: + + log-format "$HAPROXY_HTTP_LOG_FMT srv_hdng=%[bs.hdng.found,htonl,hex]" + + See also "bs.hdng.found", "fs.hdng.fixed". + +bs.hdng.found : integer + Returns the mask of the HTTP hardening conditions that were met on the + traffic received from the server for the current stream, regardless of + whether the corresponding rules were enabled or not (see section 12.10). + This allows to figure what would be blocked or modified before enabling a + rule. The bits are the same as for "bs.hdng.fixed". Note that bit numbers + are subject to change between major versions and that new options may + occasionally be introduced in minor versions. The sample fetch fails if the + server side does not use an HTTP multiplexer supporting HTTP hardening (H1, + H2, H3). See also "bs.hdng.fixed", "fs.hdng.found". bs.id : integer Returns the multiplexer's stream ID on the server side. It is the @@ -27356,6 +27386,26 @@ fs.debug_str([]) : string log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>" +fs.hdng.fixed : integer + Returns the mask of the HTTP hardening rules that were enforced on the + traffic received from the client for the current stream. This is the same as + "bs.hdng.fixed" but for the client side. Note that a request rejected before + the stream is created (e.g. on its headers, or on trailers received at the + same time) is handled and logged by the multiplexer alone, so that it can + only be reported here when the rejection happens later (e.g. on trailers + received after the headers). Example: + + log-format "$HAPROXY_HTTP_LOG_FMT cli_hdng=%[fs.hdng.found,htonl,hex]" + + See also "fs.hdng.found", "bs.hdng.fixed". + +fs.hdng.found : integer + Returns the mask of the HTTP hardening conditions that were met on the + traffic received from the client for the current stream, regardless of + whether the corresponding rules were enabled or not. This is the same as + "bs.hdng.found" but for the client side. See also "fs.hdng.fixed", + "bs.hdng.found". + fs.id : integer Returns the multiplexer's stream ID on the client side. It is the multiplexer's responsibility to return the appropriate information. For diff --git a/src/http_fetch.c b/src/http_fetch.c index 1bd17110d..ff50504ae 100644 --- a/src/http_fetch.c +++ b/src/http_fetch.c @@ -2340,6 +2340,44 @@ int val_query(struct arg *args, char **err_msg) return 1; } +/* Returns the HTTP hardening conditions met ("found") or rules enforced + * ("fixed") on the frontend ("fs.") or backend ("bs.") mux stream, as a mask + * of HTTP_HRD_* flags. The sample fetch fails if the mux doesn't support HTTP + * hardening. + */ +static int smp_fetch_hdng(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + enum mux_sctl_type sctl; + struct connection *conn; + struct stconn *sc; + uint32_t mask = 0; + + if (!smp->strm) + return 0; + + /* kw is either "{f,b}s.hdng.found" or "{f,b}s.hdng.fixed" */ + sctl = (kw[9] == 'o') ? MUX_SCTL_GET_HDNG_FOUND : MUX_SCTL_GET_HDNG_FIXED; + sc = (kw[0] == 'f' ? smp->strm->scf : smp->strm->scb); + conn = sc_conn(sc); + + if (!conn) + return 0; + + /* a missing mux is necessarily on the backend, and may arrive later */ + if (!conn->mux) { + smp->flags |= SMP_F_MAY_CHANGE; + return 0; + } + + if (!conn->mux->sctl || conn->mux->sctl(sc, sctl, &mask) == -1) + return 0; + + smp->flags = SMP_F_VOL_TEST; + smp->data.type = SMP_T_SINT; + smp->data.u.sint = mask; + return 1; +} + /************************************************************************/ /* All supported sample fetch keywords must be declared here. */ /************************************************************************/ @@ -2351,6 +2389,9 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "base32+src", smp_fetch_base32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV | SMP_USE__MAY_ALIAS }, { "baseq", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV | SMP_USE__MAY_ALIAS }, + { "bs.hdng.fixed", smp_fetch_hdng, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV }, + { "bs.hdng.found", smp_fetch_hdng, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV }, + /* capture are allocated and are permanent in the stream */ { "capture.req.hdr", smp_fetch_capture_req_hdr, ARG1(1,SINT), val_cap_id, SMP_T_STR, SMP_USE_HRQHP }, @@ -2371,6 +2412,9 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV }, { "cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV }, + { "fs.hdng.fixed", smp_fetch_hdng, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, + { "fs.hdng.found", smp_fetch_hdng, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, + /* hdr is valid in both directions (eg: for "stick ...") but hdr_* are * only here to match the ACL's name, are request-only and are used for * ACL compatibility only.