]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: proxy_protocol: Support sending unique IDs using PPv2
authorTim Duesterhus <tim@bastelstu.be>
Fri, 13 Mar 2020 11:34:24 +0000 (12:34 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 13 Mar 2020 16:26:43 +0000 (17:26 +0100)
This patch adds the `unique-id` option to `proxy-v2-options`. If this
option is set a unique ID will be generated based on the `unique-id-format`
while sending the proxy protocol v2 header and stored as the unique id for
the first stream of the connection.

This feature is meant to be used in `tcp` mode. It works on HTTP mode, but
might result in inconsistent unique IDs for the first request on a keep-alive
connection, because the unique ID for the first stream is generated earlier
than the others.

Now that we can send unique IDs in `tcp` mode the `%ID` log variable is made
available in TCP mode.

doc/configuration.txt
include/proto/connection.h
include/types/server.h
reg-tests/connection/proxy_protocol_send_unique_id.vtc [new file with mode: 0644]
src/connection.c
src/log.c
src/server.c
src/stream_interface.c

index 57f777a78a46c5c6ad47627ce2319488a1a15024..cc5fbf2d06eb95db143f2c8407361c2889d012e6 100644 (file)
@@ -12657,13 +12657,23 @@ send-proxy-v2
   this section and send-proxy" option of the "bind" keyword.
 
 proxy-v2-options <option>[,<option>]*
-  The "proxy-v2-options" parameter add option to send in PROXY protocol version
-  2 when "send-proxy-v2" is used. Options available are "ssl" (see also
-  send-proxy-v2-ssl), "cert-cn" (see also "send-proxy-v2-ssl-cn"), "ssl-cipher":
-  name of the used cipher, "cert-sig": signature algorithm of the used
-  certificate, "cert-key": key algorithm of the used certificate), "authority":
-  host name value passed by the client (only sni from a tls connection is
-  supported), "crc32c": checksum of the proxy protocol v2 header.
+  The "proxy-v2-options" parameter add options to send in PROXY protocol
+  version 2 when "send-proxy-v2" is used. Options available are:
+
+  - ssl       : See also "send-proxy-v2-ssl".
+  - cert-cn   : See also "send-proxy-v2-ssl-cn".
+  - ssl-cipher: Name of the used cipher.
+  - cert-sig  : Signature algorithm of the used certificate.
+  - cert-key  : Key algorithm of the used certificate
+  - authority : Host name value passed by the client (only SNI from a TLS
+                connection is supported).
+  - crc32c    : Checksum of the PROXYv2 header.
+  - unique-id : Send a unique ID generated using the frontend's
+                "unique-id-format" within the PROXYv2 header.
+                This unique-id is primarily meant for "mode tcp". It can
+                lead to unexpected results in "mode http", because the
+                generated unique ID is also used for the first HTTP request
+                within a Keep-Alive connection.
 
 send-proxy-v2-ssl
   The "send-proxy-v2-ssl" parameter enforces use of the PROXY protocol version
index 9b8eb8ad3deb46e9d2b2abe7d83d698b07fcbb69..ecc03de8a1b5f499626bd246fb7ebf869f43e307 100644 (file)
@@ -47,9 +47,9 @@ int conn_fd_check(struct connection *conn);
 
 /* receive a PROXY protocol header over a connection */
 int conn_recv_proxy(struct connection *conn, int flag);
-int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote);
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm);
 int make_proxy_line_v1(char *buf, int buf_len, struct sockaddr_storage *src, struct sockaddr_storage *dst);
-int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote);
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm);
 
 int conn_subscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es);
 int conn_unsubscribe(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es);
index 099b1d8a87a0764320448c2fe31ecfad5d1e0509..0257f580fcf18ef5493044a35ceae6159ba99c8c 100644 (file)
@@ -154,6 +154,7 @@ enum srv_initaddr {
 #define SRV_PP_V2_SSL_CIPHER    0x0040   /* proxy protocol version 2 with cipher used */
 #define SRV_PP_V2_AUTHORITY     0x0080   /* proxy protocol version 2 with authority */
 #define SRV_PP_V2_CRC32C        0x0100   /* proxy protocol version 2 with crc32c */
+#define SRV_PP_V2_UNIQUE_ID     0x0200   /* proxy protocol version 2 with unique ID */
 
 /* function which act on servers need to return various errors */
 #define SRV_STATUS_OK       0   /* everything is OK. */
diff --git a/reg-tests/connection/proxy_protocol_send_unique_id.vtc b/reg-tests/connection/proxy_protocol_send_unique_id.vtc
new file mode 100644 (file)
index 0000000..c1f0fc4
--- /dev/null
@@ -0,0 +1,42 @@
+varnishtest "Check that the unique ID TLV is properly sent"
+
+#REQUIRE_VERSION=2.2
+
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        log global
+        unique-id-format %{+X}o\ TEST-%[req.hdr(in)]
+
+    listen sender
+        bind "fd@${feS}"
+
+        unique-id-header unique_id
+
+        server example ${h1_feR_addr}:${h1_feR_port} send-proxy-v2 proxy-v2-options unique-id
+
+    listen receiver
+        bind "fd@${feR}" accept-proxy
+
+        http-request set-var(txn.http_unique_id) req.hdr(unique_id)
+        http-request set-var(txn.proxy_unique_id) fc_pp_unique_id
+        http-after-response set-header http_unique_id %[var(txn.http_unique_id)]
+        http-after-response set-header proxy_unique_id %[var(txn.proxy_unique_id)]
+        http-request return status 200
+} -start
+
+# Validate that a correct header passes
+client c1 -connect ${h1_feS_sock} {
+    txreq -url "/" \
+        -hdr "in: foo"
+    rxresp
+    expect resp.http.http_unique_id == "TEST-foo"
+    expect resp.http.proxy_unique_id == "TEST-foo"
+    txreq -url "/" \
+        -hdr "in: bar"
+    rxresp
+    expect resp.http.http_unique_id == "TEST-bar"
+    expect resp.http.proxy_unique_id == "TEST-foo"
+} -run
index 728c0ec397cdd4169e5e40891dec223dec9468f8..4e48aa94590458d3bb901fecd8cb390dd8999372 100644 (file)
@@ -1241,12 +1241,12 @@ int conn_recv_socks4_proxy_response(struct connection *conn)
 }
 
 /* Note: <remote> is explicitly allowed to be NULL */
-int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote)
+int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm)
 {
        int ret = 0;
 
        if (srv && (srv->pp_opts & SRV_PP_V2)) {
-               ret = make_proxy_line_v2(buf, buf_len, srv, remote);
+               ret = make_proxy_line_v2(buf, buf_len, srv, remote, strm);
        }
        else {
                if (remote && conn_get_src(remote) && conn_get_dst(remote))
@@ -1353,7 +1353,7 @@ static int make_tlv(char *dest, int dest_len, char type, uint16_t length, const
 }
 
 /* Note: <remote> is explicitly allowed to be NULL */
-int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote)
+int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connection *remote, struct stream *strm)
 {
        const char pp2_signature[] = PP2_SIGNATURE;
        void *tlv_crc32c_p = NULL;
@@ -1468,6 +1468,20 @@ int make_proxy_line_v2(char *buf, int buf_len, struct server *srv, struct connec
                }
        }
 
+       if (srv->pp_opts & SRV_PP_V2_UNIQUE_ID) {
+               struct session* sess = strm_sess(strm);
+               struct ist unique_id = stream_generate_unique_id(strm, &sess->fe->format_unique_id);
+
+               value = unique_id.ptr;
+               value_len = unique_id.len;
+
+               if (value_len >= 0) {
+                       if ((buf_len - ret) < sizeof(struct tlv))
+                               return 0;
+                       ret += make_tlv(&buf[ret], (buf_len - ret), PP2_TYPE_UNIQUE_ID, value_len, value);
+               }
+       }
+
 #ifdef USE_OPENSSL
        if (srv->pp_opts & SRV_PP_V2_SSL) {
                struct tlv_ssl *tlv;
index 8f502ac7eafba72492286170fac27cc5f3709329..2cac07486e37c181fbe731c4102d67700b96d45e 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -137,7 +137,7 @@ static const struct logformat_type logformat_keywords[] = {
        { "CC", LOG_FMT_CCLIENT, PR_MODE_HTTP, LW_REQHDR, NULL },  /* client cookie */
        { "CS", LOG_FMT_CSERVER, PR_MODE_HTTP, LW_RSPHDR, NULL },  /* server cookie */
        { "H", LOG_FMT_HOSTNAME, PR_MODE_TCP, LW_INIT, NULL }, /* Hostname */
-       { "ID", LOG_FMT_UNIQUEID, PR_MODE_HTTP, LW_BYTES, NULL }, /* Unique ID */
+       { "ID", LOG_FMT_UNIQUEID, PR_MODE_TCP, LW_BYTES, NULL }, /* Unique ID */
        { "ST", LOG_FMT_STATUS, PR_MODE_TCP, LW_RESP, NULL },   /* status code */
        { "T", LOG_FMT_DATEGMT, PR_MODE_TCP, LW_INIT, NULL },   /* date GMT */
        { "Ta", LOG_FMT_Ta, PR_MODE_HTTP, LW_BYTES, NULL },      /* Time active (tr to end) */
index 9655702227a6cb27123592fdd7e97cd1b457b2ed..908439d0069bcde69eab49f967e841b09adbf6ab 100644 (file)
@@ -656,6 +656,8 @@ static int srv_parse_proxy_v2_options(char **args, int *cur_arg,
                        newsrv->pp_opts |= SRV_PP_V2_AUTHORITY;
                } else if (!strcmp(p, "crc32c")) {
                        newsrv->pp_opts |= SRV_PP_V2_CRC32C;
+               } else if (!strcmp(p, "unique-id")) {
+                       newsrv->pp_opts |= SRV_PP_V2_UNIQUE_ID;
                } else
                        goto fail;
        }
index 47cbd9693acab54f2f34ca1a82c3953628819eae..7bb864d529c2171c28ab59dff71ed80791d322a1 100644 (file)
@@ -354,9 +354,12 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
                if (cs && cs->data_cb == &si_conn_cb) {
                        struct stream_interface *si = cs->data;
                        struct conn_stream *remote_cs = objt_cs(si_opposite(si)->end);
+                       struct stream *strm = si_strm(si);
+
                        ret = make_proxy_line(trash.area, trash.size,
                                              objt_server(conn->target),
-                                             remote_cs ? remote_cs->conn : NULL);
+                                             remote_cs ? remote_cs->conn : NULL,
+                                             strm);
                        /* We may not have a conn_stream yet, if we don't
                         * know which mux to use, because it will be decided
                         * during the SSL handshake. In this case, there should
@@ -371,7 +374,8 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
 
                        ret = make_proxy_line(trash.area, trash.size,
                                              objt_server(conn->target),
-                                             objt_conn(sess->origin));
+                                             objt_conn(sess->origin),
+                                             NULL);
                }
                else {
                        /* The target server expects a LOCAL line to be sent first. Retrieving
@@ -381,7 +385,8 @@ int conn_si_send_proxy(struct connection *conn, unsigned int flag)
                                goto out_wait;
 
                        ret = make_proxy_line(trash.area, trash.size,
-                                             objt_server(conn->target), conn);
+                                             objt_server(conn->target), conn,
+                                             NULL);
                }
 
                if (!ret)