This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 622e4b185d x-ja3-via and x-ja4-via (#12220)
622e4b185d is described below
commit 622e4b185d8e4f8a9f8be248159d2ed52723d6e6
Author: Brian Neradt <[email protected]>
AuthorDate: Mon May 5 16:46:57 2025 -0500
x-ja3-via and x-ja4-via (#12220)
Add via strings for the JA* fingerprints. This can be helpful in
situations where multiple layers of proxies can add these and it may not
be obvious what host added what fingerprint.
---
doc/admin-guide/plugins/ja3_fingerprint.en.rst | 3 +++
plugins/experimental/ja4_fingerprint/plugin.cc | 20 +++++++++++++++++---
plugins/ja3_fingerprint/ja3_fingerprint.cc | 15 +++++++++++++--
.../ja3_fingerprint/ja3_fingerprint.test.py | 4 +++-
.../ja3_fingerprint_global.replay.yaml | 3 +++
.../ja3_fingerprint_remap.replay.yaml | 4 ++++
.../ja4_fingerprint/ja4_fingerprint.replay.yaml | 1 +
.../ja4_fingerprint/ja4_fingerprint.test.py | 1 +
8 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/doc/admin-guide/plugins/ja3_fingerprint.en.rst
b/doc/admin-guide/plugins/ja3_fingerprint.en.rst
index 8cfa7b5aaf..93b37b4835 100644
--- a/doc/admin-guide/plugins/ja3_fingerprint.en.rst
+++ b/doc/admin-guide/plugins/ja3_fingerprint.en.rst
@@ -38,6 +38,9 @@ JA3 is available `here <https://github.com/salesforce/ja3>`__.
The calculated JA3 fingerprints are then appended to upstream request in the
field ``X-JA3-Sig``
(to be processed at upstream). If multiple duplicates exist for the field
name, it will append to the last
occurrence; if none exists, it will add such a field to the headers. The
signatures can also be logged locally.
+To help identify what proxy is adding what signature when there are multiple
proxies in a chain, the plugin
+also adds a ``x-ja3-via`` header with a ``;`` separated list of
:ts:cv:`proxy.config.proxy_name` values for
+each proxy in the chain that adds a signature.
Plugin Configuration
====================
diff --git a/plugins/experimental/ja4_fingerprint/plugin.cc
b/plugins/experimental/ja4_fingerprint/plugin.cc
index 711f53d781..ada54ed1c9 100644
--- a/plugins/experimental/ja4_fingerprint/plugin.cc
+++ b/plugins/experimental/ja4_fingerprint/plugin.cc
@@ -38,6 +38,7 @@
#include <cstdio>
#include <memory>
#include <string>
+#include <string_view>
struct JA4_data {
std::string fingerprint;
@@ -60,7 +61,7 @@ static void
add_ciphers(JA4::TLSClientHelloSummary &summary, SSL *
static void add_extensions(JA4::TLSClientHelloSummary &summary,
SSL *ssl);
static std::string hash_with_SHA256(std::string_view sv);
static int handle_read_request_hdr(TSCont cont, TSEvent event,
void *edata);
-static void append_JA4_header(TSCont cont, TSHttpTxn txnp,
std::string const *fingerprint);
+static void append_JA4_headers(TSCont cont, TSHttpTxn txnp,
std::string const *fingerprint);
static void append_to_field(TSMBuffer bufp, TSMLoc hdr_loc, char const *field,
int field_len, char const *value, int value_len);
static int handle_vconn_close(TSCont cont, TSEvent event, void *edata);
@@ -70,6 +71,8 @@ constexpr char const *PLUGIN_NAME{"ja4_fingerprint"};
constexpr char const *PLUGIN_VENDOR{"Apache Software Foundation"};
constexpr char const *PLUGIN_SUPPORT_EMAIL{"[email protected]"};
+constexpr std::string_view JA4_VIA_HEADER{"x-ja4-via"};
+
constexpr unsigned int EXT_ALPN{0x10};
constexpr unsigned int EXT_SUPPORTED_VERSIONS{0x2b};
constexpr int SSL_SUCCESS{1};
@@ -322,7 +325,7 @@ handle_read_request_hdr(TSCont cont, TSEvent event, void
*edata)
std::string *fingerprint{static_cast<std::string *>(TSUserArgGet(vconn,
*get_user_arg_index()))};
if (fingerprint) {
- append_JA4_header(cont, txnp, fingerprint);
+ append_JA4_headers(cont, txnp, fingerprint);
} else {
Dbg(dbg_ctl, "No JA4 fingerprint attached to vconn!");
}
@@ -332,12 +335,23 @@ handle_read_request_hdr(TSCont cont, TSEvent event, void
*edata)
}
void
-append_JA4_header(TSCont /* cont ATS_UNUSED */, TSHttpTxn txnp, std::string
const *fingerprint)
+append_JA4_headers(TSCont /* cont ATS_UNUSED */, TSHttpTxn txnp, std::string
const *fingerprint)
{
TSMBuffer bufp;
TSMLoc hdr_loc;
if (TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc)) {
append_to_field(bufp, hdr_loc, "ja4", 3, fingerprint->data(),
fingerprint->size());
+
+ TSMgmtString proxy_name = nullptr;
+ if (TS_SUCCESS != TSMgmtStringGet("proxy.config.proxy_name", &proxy_name))
{
+ TSError("[%s] Failed to get proxy name for %s, set
'proxy.config.proxy_name' in records.config", PLUGIN_NAME,
+ JA4_VIA_HEADER.data());
+ proxy_name = TSstrdup("unknown");
+ }
+ append_to_field(bufp, hdr_loc, JA4_VIA_HEADER.data(),
static_cast<int>(JA4_VIA_HEADER.length()), proxy_name,
+ static_cast<int>(std::strlen(proxy_name)));
+ TSfree(proxy_name);
+
} else {
Dbg(dbg_ctl, "Failed to get headers.");
}
diff --git a/plugins/ja3_fingerprint/ja3_fingerprint.cc
b/plugins/ja3_fingerprint/ja3_fingerprint.cc
index b1403d8ae6..e439aab9c9 100644
--- a/plugins/ja3_fingerprint/ja3_fingerprint.cc
+++ b/plugins/ja3_fingerprint/ja3_fingerprint.cc
@@ -27,6 +27,7 @@
#include <getopt.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <string_view>
#include "ts/apidefs.h"
#include "ts/ts.h"
@@ -49,8 +50,8 @@
namespace
{
-//
-constexpr int ja3_hash_included_byte_count{16};
+constexpr std::string_view JA3_VIA_HEADER{"x-ja3-via"};
+constexpr int ja3_hash_included_byte_count{16};
static_assert(ja3_hash_included_byte_count <= MD5_DIGEST_LENGTH);
constexpr int ja3_hash_hex_string_with_null_terminator_length{2 *
ja3_hash_included_byte_count + 1};
@@ -255,6 +256,16 @@ modify_ja3_headers(TSCont contp, TSHttpTxn txnp, ja3_data
const *ja3_vconn_data)
TSAssert(TS_SUCCESS == TSHttpTxnServerReqGet(txnp, &bufp, &hdr_loc));
}
+ TSMgmtString proxy_name = nullptr;
+ if (TS_SUCCESS != TSMgmtStringGet("proxy.config.proxy_name", &proxy_name)) {
+ TSError("[%s] Failed to get proxy name for %s, set
'proxy.config.proxy_name' in records.config", PLUGIN_NAME,
+ JA3_VIA_HEADER.data());
+ proxy_name = TSstrdup("unknown");
+ }
+ append_to_field(bufp, hdr_loc, JA3_VIA_HEADER.data(),
static_cast<int>(JA3_VIA_HEADER.length()), proxy_name,
+ static_cast<int>(std::strlen(proxy_name)), preserve_flag);
+ TSfree(proxy_name);
+
// Add JA3 md5 fingerprints
append_to_field(bufp, hdr_loc, "x-ja3-sig", 9, ja3_vconn_data->md5_string,
32, preserve_flag);
diff --git
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
index 57a6a6f23a..ffd1187f0d 100644
--- a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
+++ b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
@@ -53,7 +53,7 @@ class JA3FingerprintTest:
self._modify_incoming = modify_incoming
- tr = Test.AddTestRun('Testing ja3_fingerprint plugin.')
+ tr = Test.AddTestRun(f'test_remap: {test_remap}, modify_incoming:
{modify_incoming}')
self._configure_dns(tr)
self._configure_server(tr)
self._configure_trafficserver()
@@ -87,6 +87,7 @@ class JA3FingerprintTest:
"x-ja3-raw: first-signature", "Verify the already-existing raw
header was preserved.")
self._server.Streams.All += Testers.ExcludesExpression(
"x-ja3-raw: first-signature;", "Verify no extra values were
added due to preserve.")
+ self._server.Streams.All += Testers.ContainsExpression("x-ja3-via:
test.proxy.com", "The x-ja3-via string was added.")
def _configure_trafficserver(self) -> None:
"""Configure Traffic Server to be used in the test."""
@@ -117,6 +118,7 @@ class JA3FingerprintTest:
'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE',
'proxy.config.dns.nameservers':
f"127.0.0.1:{self._dns.Variables.Port}",
'proxy.config.dns.resolv_conf': 'NULL',
+ 'proxy.config.proxy_name': 'test.proxy.com',
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'http|ja3_fingerprint',
})
diff --git
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_global.replay.yaml
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_global.replay.yaml
index 6c0806898e..bd42ddd9cc 100644
---
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_global.replay.yaml
+++
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_global.replay.yaml
@@ -42,6 +42,7 @@ sessions:
headers:
fields:
- [ X-Request, { value: 'https-request', as: equal } ]
+ - [ x-ja3-via, { value: 'test.proxy.com', as: equal } ]
- [ X-JA3-Sig, { as: present } ]
- [ X-JA3-Raw, { as: present } ]
@@ -81,6 +82,7 @@ sessions:
- [ uuid, http2-request ]
- [ x-request, http2-request ]
- [ x-ja3-raw, first-signature ]
+ - [ x-ja3-via, first-via ]
content:
size: 399
@@ -88,6 +90,7 @@ sessions:
headers:
fields:
- [ x-request, { value: 'http2-request', as: equal } ]
+ - [ x-ja3-via, { value: 'first-via', as: equal } ]
- [ X-JA3-Sig, { as: present } ]
- [ X-JA3-Raw, { as: present } ]
diff --git
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_remap.replay.yaml
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_remap.replay.yaml
index 6123304bc9..6fcc3293ba 100644
---
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_remap.replay.yaml
+++
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_remap.replay.yaml
@@ -44,6 +44,7 @@ sessions:
headers:
fields:
- [ X-Request, { value: 'https-request', as: equal } ]
+ - [ x-ja3-via, { as: absent } ]
- [ X-JA3-Sig, { as: absent } ]
- [ X-JA3-Raw, { as: absent } ]
@@ -82,6 +83,8 @@ sessions:
- [ content-type, image/jpeg ]
- [ uuid, http2-request ]
- [ x-request, http2-request ]
+ - [ x-ja3-sig, first-signature ]
+ - [ x-ja3-via, first-via ]
content:
size: 399
@@ -91,6 +94,7 @@ sessions:
headers:
fields:
- [ x-request, { value: 'http2-request', as: equal } ]
+ - [ x-ja3-via, { value: 'first-via, test.proxy.com', as: equal } ]
- [ X-JA3-Sig, { as: present } ]
- [ X-JA3-Raw, { as: absent } ]
diff --git
a/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.replay.yaml
b/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.replay.yaml
index 8e23953c95..f22de8c76d 100644
--- a/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.replay.yaml
+++ b/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.replay.yaml
@@ -38,6 +38,7 @@ sessions:
headers:
fields:
- [ ja4, { as: contains } ]
+ - [ x-ja4-via, { value: 'test.proxy.com', as: equal } ]
server-response:
status: 200
diff --git
a/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.test.py
b/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.test.py
index 0b3eb0ace0..fdc5cac5ba 100644
--- a/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.test.py
+++ b/tests/gold_tests/pluginTest/ja4_fingerprint/ja4_fingerprint.test.py
@@ -117,6 +117,7 @@ class TestJA4Fingerprint:
'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}',
'proxy.config.ssl.server.private_key.path':
f'{ts.Variables.SSLDir}',
'proxy.config.http.server_ports': f'{self._port_one}:ssl',
+ 'proxy.config.proxy_name': 'test.proxy.com',
'proxy.config.diags.debug.enabled': 1,
'proxy.config.diags.debug.tags': 'ja4_fingerprint|http',
})