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',
             })

Reply via email to