MEDIUM: sample: Add fetch for arbitrary TLVs

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.
This commit is contained in:
Alexander Stephan 2023-08-16 15:53:51 +02:00 committed by Willy Tarreau
parent fecc573da1
commit f773ef721c
3 changed files with 147 additions and 8 deletions

View 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

View File

@ -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

View 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 */ },
}};