From: Christopher Faulet Date: Tue, 15 Apr 2025 13:36:19 +0000 (+0200) Subject: MINOR: proxy: Add options to drop HTTP trailers during message forwarding X-Git-Tag: v3.2-dev12~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=52002036777c3ce189146df93d38597ee20d5b42;p=thirdparty%2Fhaproxy.git MINOR: proxy: Add options to drop HTTP trailers during message forwarding In RFC9110, it is stated that trailers could be merged with the headers. While it should be performed with a speicial care, it may be a problem for some applications. To avoid any trouble with such applications, two new options were added to drop trailers during the message forwarding. On the backend, "http-drop-request-trailers" option can be enabled to drop trailers from the requests before sending them to the server. And on the frontend, "http-drop-response-trailers" option can be enabled to drop trailers from the responses before sending them to the client. The options can be defined in defaults sections and disabled with "no" keyword. This patch should fix the issue #2930. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index ae9ca5c6d..c8ec013b7 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -6206,6 +6206,8 @@ option forwarded (*) X - X X option h1-case-adjust-bogus-client (*) X X X - option h1-case-adjust-bogus-server (*) X - X X option http-buffer-request (*) X X X X +option http-drop-request-trailers (*) X - - X +option http-drop-response-trailers (*) X - X - option http-ignore-probes (*) X X X - option http-keep-alive (*) X X X X option http-no-delay (*) X X X X @@ -10364,6 +10366,50 @@ no option http-buffer-request See also : "option http-no-delay", "timeout http-request", "http-request wait-for-body" +option http-drop-request-trailers +no option http-drop-request-trailers + Drop the HTTP trailers from the request when sent to the server + + May be used in the following contexts: http + + May be used in sections : defaults | frontend | listen | backend + yes | no | no | yes + + Arguments : none + + When this option is enabled, any HTTP trailers found in a request will be + dropped before sending it to the server. + + RFC9110#section-6.5.1 stated that trailer fields could be merged into the + header fields. It should be done on purpose, but it may be a problem for some + applications, espcially if malicious clients hide sensitive header fields in + the trailers part and some intermediaries merge them with headers with no + specific checks. In that case, this option can be enabled on the backend to + drop any trailer fields found in requests before sending them to the server. + + If this option has been enabled in a "defaults" section, it can be disabled + in a specific instance by prepending the "no" keyword before it. + + See also: "option http-drop-response-trailers" + +option http-drop-response-trailers +no option http-drop-response-trailers + Drop the HTTP trailers from the response when sent to the client + + May be used in the following contexts: http + + May be used in sections : defaults | frontend | listen | backend + yes | yes | yes | no + + Arguments : none + + This option is similar to "option http-drop-request-trailers" but it must be + used to drop trailer fields from responses before sending them to clients. + + If this option has been enabled in a "defaults" section, it can be disabled + in a specific instance by prepending the "no" keyword before it. + + See also: "option http-drop-request-trailers" option http-ignore-probes no option http-ignore-probes diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 6e6b8fe62..0ea2fd95b 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -114,8 +114,9 @@ enum PR_SRV_STATE_FILE { #define PR_O_HTTP_CLO 0x01000000 /* HTTP close mode (httpclose) */ #define PR_O_HTTP_SCL 0x02000000 /* HTTP server close mode (http-server-close) */ #define PR_O_HTTP_MODE 0x03000000 /* MASK to retrieve the HTTP mode */ -/* unused: 0x04000000 */ -/* unused: 0x08000000 */ + +#define PR_O_HTTP_DROP_REQ_TRLS 0x04000000 /* Drop the request trailers when forwarding to the server */ +#define PR_O_HTTP_DROP_RES_TRLS 0x08000000 /* Drop response trailers when forwarding to the client */ #define PR_O_TCPCHK_SSL 0x10000000 /* at least one TCPCHECK connect rule requires SSL */ #define PR_O_CONTSTATS 0x20000000 /* continuous counters */ diff --git a/src/h1.c b/src/h1.c index faf18ff13..2a4f6ccf7 100644 --- a/src/h1.c +++ b/src/h1.c @@ -1242,7 +1242,6 @@ int h1_headers_to_hdr_list(char *start, const char *stop, goto http_msg_invalid; } } - break; default: diff --git a/src/h3.c b/src/h3.c index 69528147b..0f15a8935 100644 --- a/src/h3.c +++ b/src/h3.c @@ -1818,6 +1818,11 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx) TRACE_ENTER(H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs); hdr = 0; + + /* Skip the trailers because the corresponding conf option was set */ + if (qcs->qcc->proxy->options & PR_O_HTTP_DROP_RES_TRLS) + goto skip_trailers; + for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) { type = htx_get_blk_type(blk); @@ -1842,6 +1847,7 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx) } } + skip_trailers: if (!hdr) { /* No headers encoded here so no need to generate a H3 HEADERS * frame. Mux will send an empty QUIC STREAM frame with FIN. diff --git a/src/mux_h1.c b/src/mux_h1.c index 490391c4e..17dd74652 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -3371,6 +3371,11 @@ static size_t h1_make_trailers(struct h1s *h1s, struct h1m *h1m, struct htx *htx ((h1m->flags & H1_MF_RESP) && (h1s->flags & H1S_F_BODYLESS_RESP))) goto nextblk; + /* Skip the trailers because the corresponding conf option was set */ + if ((!(h1m->flags & H1_MF_RESP) && (h1c->px->options & PR_O_HTTP_DROP_RES_TRLS)) || + ((h1m->flags & H1_MF_RESP) && (h1c->px->options & PR_O_HTTP_DROP_REQ_TRLS))) + goto nextblk; + n = htx_get_blk_name(htx, blk); v = htx_get_blk_value(htx, blk); diff --git a/src/mux_h2.c b/src/mux_h2.c index dbda2d34b..e739c8aee 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -7410,6 +7410,12 @@ static size_t h2s_make_trailers(struct h2s *h2s, struct htx *htx) /* get trailers. */ hdr = 0; + + /* Skip the trailers because the corresponding conf option was set */ + if ((!(h2c->flags & H2_CF_IS_BACK) && (h2c->proxy->options & PR_O_HTTP_DROP_RES_TRLS)) || + ((h2c->flags & H2_CF_IS_BACK) && (h2c->proxy->options & PR_O_HTTP_DROP_REQ_TRLS))) + goto skip_trailers; + for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) { type = htx_get_blk_type(blk); @@ -7434,6 +7440,7 @@ static size_t h2s_make_trailers(struct h2s *h2s, struct htx *htx) } } + skip_trailers: /* marker for end of trailers */ list[hdr].n = ist(""); diff --git a/src/proxy.c b/src/proxy.c index 8d87bf85c..d89f326c2 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -87,6 +87,8 @@ const struct cfg_opt cfg_opts[] = { "contstats", PR_O_CONTSTATS, PR_CAP_FE, 0, 0 }, { "dontlognull", PR_O_NULLNOLOG, PR_CAP_FE, 0, 0 }, { "http-buffer-request", PR_O_WREQ_BODY, PR_CAP_FE | PR_CAP_BE, 0, PR_MODE_HTTP }, + { "http-drop-request-trailers", PR_O_HTTP_DROP_REQ_TRLS, PR_CAP_BE, 0, PR_MODE_HTTP }, + { "http-drop-response-trailers", PR_O_HTTP_DROP_RES_TRLS, PR_CAP_FE, 0, PR_MODE_HTTP }, { "http-ignore-probes", PR_O_IGNORE_PRB, PR_CAP_FE, 0, PR_MODE_HTTP }, { "idle-close-on-response", PR_O_IDLE_CLOSE_RESP, PR_CAP_FE, 0, PR_MODE_HTTP }, { "prefer-last-server", PR_O_PREF_LAST, PR_CAP_BE, 0, PR_MODE_HTTP },