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 5387ef7bab ja3_fingerprint: autest and README update. (#10871)
5387ef7bab is described below
commit 5387ef7bab213d0876971a653d2396899282bc2f
Author: Brian Neradt <[email protected]>
AuthorDate: Wed Nov 29 16:16:14 2023 -0600
ja3_fingerprint: autest and README update. (#10871)
This adds an autest for ja3_fingerprint and corrects a typo in the
README for the plugin.
---
plugins/ja3_fingerprint/README | 26 +++-
.../ja3_fingerprint/ja3_fingerprint.test.py | 169 +++++++++++++++++++++
.../ja3_fingerprint_global.replay.yaml | 105 +++++++++++++
.../ja3_fingerprint_remap.replay.yaml | 109 +++++++++++++
4 files changed, 401 insertions(+), 8 deletions(-)
diff --git a/plugins/ja3_fingerprint/README b/plugins/ja3_fingerprint/README
index fdb85c7826..491a68bdff 100644
--- a/plugins/ja3_fingerprint/README
+++ b/plugins/ja3_fingerprint/README
@@ -12,15 +12,25 @@ The log file format is as follows:
[time] [client IP] [JA3 string] [MD5 Hash]
-2. plugin.config
-In plugin.config, supply name of the plugin and options
-Example: ja3_fingerprint.so --ja3raw --ja3log
-Add flag --ja3raw if `X-JA3-Raw` is desired other than only `X-JA3-Sig`.
+2. Plugin Configuration Options
+The following optional arguments can be used to configure the plugin's
behavior:
+
+Add flag --ja3raw if `X-JA3-Raw` is desired in addition to `X-JA3-Sig`.
Add flag --ja3log if local logging in standard logging directory is desired.
-3. remap.config
-This plugin can also be used as a remap plugin. For each remap rule, add
plugin and parameter field. For example:
-map http://from.com http://to.com @plugin=ja3_fingerprint.so
[@pparam=--ja3raw] [@pparam=--ja3log]
+3. plugin.config
+In plugin.config, supply name of the plugin and any desired options. For
example:
+
+ja3_fingerprint.so --ja3raw --ja3log
+
+4. remap.config
+This plugin can alternatively be used as a remap plugin. For each remap rule,
+add plugin and parameter field. For example:
+
+map http://from.com http://to.com @plugin=ja3_fingerprint.so @pparam=--ja3raw
@pparam=--ja3log
+
+The plugin is not designed to function as both a global and remap plugin, so
+choose one configuration or the other.
-4. Requirement
+5. Requirement
Won't compile against OpenSSL 1.1.0 due to APIs and opaque structures.
diff --git
a/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
new file mode 100644
index 0000000000..89c8514ee8
--- /dev/null
+++ b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint.test.py
@@ -0,0 +1,169 @@
+'''
+Verify the behavior of the JA3 fingerprint plugin.
+'''
+# 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.
+
+import os
+import re
+
+Test.Summary = __doc__
+
+
+class JA3FingerprintTest:
+ """Verify the behavior of the JA3 fingerprint plugin."""
+
+ _dns_counter: int = 0
+ _server_counter: int = 0
+ _ts_counter: int = 0
+ _client_counter: int = 0
+
+ def __init__(self, test_remap: bool) -> None:
+ """Configure the test processes in preparation for the TestRun.
+
+ :param test_remap: Whether to configure the plugin as a remap plugin
+ instead of as a global plugin.
+ """
+ self._test_remap = test_remap
+ if test_remap:
+ self._replay_file = 'ja3_fingerprint_remap.replay.yaml'
+ else:
+ self._replay_file = 'ja3_fingerprint_global.replay.yaml'
+
+ tr = Test.AddTestRun('Testing ja3_fingerprint plugin.')
+ self._configure_dns(tr)
+ self._configure_server(tr)
+ self._configure_trafficserver()
+ self._configure_client(tr)
+ self._await_ja3log()
+
+ def _configure_dns(self, tr: 'TestRun') -> None:
+ """Configure a nameserver for the test.
+
+ :param tr: The TestRun to add the nameserver to.
+ """
+ name = f'dns{self._dns_counter}'
+ self._dns = tr.MakeDNServer(name, default='127.0.0.1')
+ JA3FingerprintTest._dns_counter += 1
+
+ def _configure_server(self, tr: 'TestRun') -> None:
+ """Configure the server to be used in the test.
+
+ :param tr: The TestRun to add the server to.
+ """
+ name = f'server{self._server_counter}'
+ self._server = tr.AddVerifierServerProcess(name, self._replay_file)
+ 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.")
+
+ def _configure_trafficserver(self) -> None:
+ """Configure Traffic Server to be used in the test."""
+ # Associate ATS with the Test so that metrics can be verified.
+ name = f'ts{self._ts_counter}'
+ self._ts = Test.MakeATSProcess(name, enable_cache=False,
enable_tls=True)
+ JA3FingerprintTest._ts_counter += 1
+ self._ts.addDefaultSSLFiles()
+ self._ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+ )
+ server_port = self._server.Variables.https_port
+ self._ts.Disk.remap_config.AddLine(
+ f'map https://https.server.com
https://https.backend.com:{server_port}'
+ )
+
+ if self._test_remap:
+ self._ts.Disk.remap_config.AddLine(
+ f'map https://http2.server.com
https://http2.backend.com:{server_port} '
+ '@plugin=ja3_fingerprint.so @pparam=--ja3log'
+ )
+ else:
+ self._ts.Disk.plugin_config.AddLine('ja3_fingerprint.so --ja3log
--ja3raw')
+ self._ts.Disk.remap_config.AddLine(
+ f'map https://http2.server.com
https://http2.backend.com:{server_port}'
+ )
+
+ self._ts.Disk.records_config.update({
+ 'proxy.config.ssl.server.cert.path': self._ts.Variables.SSLDir,
+ 'proxy.config.ssl.server.private_key.path':
self._ts.Variables.SSLDir,
+ '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.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'http|ja3_fingerprint',
+ })
+
+ ja3log_path = os.path.join(self._ts.Variables.LOGDIR,
"ja3_fingerprint.log")
+ self._ts.Disk.File(ja3log_path, id='ja3_log')
+
+ if self._test_remap:
+ # Only the http2 request should be logged because only that request
+ # had the plugin configured for that remap rule.
+ regex = r'(.*JA3.*MD5){1}'
+ else:
+ regex = r'(.*JA3.*MD5){2}'
+
+ self._ts.Disk.ja3_log.Content += Testers.ContainsExpression(
+ regex,
+ "Verify the JA3 log contains a JA3 line.",
+ reflags=re.MULTILINE | re.DOTALL)
+
+ def _configure_client(self, tr: 'TestRun') -> None:
+ """Configure the TestRun.
+
+ :param tr: The TestRun to add the client to.
+ """
+ name = f'client{self._client_counter}'
+ p = tr.AddVerifierClientProcess(
+ name,
+ self._replay_file,
+ http_ports=[self._ts.Variables.port],
+ https_ports=[self._ts.Variables.ssl_port])
+ JA3FingerprintTest._client_counter += 1
+
+ p.StartBefore(self._dns)
+ p.StartBefore(self._server)
+ p.StartBefore(self._ts)
+ tr.StillRunningAfter = self._ts
+
+ p.Streams.All += Testers.ContainsExpression(
+ "https-response",
+ "Verify the HTTPS response was received.")
+ p.Streams.All += Testers.ContainsExpression(
+ "http2-response",
+ "Verify the HTTP/2 response was received.")
+
+ def _await_ja3log(self) -> None:
+ """Await the creation of the JA3 log."""
+ tr = Test.AddTestRun('Await the contents of the JA3 log.')
+
+ waiter = tr.Processes.Process('waiter', 'sleep 30')
+ ja3_path = self._ts.Disk.ja3_log.AbsPath
+ waiter.Ready = When.FileContains(ja3_path, "JA3")
+
+ p = tr.Processes.Default
+ p.Command = f'echo await {ja3_path} creation'
+ p.StartBefore(waiter)
+
+
+JA3FingerprintTest(test_remap=False)
+JA3FingerprintTest(test_remap=True)
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
new file mode 100644
index 0000000000..e14eb417e2
--- /dev/null
+++
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_global.replay.yaml
@@ -0,0 +1,105 @@
+# 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.
+
+meta:
+ version: "1.0"
+
+sessions:
+
+- protocol:
+ - name: tls
+ sni: https.server.com
+ - name: tcp
+ - name: ip
+
+ transactions:
+
+ - client-request:
+ method: GET
+ url: /some/path/https
+ version: '1.1'
+ headers:
+ fields:
+ - [ Host, https.server.com ]
+ - [ Content-Length, 0 ]
+ - [ X-Request, https-request ]
+ - [ uuid, https-request ]
+
+ proxy-request:
+ headers:
+ fields:
+ - [ X-Request, { value: 'https-request', as: equal } ]
+ - [ X-JA3-Sig, { as: present } ]
+ - [ X-JA3-Raw, { as: present } ]
+
+ server-response:
+ status: 200
+ reason: OK
+ headers:
+ fields:
+ - [ Content-Length, 36 ]
+ - [ X-Response, https-response ]
+ - [ Connection, close ]
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Response, { value: 'https-response', as: equal } ]
+
+- protocol:
+ - name: http
+ version: 2
+ - name: tls
+ sni: http2.server.com
+ - name: tcp
+ - name: ip
+
+ transactions:
+
+ - client-request:
+ headers:
+ fields:
+ - [ :method, POST ]
+ - [ :scheme, https ]
+ - [ :authority, http2.server.com ]
+ - [ :path, /some/path/http2 ]
+ - [ content-type, image/jpeg ]
+ - [ uuid, http2-request ]
+ - [ x-request, http2-request ]
+ content:
+ size: 399
+
+ proxy-request:
+ headers:
+ fields:
+ - [ x-request, { value: 'http2-request', as: equal } ]
+ - [ X-JA3-Sig, { as: present } ]
+ - [ X-JA3-Raw, { as: present } ]
+
+ server-response:
+ headers:
+ fields:
+ - [ :status, 200 ]
+ - [ Content-Length, 36 ]
+ - [ x-response, http2-response ]
+ - [ Connection, close ]
+
+ proxy-response:
+ headers:
+ fields:
+ - [ :status, { value: 200, as: equal } ]
+ - [ x-response, { value: 'http2-response', as: equal } ]
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
new file mode 100644
index 0000000000..6123304bc9
--- /dev/null
+++
b/tests/gold_tests/pluginTest/ja3_fingerprint/ja3_fingerprint_remap.replay.yaml
@@ -0,0 +1,109 @@
+# 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.
+
+meta:
+ version: "1.0"
+
+sessions:
+
+- protocol:
+ - name: tls
+ sni: https.server.com
+ - name: tcp
+ - name: ip
+
+ transactions:
+
+ - client-request:
+ method: GET
+ url: /some/path/https
+ version: '1.1'
+ headers:
+ fields:
+ - [ Host, https.server.com ]
+ - [ Content-Length, 0 ]
+ - [ X-Request, https-request ]
+ - [ uuid, https-request ]
+
+ # The ja3_fingerprint plugin is not configured as a remap plugin for
+ # https.server.com.
+ proxy-request:
+ headers:
+ fields:
+ - [ X-Request, { value: 'https-request', as: equal } ]
+ - [ X-JA3-Sig, { as: absent } ]
+ - [ X-JA3-Raw, { as: absent } ]
+
+ server-response:
+ status: 200
+ reason: OK
+ headers:
+ fields:
+ - [ Content-Length, 36 ]
+ - [ X-Response, https-response ]
+ - [ Connection, close ]
+
+ proxy-response:
+ status: 200
+ headers:
+ fields:
+ - [ X-Response, { value: 'https-response', as: equal } ]
+
+- protocol:
+ - name: http
+ version: 2
+ - name: tls
+ sni: http2.server.com
+ - name: tcp
+ - name: ip
+
+ transactions:
+
+ - client-request:
+ headers:
+ fields:
+ - [ :method, POST ]
+ - [ :scheme, https ]
+ - [ :authority, http2.server.com ]
+ - [ :path, /some/path/http2 ]
+ - [ content-type, image/jpeg ]
+ - [ uuid, http2-request ]
+ - [ x-request, http2-request ]
+ content:
+ size: 399
+
+ # The ja3_fingerprint plugin is configured as a remap plugin for
+ # http2.server.com, but without --ja3raw configured for it.
+ proxy-request:
+ headers:
+ fields:
+ - [ x-request, { value: 'http2-request', as: equal } ]
+ - [ X-JA3-Sig, { as: present } ]
+ - [ X-JA3-Raw, { as: absent } ]
+
+ server-response:
+ headers:
+ fields:
+ - [ :status, 200 ]
+ - [ Content-Length, 36 ]
+ - [ x-response, http2-response ]
+ - [ Connection, close ]
+
+ proxy-response:
+ headers:
+ fields:
+ - [ :status, { value: 200, as: equal } ]
+ - [ x-response, { value: 'http2-response', as: equal } ]