]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: proxy: Add options to drop HTTP trailers during message forwarding
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 15 Apr 2025 13:36:19 +0000 (15:36 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 22 Apr 2025 14:14:46 +0000 (16:14 +0200)
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.

doc/configuration.txt
include/haproxy/proxy-t.h
src/h1.c
src/h3.c
src/mux_h1.c
src/mux_h2.c
src/proxy.c

index ae9ca5c6deb68f716a386544fdc761afb8ef3a0b..c8ec013b751ef0ca06c98359a747e42353398e06 100644 (file)
@@ -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
index 6e6b8fe62b555f4d5e7a943314df39f11c1b8175..0ea2fd95b3aa93827c4f8d959be3445929cea47c 100644 (file)
@@ -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 */
index faf18ff139afed4f39ac70937ef5fc0e964d97b1..2a4f6ccf713e11b60c9f20016bf4b8c7f58d4177 100644 (file)
--- 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:
index 69528147b82a9a745ea4fc818802892a82fe2f81..0f15a89356f0d8b3bcab31f454418559eaa541e7 100644 (file)
--- 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.
index 490391c4edfd28bb0b39f8c168281fe51b5a28fe..17dd746522cc33ee8fac651ab507732c7297c3eb 100644 (file)
@@ -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);
 
index dbda2d34b8c85968f4b8208caed95d48ac524555..e739c8aee9f2bb926550195e65532533cfac2617 100644 (file)
@@ -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("");
 
index 8d87bf85cd5686536a0796dc79ab703f8697c6af..d89f326c2ee21f6b5f20c26cd727561e3450c8b2 100644 (file)
@@ -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 },