]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: sample: Add fetch for arbitrary TLVs
authorAlexander Stephan <alexander.stephan@sap.com>
Wed, 16 Aug 2023 13:53:51 +0000 (15:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 29 Aug 2023 13:31:28 +0000 (15:31 +0200)
Based on the new, generic allocation infrastructure, a new sample
fetch fc_pp_tlv is introduced. It is an abstraction for existing
PPv2 TLV sample fetches. It takes any valid TLV ID as argument and
returns the value as a string, similar to fc_pp_authority and
fc_pp_unique_id.

doc/configuration.txt
reg-tests/sample_fetches/tlvs.vtc [new file with mode: 0644]
src/connection.c

index 9794c36e38d8008be5083348d8c003c346058abb..14b3d35e5c5fcf0589c48e30d616147643a982f2 100644 (file)
@@ -20174,12 +20174,28 @@ fc_lost : integer
   Linux kernels before 2.4, the sample fetch fails.
 
 fc_pp_authority : string
-  Returns the authority TLV sent by the client in the PROXY protocol header,
-  if any.
+  Returns the first authority TLV sent by the client in the PROXY protocol
+  header, if any.
 
 fc_pp_unique_id : string
-  Returns the unique ID TLV sent by the client in the PROXY protocol header,
-  if any.
+  Returns the first unique ID TLV sent by the client in the PROXY protocol
+  header, if any.
+
+fc_pp_tlv(<id>) : string
+  Returns the TLV value for the given TLV ID which must be a numeric
+  value between 0 and 255.
+
+  The received value must be smaller or equal to 1024 bytes. This is done to
+  prevent potential DoS attacks. Values smaller or equal to 256 bytes will be
+  able to be memory pooled. Therefore, try to restrict the length of sent
+  values to 256 bytes for optimal performance.
+
+  Note that unlike fc_pp_authority and fc_pp_unique_id, fc_pp_tlv is able to
+  iterate over all occurances of a requested TLV in case there are duplicate
+  TLV IDs. The order of iteration matches the position in the PROXY protocol
+  header. However, relying on duplicates should mostly be avoided as TLVs are
+  typically assumed to be unique. Generally, finding duplicated TLV IDs
+  indicates an error on the sender side of the PROXY protocol header.
 
 fc_rcvd_proxy : boolean
   Returns true if the client initiated the connection with a PROXY protocol
diff --git a/reg-tests/sample_fetches/tlvs.vtc b/reg-tests/sample_fetches/tlvs.vtc
new file mode 100644 (file)
index 0000000..9312b1d
--- /dev/null
@@ -0,0 +1,57 @@
+varnishtest "Tests for fetching PROXY protocol v2 TLVs"
+feature ignore_unknown_macro
+
+haproxy h1 -conf {
+    defaults
+        mode http
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    frontend echo
+        bind "fd@${fe1}" accept-proxy
+        tcp-request content set-var(sess.aws) fc_pp_tlv(0xEA),bytes(1) if { fc_pp_tlv(0xEE),bytes(0,1),hex eq 01 }
+        tcp-request content set-var(sess.azure) fc_pp_tlv(0xEE),bytes(1) if { fc_pp_tlv(0xEA),bytes(0,1),hex eq 01 }
+
+        http-after-response set-header echo1 %[var(sess.aws)]
+        http-after-response set-header echo2 %[var(sess.azure)]
+        http-after-response set-header echo3 %[fc_pp_tlv(0xEB)]
+        http-after-response set-header echo4 %[fc_pp_tlv(0xEC),length]
+        http-request return status 200
+} -start
+
+client c1 -connect ${h1_fe1_sock} {
+    # PROXY v2 signature
+    sendhex "0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a"
+    # version + PROXY
+    sendhex "21"
+    # TCP4
+    sendhex "11"
+    # length of the address (12) + length of the TLVs (14 + 10 + 9 + 131)
+    sendhex "00 B0"
+    # 127.0.0.1 42 127.0.0.1 1337
+    sendhex "7F 00 00 01 7F 00 00 01 00 2A 05 39"
+
+    # PP2_TYPE_AWS (0xEA) + length of the value + PP2_SUBTYPE_AWS_VPCE_ID (0x01) + "aws-vpc-id"
+    # See https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#custom-tlv for the respective definitions.
+    sendhex "EA 00 0B 01 61 77 73 2D 76 70 63 2D 69 64"
+
+    # PP2_TYPE_AZURE (0xEE) + length of the value + PP2_SUBTYPE_AZURE_PRIVATEENDPOINT_LINKID (0x01) + "LINKID"
+    # See https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview#getting-connection-information-using-tcp-proxy-v2
+    # for the respective definitions.
+    sendhex "EE 00 07 01 4C 49 4E 4B 49 44"
+
+    # custom type (0xEB) + length of the value + "custom"
+    sendhex "EB 00 06 63 75 73 74 6F 6D"
+
+    # custom type (0xEC) + length of the value (128, does not fit in pool) + random data
+    sendhex "EC 00 80 3A D9 32 9B 11 A7 29 81 14 B2 33 F0 C2 0D 7A 53 D1 97 28 74 4B 78 8A D3 10 C4 B1 88 42 9C 63 8E 8B 8A A0 B4 B0 E7 9D 20 27 0F 1E 53 4D 33 F7 5A D0 91 3F B8 C9 E9 16 C4 61 C5 13 02 92 64 9D D4 22 5C 8E 4E 0B 2D 2D 7D 9F 5D 97 9B 25 C4 12 7D 21 75 C8 15 92 6B 64 F2 5F C0 A9 0F 9A 7D 0A 6D 68 79 F4 56 18 6F 23 45 2A 9B 36 34 3A 47 43 32 29 18 6F 23 45 2A 9B 36 34 3A 47 43 32 29 32 29"
+
+    txreq -url "/"
+    rxresp
+    expect resp.status == 200
+    expect resp.http.echo1 == "aws-vpc-id"
+    expect resp.http.echo2 == "LINKID"
+    expect resp.http.echo3 == "custom"
+    expect resp.http.echo4 == 128
+} -run
index c7403b5829f963e78c96a6faf7401f5ab02ad207..5bcc4d032c3fab4f784831e0b279e433697fb8ad 100644 (file)
@@ -15,6 +15,7 @@
 #include <import/ebmbtree.h>
 
 #include <haproxy/api.h>
+#include <haproxy/arg.h>
 #include <haproxy/cfgparse.h>
 #include <haproxy/connection.h>
 #include <haproxy/fd.h>
@@ -2258,11 +2259,77 @@ int smp_fetch_fc_rcvd_proxy(const struct arg *args, struct sample *smp, const ch
        return 1;
 }
 
+/*
+ * This function checks the TLV type converter configuration.
+ * It expects the corresponding TLV type as a string representing the number.
+ * args[0] will be turned into the numerical value of the TLV type string.
+ */
+static int smp_check_tlv_type(struct arg *args, char **err)
+{
+       int type;
+       char *endp;
+
+       type = strtoul(args[0].data.str.area, &endp, 0);
+       if (endp && *endp != '\0') {
+               memprintf(err, "Could not convert type '%s'", args[0].data.str.area);
+               return 0;
+       }
+
+       if (type < 0 || type > 255) {
+               memprintf(err, "Invalid TLV Type '%s'", args[0].data.str.area);
+               return 0;
+       }
+
+       chunk_destroy(&args[0].data.str);
+       args[0].type = ARGT_SINT;
+       args[0].data.sint = type;
+
+       return 1;
+}
+
+/* fetch an arbitrary TLV from a PROXY protocol v2 header */
+int smp_fetch_fc_pp_tlv(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+       int idx;
+       struct connection *conn = NULL;
+       struct conn_tlv_list *conn_tlv = NULL;
+
+       conn = objt_conn(smp->sess->origin);
+       if (!conn)
+               return 0;
+
+       if (conn->flags & CO_FL_WAIT_XPRT) {
+               smp->flags |= SMP_F_MAY_CHANGE;
+               return 0;
+       }
+
+       if (args[0].type != ARGT_SINT)
+               return 0;
+
+       idx = args[0].data.sint;
+       conn_tlv = smp->ctx.p ? smp->ctx.p : LIST_ELEM(conn->tlv_list.n, struct conn_tlv_list *, list);
+       list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
+               if (conn_tlv->type == idx) {
+                       smp->flags |= SMP_F_NOT_LAST;
+                       smp->data.type = SMP_T_STR;
+                       smp->data.u.str.area = conn_tlv->value;
+                       smp->data.u.str.data = conn_tlv->len;
+                       smp->ctx.p = conn_tlv;
+
+                       return 1;
+               }
+       }
+
+       smp->flags &= ~SMP_F_NOT_LAST;
+
+       return 0;
+}
+
 /* fetch the authority TLV from a PROXY protocol header */
 int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
        struct connection *conn = NULL;
-       struct conn_tlv_list *conn_tlv;
+       struct conn_tlv_list *conn_tlv = NULL;
 
        conn = objt_conn(smp->sess->origin);
        if (!conn)
@@ -2276,7 +2343,6 @@ int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const
        conn_tlv = smp->ctx.p ? smp->ctx.p : LIST_ELEM(conn->tlv_list.n, struct conn_tlv_list *, list);
        list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
                if (conn_tlv->type == PP2_TYPE_AUTHORITY) {
-                       smp->flags |= SMP_F_NOT_LAST;
                        smp->data.type = SMP_T_STR;
                        smp->data.u.str.area = conn_tlv->value;
                        smp->data.u.str.data = conn_tlv->len;
@@ -2295,7 +2361,7 @@ int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const
 int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
        struct connection *conn = NULL;
-       struct conn_tlv_list *conn_tlv;
+       struct conn_tlv_list *conn_tlv = NULL;
 
        conn = objt_conn(smp->sess->origin);
        if (!conn)
@@ -2309,7 +2375,6 @@ int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp, const
        conn_tlv = smp->ctx.p ? smp->ctx.p : LIST_ELEM(conn->tlv_list.n, struct conn_tlv_list *, list);
        list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
                if (conn_tlv->type == PP2_TYPE_UNIQUE_ID) {
-                       smp->flags |= SMP_F_NOT_LAST;
                        smp->data.type = SMP_T_STR;
                        smp->data.u.str.area = conn_tlv->value;
                        smp->data.u.str.data = conn_tlv->len;
@@ -2398,6 +2463,7 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
        { "fc_rcvd_proxy", smp_fetch_fc_rcvd_proxy, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
        { "fc_pp_authority", smp_fetch_fc_pp_authority, 0, NULL, SMP_T_STR, SMP_USE_L4CLI },
        { "fc_pp_unique_id", smp_fetch_fc_pp_unique_id, 0, NULL, SMP_T_STR, SMP_USE_L4CLI },
+       { "fc_pp_tlv", smp_fetch_fc_pp_tlv, ARG1(1, STR), smp_check_tlv_type, SMP_T_STR, SMP_USE_L4CLI },
        { /* END */ },
 }};