randall commented on a change in pull request #6609:
URL: https://github.com/apache/trafficserver/pull/6609#discussion_r419560764



##########
File path: doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst
##########
@@ -106,6 +106,14 @@ Types
       Invoked with the event :c:data:`TS_EVENT_LIFECYCLE_TASK_THREADS_READY` 
and ``NULL``
       data.
 
+   .. cpp:enumerator:: TS_LIFECYCLE_SSL_SECRET_HOOK
+
+      Called before the data for the certificate or key is loaded.  The data 
argument to the callback is a pointer to a :type:`TSSecretID` which
+      contains a pointer to the name of the certificate or key and the 
relavent version if applicable.

Review comment:
       relevant

##########
File path: doc/developer-guide/api/functions/TSSslSecret.en.rst
##########
@@ -0,0 +1,60 @@
+.. 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
+
+.. default-domain:: c
+
+TSSslSecretSet
+**************
+
+Set the data associated with a secret name specified in the config.
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+    #include <ts/ts.h>
+
+.. function:: TSReturnCode TSSslSecretSet(const char * secret_name, const char 
* secret_data, int secret_data_len)
+
+Description
+===========
+
+:func:`TSSslSecretSet` updates the current secret map. Generally the secret 
name corresponds to the name of a certificate or a key.
+Future creation of SSL_CTX objects that use the secret will use the newly 
specified data. It can be useful to call this function
+from the :data:`TS_LIFECYCLE_SSL_SECRET_HOOK`.
+
+TSSslSecretGet
+**************
+
+Get the data associated with a secret name specified in the config.
+
+Synopsis
+========
+
+.. code-block:: cpp
+
+    #include <ts/ts.h>
+
+.. function:: TSReturnCode TSSslSecretGet(const char * secret_name, const char 
**secret_data_return, int *secret_data_len)
+
+Description
+===========
+
+:func:`TSSslSecretGet` fetchs the named secret from the current secret map. 
TS_ERROR is returned if there is no entry for the secret.

Review comment:
       fetches

##########
File path: tests/gold_tests/tls/tls_check_dual_cert_selection_plugin.test.py
##########
@@ -0,0 +1,133 @@
+'''
+'''
+#  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 ATS offering both RSA and EC certificates loaded via plugin
+'''
+
+Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server", ssl=True)
+dns = Test.MakeDNServer("dns")
+
+request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": 
"1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed-foo-ec.pem")
+ts.addSSLfile("ssl/signed-foo-ec.key")
+ts.addSSLfile("ssl/signed-san.pem")
+ts.addSSLfile("ssl/signed-san.key")
+ts.addSSLfile("ssl/signed-san-ec.pem")
+ts.addSSLfile("ssl/signed-san-ec.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / https://foo.com:{1}'.format(ts.Variables.ssl_port, 
server.Variables.SSL_Port))
+
+ts.Disk.ssl_multicert_config.AddLines([
+    'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem 
ssl_key_name=signed-foo-ec.key,signed-foo.key',
+    'ssl_cert_name=signed-san-ec.pem,signed-san.pem 
ssl_key_name=signed-san-ec.key,signed-san.key',
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+])
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+# Case 1, global config policy=permissive properties=signature
+#         override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.dns.resolv_conf': 'NULL',
+    'proxy.config.diags.debug.tags':  'ssl_secret_load_test',
+    'proxy.config.diags.debug.enabled': 1
+})
+
+dns.addRecords(records={"foo.com.": ["127.0.0.1"]})
+dns.addRecords(records={"bar.com.": ["127.0.0.1"]})
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for foo should return EC cert")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(dns)
+tr.Processes.Default.StartBefore(Test.Processes.ts, 
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for one.com should return EC cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("rsa.com only in rsa cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
rsa.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");

Review comment:
       trailing semicolon

##########
File path: tests/gold_tests/tls/tls_check_dual_cert_selection_plugin.test.py
##########
@@ -0,0 +1,133 @@
+'''
+'''
+#  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 ATS offering both RSA and EC certificates loaded via plugin
+'''
+
+Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server", ssl=True)
+dns = Test.MakeDNServer("dns")
+
+request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": 
"1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed-foo-ec.pem")
+ts.addSSLfile("ssl/signed-foo-ec.key")
+ts.addSSLfile("ssl/signed-san.pem")
+ts.addSSLfile("ssl/signed-san.key")
+ts.addSSLfile("ssl/signed-san-ec.pem")
+ts.addSSLfile("ssl/signed-san-ec.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / https://foo.com:{1}'.format(ts.Variables.ssl_port, 
server.Variables.SSL_Port))
+
+ts.Disk.ssl_multicert_config.AddLines([
+    'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem 
ssl_key_name=signed-foo-ec.key,signed-foo.key',
+    'ssl_cert_name=signed-san-ec.pem,signed-san.pem 
ssl_key_name=signed-san-ec.key,signed-san.key',
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+])
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+# Case 1, global config policy=permissive properties=signature
+#         override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.dns.resolv_conf': 'NULL',
+    'proxy.config.diags.debug.tags':  'ssl_secret_load_test',
+    'proxy.config.diags.debug.enabled': 1
+})
+
+dns.addRecords(records={"foo.com.": ["127.0.0.1"]})
+dns.addRecords(records={"bar.com.": ["127.0.0.1"]})
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for foo should return EC cert")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(dns)
+tr.Processes.Default.StartBefore(Test.Processes.ts, 
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for one.com should return EC cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("rsa.com only in rsa cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
rsa.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a EC cert
+tr = Test.AddTestRun("ec.com only in ec cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername ec.com 
-connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");

Review comment:
       trailing semicolon

##########
File path: tests/gold_tests/tls/tls_client_cert_plugin.test.py
##########
@@ -0,0 +1,303 @@
+'''
+Test offering client cert to origin, but using plugin for 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 subprocess
+import os
+
+Test.Summary = '''
+Test offering client cert to origin, but using plugin for cert loading
+'''
+
+ts = Test.MakeATSProcess("ts", command="traffic_manager", select_ports=True)
+cafile = "{0}/signer.pem".format(Test.RunDirectory)
+cafile2 = "{0}/signer2.pem".format(Test.RunDirectory)
+# --clientverify: "" empty string because microserver does store_true for 
argparse, but options is a dictionary
+server = Test.MakeOriginServer("server", ssl=True, options = { "--clientCA": 
cafile, "--clientverify": ""}, 
clientcert="{0}/signed-foo.pem".format(Test.RunDirectory), 
clientkey="{0}/signed-foo.key".format(Test.RunDirectory))
+server2 = Test.MakeOriginServer("server2", ssl=True, options = { "--clientCA": 
cafile2, "--clientverify": ""}, 
clientcert="{0}/signed2-bar.pem".format(Test.RunDirectory), 
clientkey="{0}/signed-bar.key".format(Test.RunDirectory))
+server3 = Test.MakeOriginServer("server3")
+server.Setup.Copy("ssl/signer.pem")
+server.Setup.Copy("ssl/signer2.pem")
+server.Setup.Copy("ssl/signed-foo.pem")
+server.Setup.Copy("ssl/signed-foo.key")
+server.Setup.Copy("ssl/signed2-foo.pem")
+server.Setup.Copy("ssl/signed2-bar.pem")
+server.Setup.Copy("ssl/signed-bar.key")
+server2.Setup.Copy("ssl/signer.pem")
+server2.Setup.Copy("ssl/signer2.pem")
+server2.Setup.Copy("ssl/signed-foo.pem")
+server2.Setup.Copy("ssl/signed-foo.key")
+server2.Setup.Copy("ssl/signed2-foo.pem")
+server2.Setup.Copy("ssl/signed2-bar.pem")
+server2.Setup.Copy("ssl/signed-bar.key")
+
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: bar.com\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+#
+# Certs and keys loaded into the ts/ssl directory, but the paths in the
+# configs omit the ssl subdirectory.  The ssl_secret_load_test plugin adds 
this back in
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed2-foo.pem")
+ts.addSSLfile("ssl/signed-bar.pem")
+ts.addSSLfile("ssl/signed2-bar.pem")
+ts.addSSLfile("ssl/signed-bar.key")
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+ts.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 1,
+    'proxy.config.diags.debug.tags': 'ssl_secret_load_test|ssl',
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.verify.server':  0,
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.ssl.client.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.cert.filename': 'signed-foo.pem',
+    'proxy.config.ssl.client.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.private_key.filename': 'signed-foo.key',
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.url_remap.pristine_host_hdr' : 1,
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+    'map /case1 https://127.0.0.1:{0}/'.format(server.Variables.SSL_Port)
+)
+ts.Disk.remap_config.AddLine(
+    'map /case2 https://127.0.0.1:{0}/'.format(server2.Variables.SSL_Port)
+)
+
+ts.Disk.sni_yaml.AddLine(
+    'sni:')
+ts.Disk.sni_yaml.AddLine(
+    '- fqdn: bar.com')
+ts.Disk.sni_yaml.AddLine(
+    '  client_cert: {0}/../signed2-bar.pem'.format(ts.Variables.SSLDir))
+ts.Disk.sni_yaml.AddLine(
+    '  client_key: {0}/../signed-bar.key'.format(ts.Variables.SSLDir))
+
+
+# Should succeed
+tr = Test.AddTestRun("Connect with first client cert to first server")
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(server2)
+tr.StillRunningAfter = ts
+tr.StillRunningAfter = server
+tr.StillRunningAfter = server2
+tr.Processes.Default.Command = "curl -H host:example.com  
http://127.0.0.1:{0}/case1".format(ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not 
Connect", "Check response")
+
+#Should fail
+trfail = Test.AddTestRun("Connect with first client cert to second server")
+trfail.StillRunningAfter = ts
+trfail.StillRunningAfter = server
+trfail.StillRunningAfter = server2
+trfail.Processes.Default.Command = 'curl -H host:example.com  
http://127.0.0.1:{0}/case2'.format(ts.Variables.port)
+trfail.Processes.Default.ReturnCode = 0
+trfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could 
Not Connect", "Check response")
+
+# Should succeed
+trbar = Test.AddTestRun("Connect with signed2 bar to second server")
+trbar.StillRunningAfter = ts
+trbar.StillRunningAfter = server
+trbar.StillRunningAfter = server2
+trbar.Processes.Default.Command = "curl -H host:bar.com  
http://127.0.0.1:{0}/case2".format(ts.Variables.port)
+trbar.Processes.Default.ReturnCode = 0
+trbar.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not 
Connect", "Check response")
+
+#Should fail
+trbarfail = Test.AddTestRun("Connect with signed2 bar cert to first server")
+trbarfail.StillRunningAfter = ts
+trbarfail.StillRunningAfter = server
+trbarfail.StillRunningAfter = server2
+trbarfail.Processes.Default.Command = 'curl -H host:bar.com  
http://127.0.0.1:{0}/case1'.format(ts.Variables.port)
+trbarfail.Processes.Default.ReturnCode = 0
+trbarfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could 
Not Connect", "Check response")
+
+tr2 = Test.AddTestRun("Update config files")
+# Update the SNI config
+snipath = ts.Disk.sni_yaml.AbsPath
+recordspath = ts.Disk.records_config.AbsPath
+tr2.Disk.File(snipath, id = "sni_yaml", typename="ats:config"),
+tr2.Disk.sni_yaml.AddLine(
+    'sni:')
+tr2.Disk.sni_yaml.AddLine(
+    '- fqdn: bar.com')
+tr2.Disk.sni_yaml.AddLine(
+    '  client_cert: {0}/../signed-bar.pem'.format(ts.Variables.SSLDir))
+tr2.Disk.sni_yaml.AddLine(
+    '  client_key: {0}/../signed-bar.key'.format(ts.Variables.SSLDir))
+# recreate the records.config with the cert filename changed
+tr2.Disk.File(recordspath, id = "records_config", 
typename="ats:config:records"),
+tr2.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.verify.server':  0,
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.ssl.client.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.cert.filename': 'signed2-foo.pem',
+    'proxy.config.ssl.client.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.private_key.filename': 'signed-foo.key',
+    'proxy.config.url_remap.pristine_host_hdr' : 1,
+    'proxy.config.diags.debug.enabled': 1,
+    'proxy.config.diags.debug.tags': 'ssl_secret_load_test|ssl',
+})
+tr2.StillRunningAfter = ts
+tr2.StillRunningAfter = server
+tr2.StillRunningAfter = server2
+tr2.Processes.Default.Command = 'echo Updated configs'
+# Need to copy over the environment so traffic_ctl knows where to find the 
unix domain socket
+tr2.Processes.Default.Env = ts.Env
+tr2.Processes.Default.ReturnCode = 0
+
+# Parking this as a ready tester on a meaningless process
+# Stall the test runs until the sni reload has completed
+# At that point the new sni settings are ready to go
+def sni_reload_done(tsenv):

Review comment:
       indentation here is not multiple of four

##########
File path: tests/gold_tests/tls/tls_check_dual_cert_selection_plugin.test.py
##########
@@ -0,0 +1,133 @@
+'''
+'''
+#  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 ATS offering both RSA and EC certificates loaded via plugin
+'''
+
+Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server", ssl=True)
+dns = Test.MakeDNServer("dns")
+
+request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": 
"1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed-foo-ec.pem")
+ts.addSSLfile("ssl/signed-foo-ec.key")
+ts.addSSLfile("ssl/signed-san.pem")
+ts.addSSLfile("ssl/signed-san.key")
+ts.addSSLfile("ssl/signed-san-ec.pem")
+ts.addSSLfile("ssl/signed-san-ec.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / https://foo.com:{1}'.format(ts.Variables.ssl_port, 
server.Variables.SSL_Port))
+
+ts.Disk.ssl_multicert_config.AddLines([
+    'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem 
ssl_key_name=signed-foo-ec.key,signed-foo.key',
+    'ssl_cert_name=signed-san-ec.pem,signed-san.pem 
ssl_key_name=signed-san-ec.key,signed-san.key',
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+])
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+# Case 1, global config policy=permissive properties=signature
+#         override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.dns.resolv_conf': 'NULL',
+    'proxy.config.diags.debug.tags':  'ssl_secret_load_test',
+    'proxy.config.diags.debug.enabled': 1
+})
+
+dns.addRecords(records={"foo.com.": ["127.0.0.1"]})
+dns.addRecords(records={"bar.com.": ["127.0.0.1"]})
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for foo should return EC cert")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(dns)
+tr.Processes.Default.StartBefore(Test.Processes.ts, 
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for one.com should return EC cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");

Review comment:
       trailing semicolon

##########
File path: tests/gold_tests/tls/tls_client_cert_override_plugin.test.py
##########
@@ -0,0 +1,184 @@
+#  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.
+
+Test.Summary = '''
+Test conf_remp to specify different client certificates to offer to the 
origin.  Loading certs/keys via plugin.
+'''
+
+import os
+import subprocess
+
+ts = Test.MakeATSProcess("ts", command="traffic_manager", select_ports=True)
+cafile = "{0}/signer.pem".format(Test.RunDirectory)
+cafile2 = "{0}/signer2.pem".format(Test.RunDirectory)
+server = Test.MakeOriginServer("server", ssl=True, options = { "--clientCA": 
cafile, "--clientverify": ""}, 
clientcert="{0}/signed-foo.pem".format(Test.RunDirectory), 
clientkey="{0}/signed-foo.key".format(Test.RunDirectory))
+server2 = Test.MakeOriginServer("server2", ssl=True, options = { "--clientCA": 
cafile2, "--clientverify": ""}, 
clientcert="{0}/signed2-bar.pem".format(Test.RunDirectory), 
clientkey="{0}/signed-bar.key".format(Test.RunDirectory))
+server3 = Test.MakeOriginServer("server3")
+server.Setup.Copy("ssl/signer.pem")
+server.Setup.Copy("ssl/signer2.pem")
+server.Setup.Copy("ssl/signed-foo.pem")
+server.Setup.Copy("ssl/signed-foo.key")
+server.Setup.Copy("ssl/signed2-foo.pem")
+server.Setup.Copy("ssl/signed2-bar.pem")
+server.Setup.Copy("ssl/signed-bar.key")
+server2.Setup.Copy("ssl/signer.pem")
+server2.Setup.Copy("ssl/signer2.pem")
+server2.Setup.Copy("ssl/signed-foo.pem")
+server2.Setup.Copy("ssl/signed-foo.key")
+server2.Setup.Copy("ssl/signed2-foo.pem")
+server2.Setup.Copy("ssl/signed2-bar.pem")
+server2.Setup.Copy("ssl/signed-bar.key")
+
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+request_header = {"headers": "GET / HTTP/1.1\r\nHost: bar.com\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed2-foo.pem")
+ts.addSSLfile("ssl/signed-bar.pem")
+ts.addSSLfile("ssl/signed2-bar.pem")
+ts.addSSLfile("ssl/signed-bar.key")
+
+ts.Disk.sni_yaml.AddLine('sni:')
+ts.Disk.sni_yaml.AddLine('- fqdn: random')
+ts.Disk.sni_yaml.AddLine('  verify_server_properties: NONE')
+snipath = ts.Disk.sni_yaml.AbsPath
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.verify.server':  0,
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.ssl.client.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.cert.filename': 'signed-foo.pem',
+    'proxy.config.ssl.client.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.client.private_key.filename': 'signed-foo.key',
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.url_remap.pristine_host_hdr' : 1,
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+    'map /case1 https://127.0.0.1:{0}/ @plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.cert.filename={1} plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.private_key.filename={2}'.format(server.Variables.SSL_Port,
 "signed-foo.pem", "signed-foo.key")
+)
+ts.Disk.remap_config.AddLine(
+    'map /badcase1 https://127.0.0.1:{0}/ @plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.cert.filename={1} plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.private_key.filename={2}'.format(server.Variables.SSL_Port,
 "signed2-foo.pem", "signed-foo.key")
+)
+ts.Disk.remap_config.AddLine(
+    'map /case2 https://127.0.0.1:{0}/ @plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.cert.filename={1} plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.private_key.filename={2}'.format(server2.Variables.SSL_Port,
 "signed2-foo.pem", "signed-foo.key")
+)
+ts.Disk.remap_config.AddLine(
+    'map /badcase2 https://127.0.0.1:{0}/ @plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.cert.filename={1} plugin=conf_remap.so 
@pparam=proxy.config.ssl.client.private_key.filename={2}'.format(server2.Variables.SSL_Port,
 "signed-foo.pem", "signed-foo.key")
+)
+
+# Should succeed
+tr = Test.AddTestRun("Connect with correct client cert to first server")
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(server2)
+tr.StillRunningAfter = ts
+tr.StillRunningAfter = server
+tr.StillRunningAfter = server2
+tr.Processes.Default.Command = "curl -H host:example.com  
http://127.0.0.1:{0}/case1".format(ts.Variables.port)
+tr.Processes.Default.ReturnCode = 0
+tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not 
Connect", "Check response")
+
+#Should fail
+trfail = Test.AddTestRun("Connect with bad client cert to first server")
+trfail.StillRunningAfter = ts
+trfail.StillRunningAfter = server
+trfail.StillRunningAfter = server2
+trfail.Processes.Default.Command = 'curl -H host:example.com  
http://127.0.0.1:{0}/badcase1'.format(ts.Variables.port)
+trfail.Processes.Default.ReturnCode = 0
+trfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could 
Not Connect", "Check response")
+
+# Should succeed
+trbar = Test.AddTestRun("Connect with correct client cert to second server")
+trbar.StillRunningAfter = ts
+trbar.StillRunningAfter = server
+trbar.StillRunningAfter = server2
+trbar.Processes.Default.Command = "curl -H host:bar.com  
http://127.0.0.1:{0}/case2".format(ts.Variables.port)
+trbar.Processes.Default.ReturnCode = 0
+trbar.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not 
Connect", "Check response")
+
+#Should fail
+trbarfail = Test.AddTestRun("Connect with bad client cert to second server")
+trbarfail.StillRunningAfter = ts
+trbarfail.StillRunningAfter = server
+trbarfail.StillRunningAfter = server2
+trbarfail.Processes.Default.Command = 'curl -H host:bar.com  
http://127.0.0.1:{0}/badcase2'.format(ts.Variables.port)
+trbarfail.Processes.Default.ReturnCode = 0
+trbarfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could 
Not Connect", "Check response")
+
+# Test the case of updating certificate contents without changing file name.
+trupdate = Test.AddTestRun("Update client cert file in place")
+trupdate.StillRunningAfter = ts
+trupdate.StillRunningAfter = server
+trupdate.StillRunningAfter = server2
+# Make a meaningless config change on the path so the records.config reload 
logic will trigger
+trupdate.Setup.CopyAs("ssl/signed2-bar.pem", ".", 
"{0}/signed-bar.pem".format(ts.Variables.SSLDir))
+# in the config/ssl directory for records.config
+trupdate.Setup.CopyAs("ssl/signed-foo.pem", ".", 
"{0}/signed2-foo.pem".format(ts.Variables.SSLDir))
+trupdate.Processes.Default.Command = 'traffic_ctl config set 
proxy.config.ssl.client.cert.path {0}/; touch 
{1}'.format(ts.Variables.SSLDir,snipath)
+# Need to copy over the environment so traffic_ctl knows where to find the 
unix domain socket
+trupdate.Processes.Default.Env = ts.Env
+trupdate.Processes.Default.ReturnCode = 0
+
+
+# Parking this as a ready tester on a meaningless process
+# Stall the test runs until the sni reload has completed
+# At that point the new sni settings are ready to go
+def sni_reload_done(tsenv):
+  def done_reload(process, hasRunFor, **kw):

Review comment:
       Indentation here is not multiple of four

##########
File path: tests/gold_tests/tls/tls_check_dual_cert_selection_plugin.test.py
##########
@@ -0,0 +1,133 @@
+'''
+'''
+#  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 ATS offering both RSA and EC certificates loaded via plugin
+'''
+
+Test.SkipUnless(Condition.HasOpenSSLVersion('1.1.1'))
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
+server = Test.MakeOriginServer("server", ssl=True)
+dns = Test.MakeDNServer("dns")
+
+request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": 
"1469733493.993", "body": ""}
+response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", 
"timestamp": "1469733493.993", "body": ""}
+server.addResponse("sessionlog.json", request_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/signed-foo-ec.pem")
+ts.addSSLfile("ssl/signed-foo-ec.key")
+ts.addSSLfile("ssl/signed-san.pem")
+ts.addSSLfile("ssl/signed-san.key")
+ts.addSSLfile("ssl/signed-san-ec.pem")
+ts.addSSLfile("ssl/signed-san-ec.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Disk.remap_config.AddLine(
+    'map / https://foo.com:{1}'.format(ts.Variables.ssl_port, 
server.Variables.SSL_Port))
+
+ts.Disk.ssl_multicert_config.AddLines([
+    'ssl_cert_name=signed-foo-ec.pem,signed-foo.pem 
ssl_key_name=signed-foo-ec.key,signed-foo.key',
+    'ssl_cert_name=signed-san-ec.pem,signed-san.pem 
ssl_key_name=signed-san-ec.key,signed-san.key',
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+])
+
+Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 
'ssl_secret_load_test.cc'), ts)
+
+# Case 1, global config policy=permissive properties=signature
+#         override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+    'proxy.config.ssl.server.cert.path': '{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}/../'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
+    'proxy.config.exec_thread.autoconfig.scale': 1.0,
+    'proxy.config.dns.resolv_conf': 'NULL',
+    'proxy.config.diags.debug.tags':  'ssl_secret_load_test',
+    'proxy.config.diags.debug.enabled': 1
+})
+
+dns.addRecords(records={"foo.com.": ["127.0.0.1"]})
+dns.addRecords(records={"bar.com.": ["127.0.0.1"]})
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for foo should return EC cert")
+tr.Setup.Copy("ssl/signer.pem")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(dns)
+tr.Processes.Default.StartBefore(Test.Processes.ts, 
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+
+# Should receive a RSA cert
+tr = Test.AddTestRun("Only offer RSA ciphers, should receive RSA cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
foo.com -sigalgs 'RSA-PSS+SHA256' -connect 
127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: RSA-PSS", "Should select RSA cert")
+
+# Should receive a EC cert
+tr = Test.AddTestRun("Default for one.com should return EC cert")
+tr.Processes.Default.Command = "echo foo | openssl s_client -servername 
one.com -connect 127.0.0.1:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = server
+tr.StillRunningAfter = ts
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("Peer signature 
type: ECDSA", "Should select EC cert")
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("CN = 
group.com", "Should select a group SAN");

Review comment:
       trailing semicolon




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to