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: ``
``