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 764b237996 ja3_fingerprint: add --preserve option (#11762)
764b237996 is described below

commit 764b2379967de038d8c7376be73d936e9a3a639a
Author: Brian Neradt <[email protected]>
AuthorDate: Tue Sep 17 21:14:51 2024 -0500

    ja3_fingerprint: add --preserve option (#11762)
    
    Add the --preserve option to ja3_fingerprint such that any ja* fields
    already existing are not appended with new values. This is useful in
    situations where there are multiple proxies adding ja headers and the
    one of greatest interest is the outermost edge proxy (presumably, the
    one closest to the client).
---
 doc/admin-guide/plugins/ja3_fingerprint.en.rst     | 13 ++++++++
 plugins/ja3_fingerprint/README                     |  5 +++
 plugins/ja3_fingerprint/ja3_fingerprint.cc         | 36 +++++++++++++---------
 .../ja3_fingerprint/ja3_fingerprint.test.py        |  9 +++++-
 .../ja3_fingerprint_global.replay.yaml             |  1 +
 .../ja3_fingerprint/modify-incoming-proxy.gold     |  5 +--
 .../ja3_fingerprint/modify-sent-proxy.gold         |  9 +-----
 7 files changed, 51 insertions(+), 27 deletions(-)

diff --git a/doc/admin-guide/plugins/ja3_fingerprint.en.rst 
b/doc/admin-guide/plugins/ja3_fingerprint.en.rst
index faada2530a..8cfa7b5aaf 100644
--- a/doc/admin-guide/plugins/ja3_fingerprint.en.rst
+++ b/doc/admin-guide/plugins/ja3_fingerprint.en.rst
@@ -75,6 +75,19 @@ or :file:`remap.config`.
    This option is only useful when the plugin is configured as a global 
plugin. By default this
    is not enabled.
 
+.. option:: --preserve
+
+   There may be situations in a network where there are multiple proxies 
handling the same requests.
+   In these situations, each proxy may be configured to add ja* header fields. 
By default, |TS| will
+   append the ``;`` separated signature values to the already existing header 
fields characteristic
+   of the client-side connection for the given proxy in the chain. However, it 
may be that only the
+   signatures for the proxy closest to the client are interesting for 
fingerprinting and no other
+   signatures are desired to be appended to the fields. In these situations, 
this option causes the
+   plugin to inspect the request to see whether the ja header field already 
exists, and, if so, no
+   further signatures are added to the field value.
+
+   By default this is not enabled.
+
 Requirement
 =============
 
diff --git a/plugins/ja3_fingerprint/README b/plugins/ja3_fingerprint/README
index 7a9e5d9448..3018616049 100644
--- a/plugins/ja3_fingerprint/README
+++ b/plugins/ja3_fingerprint/README
@@ -27,6 +27,11 @@ Add flag --modify-incoming if it is desired that the plugin 
modify the incoming
     plugin) not as a remap plugin.  This is because remap plugins by definition
     are enaged upon remap completion and by that point it is too late to
     meaningfully modify the client request headers.
+Add flag --preserve for situations where there may be multiple proxies
+    handling a request and the first one, the one closest to the client, is
+    the one whose ja value is the most useful. If --preserve is used, then ja
+    values are not appended to already existing values in the request. By
+    default, ja header field values are appened if one exists already.
 
 3. plugin.config
 In plugin.config, supply name of the plugin and any desired options. For 
example:
diff --git a/plugins/ja3_fingerprint/ja3_fingerprint.cc 
b/plugins/ja3_fingerprint/ja3_fingerprint.cc
index 138ecfdc3e..b1403d8ae6 100644
--- a/plugins/ja3_fingerprint/ja3_fingerprint.cc
+++ b/plugins/ja3_fingerprint/ja3_fingerprint.cc
@@ -64,6 +64,7 @@ static int             ja3_idx                        = -1;
 static int             global_raw_enabled             = 0;
 static int             global_log_enabled             = 0;
 static int             global_modify_incoming_enabled = 0;
+static int             global_preserve_enabled        = 0;
 
 struct ja3_data {
   std::string ja3_string;
@@ -86,9 +87,10 @@ struct ja3_data {
 };
 
 struct ja3_remap_info {
-  int    raw_enabled = false;
-  int    log_enabled = false;
-  TSCont handler     = nullptr;
+  int    raw_enabled      = false;
+  int    log_enabled      = false;
+  int    preserve_enabled = false;
+  TSCont handler          = nullptr;
 
   ~ja3_remap_info()
   {
@@ -167,7 +169,7 @@ custom_get_ja3(SSL *ssl)
 // This function will append value to the last occurrence of field. If none 
exists, it will
 // create a field and append to the headers
 static void
-append_to_field(TSMBuffer bufp, TSMLoc hdr_loc, const char *field, int 
field_len, const char *value, int value_len)
+append_to_field(TSMBuffer bufp, TSMLoc hdr_loc, const char *field, int 
field_len, const char *value, int value_len, bool preserve)
 {
   if (!bufp || !hdr_loc || !field || field_len <= 0) {
     return;
@@ -177,14 +179,15 @@ append_to_field(TSMBuffer bufp, TSMLoc hdr_loc, const 
char *field, int field_len
   if (target == TS_NULL_MLOC) {
     TSMimeHdrFieldCreateNamed(bufp, hdr_loc, field, field_len, &target);
     TSMimeHdrFieldAppend(bufp, hdr_loc, target);
-  } else {
+    TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, target, -1, value, 
value_len);
+  } else if (!preserve) {
     TSMLoc next = target;
     while (next) {
       target = next;
       next   = TSMimeHdrFieldNextDup(bufp, hdr_loc, target);
     }
+    TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, target, -1, value, 
value_len);
   }
-  TSMimeHdrFieldValueStringInsert(bufp, hdr_loc, target, -1, value, value_len);
   TSHandleMLocRelease(bufp, hdr_loc, target);
 }
 
@@ -237,9 +240,10 @@ static void
 modify_ja3_headers(TSCont contp, TSHttpTxn txnp, ja3_data const 
*ja3_vconn_data)
 {
   // Decide global or remap
-  ja3_remap_info *remap_info = static_cast<ja3_remap_info 
*>(TSContDataGet(contp));
-  bool            raw_flag   = remap_info ? remap_info->raw_enabled : 
global_raw_enabled;
-  bool            log_flag   = remap_info ? remap_info->log_enabled : 
global_log_enabled;
+  ja3_remap_info *remap_info    = static_cast<ja3_remap_info 
*>(TSContDataGet(contp));
+  bool            raw_flag      = remap_info ? remap_info->raw_enabled : 
global_raw_enabled;
+  bool            log_flag      = remap_info ? remap_info->log_enabled : 
global_log_enabled;
+  bool            preserve_flag = remap_info ? remap_info->preserve_enabled : 
global_preserve_enabled;
   Dbg(dbg_ctl, "Found ja3 string.");
 
   // Get handle to headers
@@ -252,11 +256,12 @@ modify_ja3_headers(TSCont contp, TSHttpTxn txnp, ja3_data 
const *ja3_vconn_data)
   }
 
   // Add JA3 md5 fingerprints
-  append_to_field(bufp, hdr_loc, "x-ja3-sig", 9, ja3_vconn_data->md5_string, 
32);
+  append_to_field(bufp, hdr_loc, "x-ja3-sig", 9, ja3_vconn_data->md5_string, 
32, preserve_flag);
 
   // If raw string is configured, added JA3 raw string to header as well
   if (raw_flag) {
-    append_to_field(bufp, hdr_loc, "x-ja3-raw", 9, 
ja3_vconn_data->ja3_string.data(), ja3_vconn_data->ja3_string.size());
+    append_to_field(bufp, hdr_loc, "x-ja3-raw", 9, 
ja3_vconn_data->ja3_string.data(), ja3_vconn_data->ja3_string.size(),
+                    preserve_flag);
   }
   TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
 
@@ -299,12 +304,13 @@ req_hdr_ja3_handler(TSCont contp, TSEvent event, void 
*edata)
 }
 
 static bool
-read_config_option(int argc, const char *argv[], int &raw, int &log, int 
&modify_incoming)
+read_config_option(int argc, const char *argv[], int &raw, int &log, int 
&modify_incoming, int &preserve)
 {
   const struct option longopts[] = {
     {"ja3raw",          no_argument, &raw,             1},
     {"ja3log",          no_argument, &log,             1},
     {"modify-incoming", no_argument, &modify_incoming, 1},
+    {"preserve",        no_argument, &preserve,        1},
     {nullptr,           0,           nullptr,          0}
   };
 
@@ -325,6 +331,7 @@ read_config_option(int argc, const char *argv[], int &raw, 
int &log, int &modify
   Dbg(dbg_ctl, "ja3 raw is %s", (raw == 1) ? "enabled" : "disabled");
   Dbg(dbg_ctl, "ja3 logging is %s", (log == 1) ? "enabled" : "disabled");
   Dbg(dbg_ctl, "ja3 modify-incoming is %s", (modify_incoming == 1) ? "enabled" 
: "disabled");
+  Dbg(dbg_ctl, "ja3 preserve is %s", (preserve == 1) ? "enabled" : "disabled");
   return true;
 }
 
@@ -340,7 +347,8 @@ TSPluginInit(int argc, const char *argv[])
   info.support_email = "[email protected]";
 
   // Options
-  if (!read_config_option(argc, argv, global_raw_enabled, global_log_enabled, 
global_modify_incoming_enabled)) {
+  if (!read_config_option(argc, argv, global_raw_enabled, global_log_enabled, 
global_modify_incoming_enabled,
+                          global_preserve_enabled)) {
     return;
   }
 
@@ -392,7 +400,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char 
* /* errbuf ATS_UNUSE
   // Parse parameters
   int discard_modify_incoming = -1; // Not used for remap.
   if (!read_config_option(argc - 1, const_cast<const char **>(argv + 1), 
remap_info->raw_enabled, remap_info->log_enabled,
-                          discard_modify_incoming)) {
+                          discard_modify_incoming, 
remap_info->preserve_enabled)) {
     Dbg(dbg_ctl, "Bad arguments");
     return TS_ERROR;
   }
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 6032509a6a..57a6a6f23a 100644
--- a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
+++ b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
@@ -80,6 +80,13 @@ class JA3FingerprintTest:
         JA3FingerprintTest._server_counter += 1
         self._server.Streams.All += 
Testers.ContainsExpression("https-request", "Verify the HTTPS request was 
received.")
         self._server.Streams.All += 
Testers.ContainsExpression("http2-request", "Verify the HTTP/2 request was 
received.")
+        if not self._test_remap:
+            # Verify --preserve worked.
+            self._server.Streams.All += Testers.ContainsExpression("x-ja3-raw: 
.*,", "Verify the new raw header was added.")
+            self._server.Streams.All += Testers.ContainsExpression(
+                "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.")
 
     def _configure_trafficserver(self) -> None:
         """Configure Traffic Server to be used in the test."""
@@ -97,7 +104,7 @@ class JA3FingerprintTest:
                 f'map https://http2.server.com 
https://http2.backend.com:{server_port} '
                 '@plugin=ja3_fingerprint.so @pparam=--ja3log')
         else:
-            arguments = '--ja3log --ja3raw'
+            arguments = '--ja3log --ja3raw --preserve'
             if self._modify_incoming:
                 arguments += ' --modify-incoming'
             self._ts.Disk.plugin_config.AddLine(f'ja3_fingerprint.so 
{arguments}')
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 e14eb417e2..6c0806898e 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
@@ -80,6 +80,7 @@ sessions:
         - [ content-type, image/jpeg ]
         - [ uuid, http2-request ]
         - [ x-request, http2-request ]
+        - [ x-ja3-raw, first-signature ]
       content:
         size: 399
 
diff --git 
a/tests/gold_tests/pluginTest/ja3_fingerprint/modify-incoming-proxy.gold 
b/tests/gold_tests/pluginTest/ja3_fingerprint/modify-incoming-proxy.gold
index d04d9bed8f..a8cd3a41bb 100644
--- a/tests/gold_tests/pluginTest/ja3_fingerprint/modify-incoming-proxy.gold
+++ b/tests/gold_tests/pluginTest/ja3_fingerprint/modify-incoming-proxy.gold
@@ -1,9 +1,6 @@
 +++++++++ Proxy's Request after hooks +++++++++
 -- State Machine Id``
 POST /some/path/http2``
-Host: ``
-Content-Type: ``
-uuid: ``
-x-request: ``
+``
 x-ja3-sig: ``
 ``
diff --git a/tests/gold_tests/pluginTest/ja3_fingerprint/modify-sent-proxy.gold 
b/tests/gold_tests/pluginTest/ja3_fingerprint/modify-sent-proxy.gold
index 4bcd35e40b..a8cd3a41bb 100644
--- a/tests/gold_tests/pluginTest/ja3_fingerprint/modify-sent-proxy.gold
+++ b/tests/gold_tests/pluginTest/ja3_fingerprint/modify-sent-proxy.gold
@@ -1,13 +1,6 @@
 +++++++++ Proxy's Request after hooks +++++++++
 -- State Machine Id``
 POST /some/path/http2``
-Host: ``
-Content-Type: ``
-uuid: ``
-x-request: ``
-Client-ip: ``
-X-Forwarded-For: ``
-Via: ``
-Transfer-Encoding: ``
+``
 x-ja3-sig: ``
 ``

Reply via email to