diff --git a/doc/configuration.txt b/doc/configuration.txt index 9794c36e38..14b3d35e5c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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() : 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 index 0000000000..9312b1df91 --- /dev/null +++ b/reg-tests/sample_fetches/tlvs.vtc @@ -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 diff --git a/src/connection.c b/src/connection.c index c7403b5829..5bcc4d032c 100644 --- a/src/connection.c +++ b/src/connection.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -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 */ }, }};