Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/ExperimentalPlugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ auto_option(HTTP_STATS FEATURE_VAR BUILD_HTTP_STATS DEFAULT ${_DEFAULT})
auto_option(ICAP FEATURE_VAR BUILD_ICAP DEFAULT ${_DEFAULT})
auto_option(INLINER FEATURE_VAR BUILD_INLINER DEFAULT ${_DEFAULT})
auto_option(JA4_FINGERPRINT FEATURE_VAR BUILD_JA4_FINGERPRINT VAR_DEPENDS DEFAULT ${_DEFAULT})
auto_option(JAX_FINGERPRINT FEATURE_VAR BUILD_JAX_FINGERPRINT VAR_DEPENDS DEFAULT ${_DEFAULT})
auto_option(
MAGICK
FEATURE_VAR
Expand Down
4 changes: 4 additions & 0 deletions doc/admin-guide/plugins/index.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ directory of the |TS| source tree. Experimental plugins can be compiled by passi
Hook Trace <hook-trace.en>
ICAP <icap.en>
JA4 Fingerprint <ja4_fingerprint.en>
JAx Fingerprint <jax_fingerprint.en>
Maxmind ACL <maxmind_acl.en>
Memcache <memcache.en>
Memory Profile <memory_profile.en>
Expand Down Expand Up @@ -236,6 +237,9 @@ directory of the |TS| source tree. Experimental plugins can be compiled by passi
:doc:`JA4 Fingerprint <ja4_fingerprint.en>`
Calculates JA4 Fingerprints for incoming TLS traffic.

:doc:`JAx Fingerprint <jax_fingerprint.en>`
Calculates JAx Fingerprints.

:doc:`MaxMind ACL <maxmind_acl.en>`
ACL based on the maxmind geo databases (GeoIP2 mmdb and libmaxminddb)

Expand Down
180 changes: 180 additions & 0 deletions doc/admin-guide/plugins/jax_fingerprint.en.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
.. Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.

.. include:: ../../common.defs

.. _admin-plugins-jax-fingerprint:

JAx Fingerprint Plugin
**********************

Description
===========

The JAx Fingerprint plugin generates client fingerprints based on the JA4+ or JA3 algorithms designed by John Althouse.

Fingerprints can be used for:

* Client identification and tracking
* Bot detection and mitigation
* Security analytics and threat intelligence
* Understanding client implementation patterns


Plugin Configuration
====================

You can use the plugin as a global plugin, a remap plugin, or both.

To use the plugin as a global plugin, add the following line to :file:`plugin.config`::

jax_fingerprint.so --standalone

To use the plugin as a remap plugin, append the following line to a remap rule on :file:`remap.config`::

@plugin=jax_fingerprint.so @pparam=--standalone

To use the plugin in a hybrid setup (both global and remap plugin), configure it in both :file:`plugin.config` and
:file:`remap.config` without `--standalone` option.


.. option:: --standalone

This option enables you to use the plugin as either a global plugin, or a remap plugin. In other
words, the option needs to be specified if you do not use the hybrid setup.

.. option:: --method <JA4|JA4H|JA3>

Fingerprinting method (e.g. JA4, JA3, etc.) to use.

.. option:: --mode <overwrite|keep|append>

This option specifies what to do if requests from clients have the header names that are specified
by `--header` and/or `--via-header`. Available setting values are "overwrite", "keep" and "append".

.. option:: --servernames <servername1,servername2,...>

This option specifies server name(s) for which the plugin generates fingerprints.
The value must be provided as a single comma separated value (no space) of server names.

.. option:: --header <header_name>

This option specifies the name of the header field where the plugin stores the generated fingerprint value. If not specified, header generation will be suppressed.

.. option:: --via-header <via_header_name>

This option specifies the name of the header field where the plugin stores the generated fingerprint-via value. If not specified, header generation will be suppressed.

.. option:: --log-filename <filename>

This option specifies the filename for the plugin log file. If not specified, log output will be suppressed.


Plugin Behavior
===============

Global plugin setup
-------------------

Global plugin setup is the best if you:
* Need a fingerprint on every request

Remap plugin setup
------------------

Remap plugin setup is the best if you:
* Need a fingerprint only on specific paths, or
* Cannot use Global plugin setup

.. note:: For JA3 and JA4, fingerprints are always generated at the beginning of connections. Using remap plugin setup only reduces the overhead of adding HTTP headers and logging.

Hybrid setup
------------

Hybrid setup is the best if you:
* Need a fingerprint only for specific server names (in TLS SNI extension), and
* Need a fingerprint only on specific paths


Log Output
==========

The plugin outputs a log file in the Traffic Server log directory (typically ``/var/log/trafficserver/``) if a log filename is
specified by ``--log-filename`` option.

**Log Format**::

[timestamp] Client: <address> <method_name>: <fingerprint>

**Example**::

[Jan 29 10:15:23.456] Client: 192.168.1.100 JA4: t13d1516h2_8daaf6152771_b186095e22b6
[Jan 29 10:15:24.123] Client: 10.0.0.50 JA4: t13d1715h2_8daaf6152771_02713d6af862


Using HTTP Headers in Origin Requests
=====================================

Origin servers can access the generated fingerprint through the injected HTTP header.
This allows the origin to:

* Make access control decisions based on client fingerprints
* Log fingerprints for security analysis
* Track client populations and TLS implementation patterns

The fingerprint-via header allows origin servers to track which Traffic Server proxy handled the request when multiple proxies are deployed.


Debugging
=========

To enable debug logging for the plugin, set the following in :file:`records.yaml`::

records:
diags:
debug:
enabled: 1
tags: jax_fingerprint


Requirements
============

* Traffic Server must be built with TLS support (OpenSSL or BoringSSL) if you use JA3 or JA4


See Also
========
* JA3 Technical Specification: https://github.com/FoxIO-LLC/ja3
* JA4+ Technical Specification: https://github.com/FoxIO-LLC/ja4


Example Configuration
=====================

Enable JA4 fingerprinting by hybrid (global + remap) setup
----------------------------------------------------------

This configuration adds x-my-ja4 header if a connection is established for either abc.example or xyz.example.

**plugin.config**::

jax_fingerprint.so --method JA4 --servernames abc.example,xyz.example

**remap.config**::

map / http://origin.example/ @plugin=jax_fingerprint.so @pparam=--method=JA4 @pparam=--header=x-my-ja4
3 changes: 3 additions & 0 deletions plugins/experimental/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ endif()
if(BUILD_JA4_FINGERPRINT)
add_subdirectory(ja4_fingerprint)
endif()
if(BUILD_JAX_FINGERPRINT)
add_subdirectory(jax_fingerprint)
endif()
if(BUILD_MAGICK)
add_subdirectory(magick)
endif()
Expand Down
41 changes: 41 additions & 0 deletions plugins/experimental/jax_fingerprint/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#######################
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor license
# agreements. See the NOTICE file distributed with this work for additional information regarding
# copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
#
#######################

add_atsplugin(
jax_fingerprint
plugin.cc
context.cc
userarg.cc
header.cc
log.cc
ja3/ja3_method.cc
ja3/ja3_utils.cc
ja4/ja4_method.cc
ja4/ja4.cc
ja4/tls_client_hello_summary.cc
ja4h/ja4h_method.cc
ja4h/ja4h.cc
)
target_link_libraries(jax_fingerprint PRIVATE OpenSSL::Crypto OpenSSL::SSL)
verify_global_plugin(jax_fingerprint)

if(BUILD_TESTING)
add_executable(test_jax ja3/test_ja3.cc ja3/ja3_utils.cc ja4/test_ja4.cc ja4/ja4.cc ja4/tls_client_hello_summary.cc)
target_link_libraries(test_jax PRIVATE Catch2::Catch2WithMain)

add_catch2_test(NAME test_jax COMMAND test_jax)
endif()
66 changes: 66 additions & 0 deletions plugins/experimental/jax_fingerprint/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/** @file

@section license License

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

#pragma once

#include "ts/ts.h"
#include "method.h"

#include <string>
#include <unordered_set>

enum class Mode : int {
OVERWRITE,
KEEP,
APPEND,
};

enum class PluginType : int {
GLOBAL,
REMAP,
};

// This hash function enables looking up the set by a string_view without making a temporary string object.
struct StringHash {
// Enable heterogeneous lookup
using is_transparent = void;

size_t
operator()(std::string_view sv) const
{
return std::hash<std::string_view>{}(sv);
}
};

struct PluginConfig {
PluginType plugin_type = PluginType::GLOBAL;
Mode mode = Mode::OVERWRITE;
struct Method method = {"uninitialized", Method::Type::CONNECTION_BASED, nullptr, nullptr};
std::string header_name = "";
std::string via_header_name = "";
std::string log_filename = "";
int user_arg_index = -1;
TSCont handler = nullptr; // For remap plugin
bool standalone = false;
std::unordered_set<std::string, StringHash, std::equal_to<>> servernames;
TSTextLogObject log_handle = nullptr;
};
72 changes: 72 additions & 0 deletions plugins/experimental/jax_fingerprint/context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/** @file

@section license License

Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "plugin.h"
#include "context.h"

JAxContext::JAxContext(const char *method_name, sockaddr const *s_sockaddr) : _method_name(method_name)
{
_addr[0] = '\0';

if (s_sockaddr == nullptr) {
return;
}

switch (s_sockaddr->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &reinterpret_cast<const sockaddr_in *>(s_sockaddr)->sin_addr, _addr, INET_ADDRSTRLEN);
break;
case AF_INET6:
inet_ntop(AF_INET6, &reinterpret_cast<const sockaddr_in6 *>(s_sockaddr)->sin6_addr, _addr, INET6_ADDRSTRLEN);
break;
case AF_UNIX:
strncpy(_addr, reinterpret_cast<const sockaddr_un *>(s_sockaddr)->sun_path, sizeof(_addr) - 1);
_addr[sizeof(_addr) - 1] = '\0';
break;
default:
break;
}
}

const std::string &
JAxContext::get_fingerprint() const
{
return this->_fingerprint;
}

void
JAxContext::set_fingerprint(const std::string &fingerprint)
{
this->_fingerprint = fingerprint;
Dbg(dbg_ctl, "Fingerprint: %s", this->_fingerprint.c_str());
}

const char *
JAxContext::get_addr() const
{
return this->_addr;
}

const char *
JAxContext::get_method_name() const
{
return this->_method_name;
}
Loading