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 <brian.ner...@gmail.com>
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 } ]

Reply via email to