From: Willy Tarreau Date: Tue, 22 Sep 2026 06:39:40 +0000 (+0000) Subject: REGTESTS: sample_fetches: test the req.ssl_alpn fetch X-Git-Tag: v3.5-dev8~32 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=701c7474b83b128783a7b0a0ebbdc7d83314e845;p=haproxy.git REGTESTS: sample_fetches: test the req.ssl_alpn fetch req.ssl_alpn was the last fetch of the raw ClientHello family without a test of its own, it only appeared as a condition in the tcp-check test. It is also the only one of the family which returns several samples, one per protocol name of the extension, so the test checks that a log-format only sees the first name while an ACL matches any of them. Five hand-crafted ClientHellos are used: one advertising "h2" and "http/1.1" behind a server_name extension, one advertising the single "acme-tls/1" name of the documentation example, one without any ALPN extension, one whose ALPN extension is too short to hold a name, and one announcing a name longer than the extension which carries it, the last two checking that the fetch gives up instead of reading the extension which follows. --- diff --git a/reg-tests/sample_fetches/ssl_client_hello_alpn.vtc b/reg-tests/sample_fetches/ssl_client_hello_alpn.vtc new file mode 100644 index 000000000..fbfef995a --- /dev/null +++ b/reg-tests/sample_fetches/ssl_client_hello_alpn.vtc @@ -0,0 +1,113 @@ +varnishtest "Test the req.ssl_alpn fetch on a raw TLS ClientHello" + +# req.ssl_alpn is the last fetch of the raw ClientHello family which had +# no coverage of its own: it is only used as a condition in +# reg-tests/checks/tcp-check-ssl.vtc. It differs from its neighbours by +# returning one sample per protocol name of the ALPN extension, so that +# only the first one is seen by a converter or a log-format while an ACL +# matches any of them. +# +# As for the other tests of this family, the ClientHellos are crafted by +# hand since the fetch parses the raw request buffer of a TCP proxy and +# not the contents deciphered by an SSL data layer. + +feature ignore_unknown_macro + +syslog S1 -level info { + # two protocol names, the first one is the returned sample, both + # are matched by the ACL + recv + expect ~ "alpn=h2 h2=1 h11=1 acme=-$" + # a single name, as in the "acme-tls/1" example of the doc + recv + expect ~ "alpn=acme-tls/1 h2=- h11=- acme=1$" + # no ALPN extension at all + recv + expect ~ "alpn=- h2=- h11=- acme=-$" + # an ALPN extension too short to hold a name + recv + expect ~ "alpn=- h2=- h11=- acme=-$" + # a name longer than the extension which holds it + recv + expect ~ "alpn=- h2=- h11=- acme=-$" +} -start + +haproxy h1 -conf { + global + .if feature(THREAD) + thread-groups 1 + .endif + chroot / + nbthread 1 + + defaults + mode tcp + timeout connect "${HAPROXY_TEST_TIMEOUT-5s}" + timeout client "${HAPROXY_TEST_TIMEOUT-5s}" + timeout server "${HAPROXY_TEST_TIMEOUT-5s}" + + frontend fe1 + bind "fd@${fe1}" + log ${S1_addr}:${S1_port} local0 + log-format "alpn=%[var(txn.a)] h2=%[var(txn.h2)] h11=%[var(txn.h11)] acme=%[var(txn.acme)]" + + tcp-request inspect-delay 100ms + # a log-format or a converter only sees the first protocol name + tcp-request content set-var(txn.a) req.ssl_alpn + # while an ACL matches any of them + tcp-request content set-var(txn.h2) int(1) if { req.ssl_alpn h2 } + tcp-request content set-var(txn.h11) int(1) if { req.ssl_alpn http/1.1 } + tcp-request content set-var(txn.acme) int(1) if { req.ssl_alpn acme-tls/1 } + tcp-request content reject +} -start + +# The ClientHellos share the 43-byte body of the other ClientHello tests: +# 16 03 01 LL LL TLS record: handshake, TLSv1.0 +# 01 00 00 LL Handshake: ClientHello +# 03 03 client_version, TLSv1.2 +# 00 .. 1f Random[32] +# 00 session_id_len = 0 +# 00 02 13 02 cipher_len = 2, one cipher suite +# 01 00 compression_len = 1, method 0 +# LL LL extensions_len +# and only differ by the extensions which follow. + +# server_name, then an ALPN (0010) advertising "h2" and "http/1.1": +# 0010 000e extension ALPN, 14 bytes +# 000c protocol_names_len +# 02 6832 "h2" +# 08 687474702f312e31 "http/1.1" +client c1 -connect ${h1_fe1_sock} { + sendhex "160301 0059 01000055 0303 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 00 00021302 0100 002a 00000014001200000f 7777772e6578616d706c652e636f6d 0010000e000c026832 08687474702f312e31" + expect_close +} -run + +# a single "acme-tls/1" name +client c2 -connect ${h1_fe1_sock} { + sendhex "160301 0040 0100003c 0303 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 00 00021302 0100 0011 0010000d000b 0a61636d652d746c732f31" + expect_close +} -run + +# a single unrelated extension (renegotiation_info): no ALPN +client c3 -connect ${h1_fe1_sock} { + sendhex "160301 0034 01000030 0303 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 00 00021302 0100 0005 ff01000100" + expect_close +} -run + +# an ALPN extension whose body is only the 2-byte list length: it cannot +# hold a name, and the fetch must give up rather than read the next +# extension header as a name length. +client c4 -connect ${h1_fe1_sock} { + sendhex "160301 003a 01000036 0303 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 00 00021302 0100 000b 00100002 0000 ff01000100" + expect_close +} -run + +# an ALPN extension announcing a 32-byte name while only 2 bytes of it +# fit in the extension: the name must not be read from the 32 0xaa bytes +# of the extension which follows. +client c5 -connect ${h1_fe1_sock} { + sendhex "160301 005c 01000058 0303 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 00 00021302 0100 002d 00100005 0003 20 6832 ff010020 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + expect_close +} -run + +syslog S1 -wait