This is an automated email from the ASF dual-hosted git repository.

lzx404243 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 7ccdbd30ac Added config to support exit on client cert load failure 
(#10958)
7ccdbd30ac is described below

commit 7ccdbd30ac8f03667d734fa40f04f1c250eed2c8
Author: Zhengxi Li <[email protected]>
AuthorDate: Tue Feb 27 10:48:50 2024 -0500

    Added config to support exit on client cert load failure (#10958)
    
    * Added ssl.client.cert.exit_on_load option.
    
    * Added doc for config
    
    * Updated test run names
---
 doc/admin-guide/files/records.yaml.en.rst          |   6 ++
 src/iocore/net/P_SSLConfig.h                       |   1 +
 src/iocore/net/SSLConfig.cc                        |  10 +-
 src/records/RecordsConfig.cc                       |   2 +
 tests/gold_tests/records/gold/full_records.yaml    |   1 +
 .../records/legacy_config/full_records.config      |   1 +
 .../gold_tests/tls/exit_on_cert_load_fail.test.py  | 119 +++++++++++++++++++++
 7 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/doc/admin-guide/files/records.yaml.en.rst 
b/doc/admin-guide/files/records.yaml.en.rst
index 81c02e4fb4..93da31b31d 100644
--- a/doc/admin-guide/files/records.yaml.en.rst
+++ b/doc/admin-guide/files/records.yaml.en.rst
@@ -3992,6 +3992,12 @@ Client-Related Configuration
 
    The filename of SSL client certificate installed on |TS|.
 
+.. ts:cv:: CONFIG proxy.config.ssl.client.cert.exit_on_load_fail INT 0
+
+   By default (``0``), |TS| will start even if problems occur when loading the
+   SSL client certificates.  If true (``1``), SSL client certificate load
+   failures will prevent |TS| from starting.
+
 .. ts:cv:: CONFIG proxy.config.ssl.client.cert.path STRING /config
    :reloadable:
 
diff --git a/src/iocore/net/P_SSLConfig.h b/src/iocore/net/P_SSLConfig.h
index 7870bf9467..0fa6439979 100644
--- a/src/iocore/net/P_SSLConfig.h
+++ b/src/iocore/net/P_SSLConfig.h
@@ -96,6 +96,7 @@ struct SSLConfigParams : public ConfigInfo {
   char *clientKeyPathOnly;
   char *clientCACertFilename;
   char *clientCACertPath;
+  int clientCertExitOnLoadError;
   YamlSNIConfig::Policy verifyServerPolicy;
   YamlSNIConfig::Property verifyServerProperties;
   bool tls_server_connection;
diff --git a/src/iocore/net/SSLConfig.cc b/src/iocore/net/SSLConfig.cc
index df1f2299e2..fc84ce517d 100644
--- a/src/iocore/net/SSLConfig.cc
+++ b/src/iocore/net/SSLConfig.cc
@@ -129,6 +129,7 @@ SSLConfigParams::reset()
   ssl_session_cache_timeout            = 0;
   ssl_session_cache_auto_clear         = 1;
   configExitOnLoadError                = 1;
+  clientCertExitOnLoadError            = 0;
 }
 
 void
@@ -503,6 +504,7 @@ SSLConfigParams::initialize()
   ssl_client_cert_path     = nullptr;
   REC_ReadConfigStringAlloc(ssl_client_cert_filename, 
"proxy.config.ssl.client.cert.filename");
   REC_ReadConfigStringAlloc(ssl_client_cert_path, 
"proxy.config.ssl.client.cert.path");
+  REC_ReadConfigInteger(clientCertExitOnLoadError, 
"proxy.config.ssl.client.cert.exit_on_load_fail");
   set_paths_helper(ssl_client_cert_path, ssl_client_cert_filename, 
&clientCertPathOnly, &clientCertPath);
   ats_free_null(ssl_client_cert_filename);
   ats_free_null(ssl_client_cert_path);
@@ -543,7 +545,13 @@ SSLConfigParams::initialize()
   // can cause HTTP layer to connect using SSL. But only if SSL
   // initialization hasn't failed already.
   client_ctx = this->getCTX(this->clientCertPath, this->clientKeyPath, 
this->clientCACertFilename, this->clientCACertPath);
-  if (!client_ctx) {
+  if (client_ctx) {
+    return;
+  }
+  // Can't get SSL client context.
+  if (this->clientCertExitOnLoadError) {
+    Fatal("Can't initialize the SSL client, HTTPS in remap rules will not 
function");
+  } else {
     SSLError("Can't initialize the SSL client, HTTPS in remap rules will not 
function");
   }
 }
diff --git a/src/records/RecordsConfig.cc b/src/records/RecordsConfig.cc
index e8a7abc8ff..7aedc3fe00 100644
--- a/src/records/RecordsConfig.cc
+++ b/src/records/RecordsConfig.cc
@@ -1134,6 +1134,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.ssl.client.verify.server.properties", 
RECD_STRING, "ALL", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.ssl.client.cert.exit_on_load_fail", RECD_INT, 
"0", RECU_RESTART_TS, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
+  ,
   {RECT_CONFIG, "proxy.config.ssl.client.cert.filename", RECD_STRING, nullptr, 
RECU_DYNAMIC, RR_NULL, RECC_STR, "^[^[:space:]]*$", RECA_NULL}
   ,
   {RECT_CONFIG, "proxy.config.ssl.client.cert.path", RECD_STRING, 
TS_BUILD_SYSCONFDIR, RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
diff --git a/tests/gold_tests/records/gold/full_records.yaml 
b/tests/gold_tests/records/gold/full_records.yaml
index 8978d8d131..8a11d24f78 100644
--- a/tests/gold_tests/records/gold/full_records.yaml
+++ b/tests/gold_tests/records/gold/full_records.yaml
@@ -509,6 +509,7 @@ ts:
         enabled: 1
       alpn_protocols: null
       cert:
+        exit_on_load_fail: 0
         filename: null
       certification_level: 0
       cipher_suite: 
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-ARIA256-GCM-SHA384:ECDHE-ARIA256-GCM-SHA384:DHE-DSS-ARIA256-GCM-SHA384:DHE-RSA-ARIA256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AE
 [...]
diff --git a/tests/gold_tests/records/legacy_config/full_records.config 
b/tests/gold_tests/records/legacy_config/full_records.config
index 2c90d65e25..96376c704f 100644
--- a/tests/gold_tests/records/legacy_config/full_records.config
+++ b/tests/gold_tests/records/legacy_config/full_records.config
@@ -362,6 +362,7 @@ CONFIG proxy.config.ssl.CA.cert.filename STRING nullptr
 CONFIG proxy.config.ssl.client.verify.server.policy STRING ENFORCED
 CONFIG proxy.config.ssl.client.verify.server.properties STRING ALL
 CONFIG proxy.config.ssl.client.cert.filename STRING nullptr
+CONFIG proxy.config.ssl.client.cert.exit_on_load_fail INT 0
 CONFIG proxy.config.ssl.client.private_key.filename STRING nullptr
 CONFIG proxy.config.ssl.client.CA.cert.filename STRING nullptr
 CONFIG proxy.config.ssl.client.sni_policy STRING host
diff --git a/tests/gold_tests/tls/exit_on_cert_load_fail.test.py 
b/tests/gold_tests/tls/exit_on_cert_load_fail.test.py
new file mode 100644
index 0000000000..f578665c48
--- /dev/null
+++ b/tests/gold_tests/tls/exit_on_cert_load_fail.test.py
@@ -0,0 +1,119 @@
+'''
+Test the exit_on_load_fail behavior for the SSL cert loading.
+'''
+#  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
+
+Test.Summary = '''
+Test the exit_on_load_fail behavior for the SSL cert loading.
+'''
+
+
+class Test_exit_on_cert_load_fail:
+    """Configure a test to verify behavior of SSL cert load failures."""
+
+    ts_counter: int = 0
+
+    def __init__(self, name: str, is_testing_server_side: bool, 
enable_exit_on_load=False):
+        """
+        Initialize the test.
+        :param name: The name of the test.
+        :param is_testing_server_side: Whether to test the server side or 
client side cert loading.
+        enable_exit_on_load: Whether to enable the exit_on_load_fail config.
+        """
+        self.name = name
+        self.is_testing_server_side = is_testing_server_side
+        self.enable_exit_on_load = enable_exit_on_load
+
+    def _configure_traffic_server(self, tr: 'TestRun'):
+        """Configure Traffic Server.
+
+        :param tr: The TestRun object to associate the ts process with.
+        """
+        ts = tr.MakeATSProcess(f"ts-{Test_exit_on_cert_load_fail.ts_counter}", 
enable_tls=True)
+        Test_exit_on_cert_load_fail.ts_counter += 1
+        self._ts = ts
+        client_cert_path = 'NULL'
+        enable_exit_on_server_cert_load_failure = 1 if 
self.enable_exit_on_load and self.is_testing_server_side else 0
+        enable_exit_on_client_cert_load_failure = 1 if 
self.enable_exit_on_load and not self.is_testing_server_side else 0
+
+        if not self.is_testing_server_side:
+            # Point to a non-existent cert to force a load failure.
+            client_cert_path = f'{ts.Variables.SSLDir}/non-existent-cert.pem'
+            # Also setup the server certs so that issues are limited to client
+            # cert loading.
+            self._ts.addDefaultSSLFiles()
+        self._ts.Disk.ssl_multicert_config.AddLine('dest_ip=* 
ssl_cert_name=server.pem ssl_key_name=server.key')
+        self._ts.Disk.records_config.update(
+            {
+                'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}',
+                'proxy.config.ssl.server.private_key.path': 
f'{ts.Variables.SSLDir}',
+                'proxy.config.ssl.client.cert.filename': client_cert_path,
+                'proxy.config.ssl.server.multicert.exit_on_load_fail': 
enable_exit_on_server_cert_load_failure,
+                'proxy.config.ssl.client.cert.exit_on_load_fail': 
enable_exit_on_client_cert_load_failure,
+            })
+        # The cert loading happen on startup, so the remap rule is not
+        # triggered.
+        RANDOM_PORT = 12345
+        self._ts.Disk.remap_config.AddLine(f'map / 
https://127.0.0.1:{RANDOM_PORT}/')
+
+    def run(self):
+        """Run the test."""
+        tr = Test.AddTestRun(self.name)
+        self._configure_traffic_server(tr)
+
+        # The client process can be anything.
+        tr.Processes.Default.Command = "echo"
+        tr.Processes.Default.StartAfter(self._ts, 
ready=When.FileExists(self._ts.Disk.diags_log))
+
+        # Override the default exclusion of error log.
+        self._ts.Disk.diags_log.Content = Testers.ContainsExpression("ERROR:", 
"These tests should have error logs.")
+
+        if self.enable_exit_on_load:
+            self._ts.ReturnCode = 70
+            self._ts.Disk.diags_log.Content += Testers.ContainsExpression(
+                "FATAL: ", "Failure loading the certs results in a fatal 
error.")
+            self._ts.Disk.diags_log.Content += Testers.ExcludesExpression(
+                "Traffic Server is fully initialized", "Traffic Server should 
exit upon the load failure.")
+        else:
+            self._ts.ReturnCode = 0
+            self._ts.Disk.diags_log.Content += Testers.ContainsExpression(
+                "Traffic Server is fully initialized", "Traffic Server should 
start up successfully.")
+
+        if self.is_testing_server_side:
+            self._ts.Disk.diags_log.Content += Testers.ContainsExpression(
+                "ERROR:.*failed to load", "Verify that there is a cert loading 
issue.")
+        else:
+            self._ts.Disk.diags_log.Content += Testers.ContainsExpression(
+                "ERROR: failed to access cert", "Verify that there is a cert 
loading issue.")
+            self._ts.Disk.diags_log.Content += Testers.ContainsExpression(
+                "Can't initialize the SSL client, HTTPS in remap rules will 
not function",
+                "There should be an error loading the cert.")
+
+
+# Test server cert loading.
+Test_exit_on_cert_load_fail(
+    "load server cert with exit on load disabled", 
is_testing_server_side=True, enable_exit_on_load=False).run()
+Test_exit_on_cert_load_fail(
+    "load server cert with exit on load enabled", is_testing_server_side=True, 
enable_exit_on_load=True).run()
+
+# Test client cert loading.
+Test_exit_on_cert_load_fail(
+    "load client cert with exit on load disabled", 
is_testing_server_side=False, enable_exit_on_load=False).run()
+Test_exit_on_cert_load_fail(
+    "load client cert with exit on load enabled", 
is_testing_server_side=False, enable_exit_on_load=True).run()

Reply via email to