You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by sh...@apache.org on 2019/01/08 14:22:08 UTC
[trafficserver] branch master updated: Allow for override of
ssl.client files
This is an automated email from the ASF dual-hosted git repository.
shinrich 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 fb513ab Allow for override of ssl.client files
fb513ab is described below
commit fb513abe68dfe95aaf3d972d7b4d20f40ef27382
Author: Susan Hinrichs <sh...@oath.com>
AuthorDate: Thu Dec 6 23:18:47 2018 +0000
Allow for override of ssl.client files
---
doc/admin-guide/files/records.config.en.rst | 5 +
.../api/functions/TSHttpOverridableConfig.en.rst | 7 +-
include/ts/apidefs.h.in | 3 +
iocore/net/I_NetVConnection.h | 31 ++---
iocore/net/P_SSLConfig.h | 8 +-
iocore/net/P_SSLSNI.h | 2 +-
iocore/net/P_UnixNetVConnection.h | 8 +-
iocore/net/SSLClientUtils.cc | 37 ------
iocore/net/SSLConfig.cc | 132 +++++++++++++------
iocore/net/SSLNetVConnection.cc | 20 ++-
iocore/net/SSLSNIConfig.cc | 5 +-
iocore/net/SSLUtils.cc | 3 +-
mgmt/RecordsConfig.cc | 4 +-
plugins/lua/ts_lua_http_config.c | 4 +
proxy/http/HttpConfig.h | 15 ++-
proxy/http/HttpSM.cc | 3 +
src/traffic_server/InkAPI.cc | 45 ++++---
src/traffic_server/InkAPITest.cc | 4 +-
.../tls/tls_client_cert_override.test.py | 145 +++++++++++++++++++++
.../gold_tests/tls/tls_verify_ca_override.test.py | 137 +++++++++++++++++++
20 files changed, 489 insertions(+), 129 deletions(-)
diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst
index 34d9e3a..e244384 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -3508,6 +3508,7 @@ Client-Related Configuration
.. ts:cv:: CONFIG proxy.config.ssl.client.cert.filename STRING NULL
:reloadable:
+ :overridable:
The filename of SSL client certificate installed on |TS|.
@@ -3519,6 +3520,7 @@ Client-Related Configuration
.. ts:cv:: CONFIG proxy.config.ssl.client.private_key.filename STRING NULL
:reloadable:
+ :overridable:
The filename of the |TS| private key. Change this variable
only if the private key is not located in the |TS| SSL
@@ -3532,11 +3534,14 @@ Client-Related Configuration
file.
.. ts:cv:: CONFIG proxy.config.ssl.client.CA.cert.filename STRING NULL
+ :reloadable:
+ :overridable:
The filename of the certificate authority against which the origin
server will be verified.
.. ts:cv:: CONFIG proxy.config.ssl.client.CA.cert.path STRING NULL
+ :reloadable:
Specifies the location of the certificate authority file against
which the origin server will be verified.
diff --git a/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst b/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst
index 91eaaff..a38fd56 100644
--- a/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst
+++ b/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst
@@ -152,7 +152,7 @@ TSOverridableConfigKey Value Configuratio
:c:macro:`TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED` :ts:cv:`proxy.config.http.post.check.content_length.enabled`
:c:macro:`TS_CONFIG_HTTP_POST_CONNECT_ATTEMPTS_TIMEOUT` :ts:cv:`proxy.config.http.post_connect_attempts_timeout`
:c:macro:`TS_CONFIG_HTTP_REDIRECT_USE_ORIG_CACHE_KEY` :ts:cv:`proxy.config.http.redirect_use_orig_cache_key`
-TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED proxy.config.http.request_buffer_enabled
+:c:macro:`TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED` :ts:cv:`proxy.config.http.request_buffer_enabled`
:c:macro:`TS_CONFIG_HTTP_REQUEST_HEADER_MAX_SIZE` :ts:cv:`proxy.config.http.request_header_max_size`
:c:macro:`TS_CONFIG_HTTP_RESPONSE_HEADER_MAX_SIZE` :ts:cv:`proxy.config.http.response_header_max_size`
:c:macro:`TS_CONFIG_HTTP_RESPONSE_SERVER_ENABLED` :ts:cv:`proxy.config.http.response_server_enabled`
@@ -184,6 +184,11 @@ TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED proxy.config
:c:macro:`TS_CONFIG_URL_REMAP_PRISTINE_HOST_HDR` :ts:cv:`proxy.config.url_remap.pristine_host_hdr`
:c:macro:`TS_CONFIG_WEBSOCKET_ACTIVE_TIMEOUT` :ts:cv:`proxy.config.websocket.active_timeout`
:c:macro:`TS_CONFIG_WEBSOCKET_NO_ACTIVITY_TIMEOUT` :ts:cv:`proxy.config.websocket.no_activity_timeout`
+:c:macro:`TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY` :ts:cv:`proxy.config.ssl.client.verify.server.policy`
+:c:macro:`TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES` :ts:cv:`proxy.config.ssl.client.verify.server.properties`
+:c:macro:`TS_CONFIG_SSL_CLIENT_CERT_FILENAME` :ts:cv:`proxy.config.ssl.client.cert.filename`
+:c:macro:`TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME` :ts:cv:`proxy.config.ssl.client.private_key.filename`
+:c:macro:`TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME` :ts:cv:`proxy.config.ssl.client.CA.cert.filename`
================================================================== ====================================================================
Examples
diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in
index bb3851f..5e7bf7c 100644
--- a/include/ts/apidefs.h.in
+++ b/include/ts/apidefs.h.in
@@ -789,6 +789,7 @@ typedef enum {
TS_CONFIG_SRV_ENABLED,
TS_CONFIG_HTTP_FORWARD_CONNECT_METHOD,
TS_CONFIG_SSL_CERT_FILENAME,
+ TS_CONFIG_SSL_CLIENT_CERT_FILENAME = TS_CONFIG_SSL_CERT_FILENAME,
TS_CONFIG_SSL_CERT_FILEPATH,
TS_CONFIG_PARENT_FAILURES_UPDATE_HOSTDB,
TS_CONFIG_HTTP_CACHE_ENABLE_DEFAULT_VARY_HEADER,
@@ -814,6 +815,8 @@ typedef enum {
TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY,
TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES,
TS_CONFIG_SSL_CLIENT_SNI_POLICY,
+ TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME,
+ TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME,
TS_CONFIG_LAST_ENTRY
} TSOverridableConfigKey;
diff --git a/iocore/net/I_NetVConnection.h b/iocore/net/I_NetVConnection.h
index e9b0427..1a8d601 100644
--- a/iocore/net/I_NetVConnection.h
+++ b/iocore/net/I_NetVConnection.h
@@ -186,7 +186,20 @@ struct NetVCOptions {
/**
* Client certificate to use in response to OS's certificate request
*/
- ats_scoped_str clientCertificate;
+ const char *ssl_client_cert_name = nullptr;
+ /*
+ * File containing private key matching certificate
+ */
+ const char *ssl_client_private_key_name = nullptr;
+ /*
+ * File containing CA certs for verifying origin's cert
+ */
+ const char *ssl_client_ca_cert_name = nullptr;
+ /*
+ * Directory containing CA certs for verifying origin's cert
+ */
+ const char *ssl_client_ca_cert_path = nullptr;
+
/// Reset all values to defaults.
/**
@@ -233,13 +246,6 @@ struct NetVCOptions {
}
return *this;
}
- self &
- set_client_certname(const char *name)
- {
- clientCertificate = ats_strdup(name);
- // clientCertificate = name;
- return *this;
- }
self &
operator=(self const &that)
@@ -255,9 +261,8 @@ struct NetVCOptions {
* memcpy removes the extra reference to that's copy of the string
* Removing the release will eventualy cause a double free crash
*/
- sni_servername = nullptr; // release any current name.
- ssl_servername = nullptr;
- clientCertificate = nullptr;
+ sni_servername = nullptr; // release any current name.
+ ssl_servername = nullptr;
memcpy(static_cast<void *>(this), &that, sizeof(self));
if (that.sni_servername) {
sni_servername.release(); // otherwise we'll free the source string.
@@ -267,10 +272,6 @@ struct NetVCOptions {
ssl_servername.release(); // otherwise we'll free the source string.
this->ssl_servername = ats_strdup(that.ssl_servername);
}
- if (that.clientCertificate) {
- clientCertificate.release(); // otherwise we'll free the source string.
- this->clientCertificate = ats_strdup(that.clientCertificate);
- }
}
return *this;
}
diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
index 3bccf9f..5c93abb 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -80,7 +80,9 @@ struct SSLConfigParams : public ConfigInfo {
int ssl_session_cache_auto_clear;
char *clientCertPath;
+ char *clientCertPathOnly;
char *clientKeyPath;
+ char *clientKeyPathOnly;
char *clientCACertFilename;
char *clientCACertPath;
YamlSNIConfig::Policy verifyServerPolicy;
@@ -115,11 +117,15 @@ struct SSLConfigParams : public ConfigInfo {
SSL_CTX *client_ctx;
+ // Making this mutable since this is a updatable
+ // cache on an otherwise immutable config object
+ // The ctx_map owns the client SSL_CTX objects and is responseible for cleaning them up
mutable std::unordered_map<std::string, SSL_CTX *> ctx_map;
mutable ink_mutex ctxMapLock;
SSL_CTX *getClientSSL_CTX(void) const;
- SSL_CTX *getNewCTX(const char *client_cert, const char *key_file) const;
+ SSL_CTX *getCTX(const char *client_cert, const char *key_file, const char *ca_bundle_file, const char *ca_bundle_path) const;
+ void cleanupCTXTable();
void initialize();
void cleanup();
diff --git a/iocore/net/P_SSLSNI.h b/iocore/net/P_SSLSNI.h
index a1d7504..220de51 100644
--- a/iocore/net/P_SSLSNI.h
+++ b/iocore/net/P_SSLSNI.h
@@ -46,7 +46,7 @@ struct NextHopProperty {
YamlSNIConfig::Policy verifyServerPolicy = YamlSNIConfig::Policy::UNSET; // whether to verify the next hop
YamlSNIConfig::Property verifyServerProperties = YamlSNIConfig::Property::UNSET; // what to verify on the next hop
SSL_CTX *ctx = nullptr; // ctx generated off the certificate to present to this server
- NextHopProperty();
+ NextHopProperty() {}
};
using actionVector = std::vector<std::unique_ptr<ActionItem>>;
diff --git a/iocore/net/P_UnixNetVConnection.h b/iocore/net/P_UnixNetVConnection.h
index 1516ddd..0a32031 100644
--- a/iocore/net/P_UnixNetVConnection.h
+++ b/iocore/net/P_UnixNetVConnection.h
@@ -66,9 +66,11 @@ NetVCOptions::reset()
etype = ET_NET;
- sni_servername = nullptr;
- ssl_servername = nullptr;
- clientCertificate = nullptr;
+ sni_servername = nullptr;
+ ssl_servername = nullptr;
+ ssl_client_cert_name = nullptr;
+ ssl_client_private_key_name = nullptr;
+ ssl_client_ca_cert_name = nullptr;
}
TS_INLINE void
diff --git a/iocore/net/SSLClientUtils.cc b/iocore/net/SSLClientUtils.cc
index 7f0ceb2..89842f5 100644
--- a/iocore/net/SSLClientUtils.cc
+++ b/iocore/net/SSLClientUtils.cc
@@ -143,7 +143,6 @@ SSLInitClientContext(const SSLConfigParams *params)
{
ink_ssl_method_t meth = nullptr;
SSL_CTX *client_ctx = nullptr;
- char *clientKeyPtr = nullptr;
// Note that we do not call RAND_seed() explicitly here, we depend on OpenSSL
// to do the seeding of the PRNG for us. This is the case for all platforms that
@@ -183,44 +182,8 @@ SSLInitClientContext(const SSLConfigParams *params)
}
#endif
- // if no path is given for the client private key,
- // assume it is contained in the client certificate file.
- clientKeyPtr = params->clientKeyPath;
- if (clientKeyPtr == nullptr) {
- clientKeyPtr = params->clientCertPath;
- }
-
- if (params->clientCertPath != nullptr && params->clientCertPath[0] != '\0') {
- if (!SSL_CTX_use_certificate_chain_file(client_ctx, params->clientCertPath)) {
- SSLError("failed to load client certificate from %s", params->clientCertPath);
- goto fail;
- }
-
- if (!SSL_CTX_use_PrivateKey_file(client_ctx, clientKeyPtr, SSL_FILETYPE_PEM)) {
- SSLError("failed to load client private key file from %s", clientKeyPtr);
- goto fail;
- }
-
- if (!SSL_CTX_check_private_key(client_ctx)) {
- SSLError("client private key (%s) does not match the certificate public key (%s)", clientKeyPtr, params->clientCertPath);
- goto fail;
- }
- }
-
SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback);
SSL_CTX_set_verify_depth(client_ctx, params->client_verify_depth);
-
- if (params->clientCACertFilename != nullptr || params->clientCACertPath != nullptr) {
- if (!SSL_CTX_load_verify_locations(client_ctx, params->clientCACertFilename, params->clientCACertPath)) {
- SSLError("invalid client CA Certificate file (%s) or CA Certificate path (%s)", params->clientCACertFilename,
- params->clientCACertPath);
- goto fail;
- }
- } else if (!SSL_CTX_set_default_verify_paths(client_ctx)) {
- SSLError("failed to set the default verify paths");
- goto fail;
- }
-
if (SSLConfigParams::init_ssl_ctx_cb) {
SSLConfigParams::init_ssl_ctx_cb(client_ctx, false);
}
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index a6dbed7..2cdeca0 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -89,12 +89,12 @@ SSLConfigParams::reset()
{
serverCertPathOnly = serverCertChainFilename = configFilePath = serverCACertFilename = serverCACertPath = clientCertPath =
clientKeyPath = clientCACertFilename = clientCACertPath = cipherSuite = client_cipherSuite = dhparamsFile = serverKeyPathOnly =
- nullptr;
- server_tls13_cipher_suites = nullptr;
- client_tls13_cipher_suites = nullptr;
- server_groups_list = nullptr;
- client_groups_list = nullptr;
- client_ctx = nullptr;
+ clientKeyPathOnly = clientCertPathOnly = nullptr;
+ server_tls13_cipher_suites = nullptr;
+ client_tls13_cipher_suites = nullptr;
+ server_groups_list = nullptr;
+ client_groups_list = nullptr;
+ client_ctx = nullptr;
clientCertLevel = client_verify_depth = verify_depth = 0;
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
verifyServerProperties = YamlSNIConfig::Property::NONE;
@@ -116,7 +116,9 @@ SSLConfigParams::cleanup()
serverCACertFilename = (char *)ats_free_null(serverCACertFilename);
serverCACertPath = (char *)ats_free_null(serverCACertPath);
clientCertPath = (char *)ats_free_null(clientCertPath);
+ clientCertPathOnly = (char *)ats_free_null(clientCertPathOnly);
clientKeyPath = (char *)ats_free_null(clientKeyPath);
+ clientKeyPathOnly = (char *)ats_free_null(clientKeyPathOnly);
clientCACertFilename = (char *)ats_free_null(clientCACertFilename);
clientCACertPath = (char *)ats_free_null(clientCACertPath);
configFilePath = (char *)ats_free_null(configFilePath);
@@ -131,7 +133,7 @@ SSLConfigParams::cleanup()
server_groups_list = (char *)ats_free_null(server_groups_list);
client_groups_list = (char *)ats_free_null(client_groups_list);
- SSLReleaseContext(client_ctx);
+ cleanupCTXTable();
reset();
}
@@ -410,14 +412,14 @@ SSLConfigParams::initialize()
REC_ReadConfigStringAlloc(ssl_client_cert_filename, "proxy.config.ssl.client.cert.filename");
REC_ReadConfigStringAlloc(ssl_client_cert_path, "proxy.config.ssl.client.cert.path");
if (ssl_client_cert_filename && ssl_client_cert_path) {
- set_paths_helper(ssl_client_cert_path, ssl_client_cert_filename, nullptr, &clientCertPath);
+ 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);
REC_ReadConfigStringAlloc(ssl_client_private_key_filename, "proxy.config.ssl.client.private_key.filename");
REC_ReadConfigStringAlloc(ssl_client_private_key_path, "proxy.config.ssl.client.private_key.path");
- set_paths_helper(ssl_client_private_key_path, ssl_client_private_key_filename, nullptr, &clientKeyPath);
+ set_paths_helper(ssl_client_private_key_path, ssl_client_private_key_filename, &clientKeyPathOnly, &clientKeyPath);
ats_free_null(ssl_client_private_key_filename);
ats_free_null(ssl_client_private_key_path);
@@ -434,41 +436,12 @@ SSLConfigParams::initialize()
// Enable client regardless of config file settings as remap file
// can cause HTTP layer to connect using SSL. But only if SSL
// initialization hasn't failed already.
- client_ctx = SSLInitClientContext(this);
+ client_ctx = this->getCTX(this->clientCertPath, this->clientKeyPath, this->clientCACertFilename, this->clientCACertPath);
if (!client_ctx) {
SSLError("Can't initialize the SSL client, HTTPS in remap rules will not function");
}
}
-// creates a new context attaching the provided certificate
-SSL_CTX *
-SSLConfigParams::getNewCTX(const char *client_cert, const char *client_key) const
-{
- SSL_CTX *nclient_ctx = nullptr;
- nclient_ctx = SSLInitClientContext(this);
- if (!nclient_ctx) {
- SSLError("Can't initialize the SSL client, HTTPS in remap rules will not function");
- return nullptr;
- }
- if (client_cert != nullptr && client_cert[0] != '\0') {
- if (!SSL_CTX_use_certificate_chain_file(nclient_ctx, (const char *)client_cert)) {
- SSLError("failed to load client certificate from %s", this->clientCertPath);
- SSLReleaseContext(nclient_ctx);
- return nullptr;
- }
- }
- // If there is not private key specified, perhaps it is in the file with the cert
- if (client_key == nullptr || client_key[0] == '\0') {
- client_key = client_cert;
- }
- // Try loading the private key
- if (client_key != nullptr && client_key[0] != '\0') {
- // If it failed, then we are just going to use the previously set private key from records.config
- SSL_CTX_use_PrivateKey_file(nclient_ctx, client_key, SSL_FILETYPE_PEM);
- }
- return nclient_ctx;
-}
-
SSL_CTX *
SSLConfigParams::getClientSSL_CTX() const
{
@@ -682,3 +655,84 @@ SSLTicketParams::cleanup()
ticket_block_free(default_global_keyblock);
ticket_key_filename = (char *)ats_free_null(ticket_key_filename);
}
+
+SSL_CTX *
+SSLConfigParams::getCTX(const char *client_cert, const char *key_file, const char *ca_bundle_file, const char *ca_bundle_path) const
+{
+ SSL_CTX *client_ctx = nullptr;
+ std::string key;
+ ts::bwprint(key, "{}:{}:{}:{}", client_cert, key_file, ca_bundle_file, ca_bundle_path);
+
+ ink_mutex_acquire(&ctxMapLock);
+ auto iter = ctx_map.find(key);
+ if (iter != ctx_map.end()) {
+ client_ctx = iter->second;
+ ink_mutex_release(&ctxMapLock);
+ return client_ctx;
+ }
+ ink_mutex_release(&ctxMapLock);
+
+ // Not yet in the table. Make the cert and add it to the table
+ client_ctx = SSLInitClientContext(this);
+
+ if (client_cert) {
+ // Set public and private keys
+ if (!SSL_CTX_use_certificate_chain_file(client_ctx, client_cert)) {
+ SSLError("failed to load client certificate from %s", client_cert);
+ goto fail;
+ }
+ if (!key_file || key_file[0] == '\0') {
+ key_file = client_cert;
+ }
+ if (!SSL_CTX_use_PrivateKey_file(client_ctx, key_file, SSL_FILETYPE_PEM)) {
+ SSLError("failed to load client private key file from %s", key_file);
+ goto fail;
+ }
+
+ if (!SSL_CTX_check_private_key(client_ctx)) {
+ SSLError("client private key (%s) does not match the certificate public key (%s)", key_file, client_cert);
+ goto fail;
+ }
+ }
+
+ // Set CA information for verifying peer cert
+ if (ca_bundle_file != nullptr || ca_bundle_path != nullptr) {
+ if (!SSL_CTX_load_verify_locations(client_ctx, ca_bundle_file, ca_bundle_path)) {
+ SSLError("invalid client CA Certificate file (%s) or CA Certificate path (%s)", ca_bundle_file, ca_bundle_path);
+ goto fail;
+ }
+ } else if (!SSL_CTX_set_default_verify_paths(client_ctx)) {
+ SSLError("failed to set the default verify paths");
+ goto fail;
+ }
+
+ ink_mutex_acquire(&ctxMapLock);
+ iter = ctx_map.find(key);
+ if (iter != ctx_map.end()) {
+ SSL_CTX_free(client_ctx);
+ client_ctx = iter->second;
+ } else {
+ ctx_map.insert(std::make_pair(key, client_ctx));
+ }
+ ink_mutex_release(&ctxMapLock);
+ return client_ctx;
+
+fail:
+ if (client_ctx) {
+ SSL_CTX_free(client_ctx);
+ }
+ return nullptr;
+}
+
+void
+SSLConfigParams::cleanupCTXTable()
+{
+ ink_mutex_acquire(&ctxMapLock);
+ auto iter = ctx_map.begin();
+ while (iter != ctx_map.end()) {
+ SSL_CTX_free(iter->second);
+ ++iter;
+ }
+ ctx_map.clear();
+ ink_mutex_release(&ctxMapLock);
+}
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index 96221d4..1e5dccf 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -22,6 +22,7 @@
*/
#include "tscore/ink_config.h"
#include "tscore/EventNotify.h"
+#include "tscore/I_Layout.h"
#include "records/I_RecHttp.h"
#include "P_Net.h"
#include "P_SSLNextProtocolSet.h"
@@ -970,7 +971,24 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
auto nps = sniParam->getPropertyConfig(serverKey);
SSL_CTX *clientCTX = nullptr;
- if (nps) {
+ // First Look to see if there are override parameters
+ if (options.ssl_client_cert_name) {
+ std::string certFilePath = Layout::get()->relative_to(params->clientCertPathOnly, options.ssl_client_cert_name);
+ std::string keyFilePath;
+ if (options.ssl_client_private_key_name) {
+ keyFilePath = Layout::get()->relative_to(params->clientKeyPathOnly, options.ssl_client_private_key_name);
+ }
+ std::string caCertFilePath;
+ if (options.ssl_client_ca_cert_name) {
+ caCertFilePath = Layout::get()->relative_to(params->clientCACertPath, options.ssl_client_ca_cert_name);
+ }
+ clientCTX =
+ params->getCTX(certFilePath.c_str(), keyFilePath.empty() ? nullptr : keyFilePath.c_str(),
+ caCertFilePath.empty() ? params->clientCACertFilename : caCertFilePath.c_str(), params->clientCACertPath);
+ } else if (options.ssl_client_ca_cert_name) {
+ std::string caCertFilePath = Layout::get()->relative_to(params->clientCACertPath, options.ssl_client_ca_cert_name);
+ clientCTX = params->getCTX(params->clientCertPath, params->clientKeyPath, caCertFilePath.c_str(), params->clientCACertPath);
+ } else if (nps) {
clientCTX = nps->ctx;
} else { // Just stay with the values passed down from the SM for verify
clientCTX = params->client_ctx;
diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc
index 524d2ab..470a544 100644
--- a/iocore/net/SSLSNIConfig.cc
+++ b/iocore/net/SSLSNIConfig.cc
@@ -41,7 +41,6 @@
static ConfigUpdateHandler<SNIConfig> *sniConfigUpdate;
struct NetAccept;
std::unordered_map<int, SSLNextProtocolSet *> snpsMap;
-NextHopProperty::NextHopProperty() {}
const NextHopProperty *
SNIConfigParams::getPropertyConfig(const std::string &servername) const
@@ -83,8 +82,8 @@ SNIConfigParams::loadSNIConfig()
auto clientCTX = params->getClientSSL_CTX();
const char *certFile = item.client_cert.data();
const char *keyFile = item.client_key.data();
- if (certFile) {
- clientCTX = params->getNewCTX(certFile, keyFile);
+ if (certFile && certFile[0] != '\0') {
+ clientCTX = params->getCTX(certFile, keyFile, params->clientCACertFilename, params->clientCACertPath);
}
auto nps = next_hop_list.emplace(next_hop_list.end());
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 5e53b0b..d0ae283 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -2289,10 +2289,11 @@ ssl_callback_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
}
#endif /* HAVE_OPENSSL_SESSION_TICKETS */
+// Release SSL_CTX and the associated data. This works for both
+// client and server contexts and gracefully accepts nullptr.
void
SSLReleaseContext(SSL_CTX *ctx)
{
- // SSL_CTX_free() does nothing if ctx in nullptr, so there's no need to check.
SSL_CTX_free(ctx);
}
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index e9cb40f..7c329f1 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1150,9 +1150,9 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.ssl.client.private_key.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.filename", RECD_STRING, nullptr, RECU_RESTART_TS, RR_NULL, RECC_STR, "^[^[:space:]]*$", RECA_NULL}
+ {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.filename", RECD_STRING, nullptr, RECU_DYNAMIC, RR_NULL, RECC_STR, "^[^[:space:]]*$", RECA_NULL}
,
- {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+ {RECT_CONFIG, "proxy.config.ssl.client.CA.cert.path", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
{RECT_CONFIG, "proxy.config.ssl.client.sni_policy", RECD_STRING, TS_BUILD_SYSCONFDIR, RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
diff --git a/plugins/lua/ts_lua_http_config.c b/plugins/lua/ts_lua_http_config.c
index 74bc201..c7f46cb 100644
--- a/plugins/lua/ts_lua_http_config.c
+++ b/plugins/lua/ts_lua_http_config.c
@@ -138,6 +138,8 @@ typedef enum {
TS_LUA_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY = TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY,
TS_LUA_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES = TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES,
TS_LUA_CONFIG_SSL_CLIENT_SNI_POLICY = TS_CONFIG_SSL_CLIENT_SNI_POLICY,
+ TS_LUA_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME = TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME,
+ TS_LUA_CONFIG_SSL_CLIENT_CA_CERT_FILENAME = TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME,
TS_LUA_CONFIG_LAST_ENTRY = TS_CONFIG_LAST_ENTRY,
} TSLuaOverridableConfigKey;
@@ -266,6 +268,8 @@ ts_lua_var_item ts_lua_http_config_vars[] = {
TS_LUA_MAKE_VAR_ITEM(TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY),
TS_LUA_MAKE_VAR_ITEM(TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES),
TS_LUA_MAKE_VAR_ITEM(TS_CONFIG_SSL_CLIENT_SNI_POLICY),
+ TS_LUA_MAKE_VAR_ITEM(TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME),
+ TS_LUA_MAKE_VAR_ITEM(TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PER_SERVER_CONNECTION_MAX),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PER_SERVER_CONNECTION_MATCH),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_LAST_ENTRY),
diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
index f274168..59f0faf 100644
--- a/proxy/http/HttpConfig.h
+++ b/proxy/http/HttpConfig.h
@@ -552,8 +552,9 @@ struct OverridableHttpConfigParams {
global_user_agent_header_size(0),
cache_heuristic_lm_factor(0.10),
background_fill_threshold(0.5),
- client_cert_filename(nullptr),
- client_cert_filepath(nullptr),
+ ssl_client_cert_filename(nullptr),
+ ssl_client_private_key_filename(nullptr),
+ ssl_client_ca_cert_filename(nullptr),
cache_vary_default_text(nullptr),
cache_vary_default_images(nullptr),
cache_vary_default_other(nullptr)
@@ -798,8 +799,9 @@ struct OverridableHttpConfigParams {
MgmtFloat background_fill_threshold;
// Various strings, good place for them here ...
- char *client_cert_filename;
- char *client_cert_filepath;
+ char *ssl_client_cert_filename;
+ char *ssl_client_private_key_filename;
+ char *ssl_client_ca_cert_filename;
char *cache_vary_default_text;
char *cache_vary_default_images;
@@ -968,8 +970,9 @@ inline HttpConfigParams::~HttpConfigParams()
ats_free(oride.body_factory_template_base);
ats_free(oride.proxy_response_server_string);
ats_free(oride.global_user_agent_header);
- ats_free(oride.client_cert_filename);
- ats_free(oride.client_cert_filepath);
+ ats_free(oride.ssl_client_cert_filename);
+ ats_free(oride.ssl_client_private_key_filename);
+ ats_free(oride.ssl_client_ca_cert_filename);
ats_free(oride.cache_vary_default_text);
ats_free(oride.cache_vary_default_images);
ats_free(oride.cache_vary_default_other);
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index 75095d9..18dad8e 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -5044,6 +5044,9 @@ HttpSM::do_http_server_open(bool raw)
if (scheme_to_use == URL_WKSIDX_HTTPS || HttpTransactHeaders::is_method_idempotent(t_state.method)) {
opt.f_tcp_fastopen = (t_state.txn_conf->sock_option_flag_out & NetVCOptions::SOCK_OPT_TCP_FAST_OPEN);
}
+ opt.ssl_client_cert_name = t_state.txn_conf->ssl_client_cert_filename;
+ opt.ssl_client_private_key_name = t_state.txn_conf->ssl_client_private_key_filename;
+ opt.ssl_client_ca_cert_name = t_state.txn_conf->ssl_client_ca_cert_filename;
if (scheme_to_use == URL_WKSIDX_HTTPS) {
SMDebug("http", "calling sslNetProcessor.connect_re");
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index c537e16..61de605 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -8198,18 +8198,16 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overr
case TS_CONFIG_HTTP_FORWARD_CONNECT_METHOD:
ret = _memberp_to_generic(&overridableHttpConfig->forward_connect_method, conv);
break;
- case TS_CONFIG_SSL_CERT_FILENAME:
- ret = _memberp_to_generic(&overridableHttpConfig->client_cert_filename, conv);
- break;
- case TS_CONFIG_SSL_CERT_FILEPATH:
- ret = _memberp_to_generic(&overridableHttpConfig->client_cert_filepath, conv);
- break;
case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER:
ret = _memberp_to_generic(&overridableHttpConfig->ssl_client_verify_server, conv);
break;
case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY:
case TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES:
case TS_CONFIG_SSL_CLIENT_SNI_POLICY:
+ case TS_CONFIG_SSL_CLIENT_CERT_FILENAME:
+ case TS_CONFIG_SSL_CERT_FILEPATH:
+ case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME:
+ case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME:
// String, must be handled elsewhere
break;
case TS_CONFIG_PARENT_FAILURES_UPDATE_HOSTDB:
@@ -8395,16 +8393,6 @@ TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char
s->t_state.txn_conf->body_factory_template_base_len = 0;
}
break;
- case TS_CONFIG_SSL_CERT_FILENAME:
- if (value && length > 0) {
- s->t_state.txn_conf->client_cert_filename = const_cast<char *>(value);
- }
- break;
- case TS_CONFIG_SSL_CERT_FILEPATH:
- if (value && length > 0) {
- s->t_state.txn_conf->client_cert_filepath = const_cast<char *>(value);
- }
- break;
case TS_CONFIG_HTTP_INSERT_FORWARDED:
if (value && length > 0) {
ts::LocalBufferWriter<1024> error;
@@ -8431,6 +8419,24 @@ TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char
s->t_state.txn_conf->ssl_client_sni_policy = const_cast<char *>(value);
}
break;
+ case TS_CONFIG_SSL_CLIENT_CERT_FILENAME:
+ if (value && length > 0) {
+ s->t_state.txn_conf->ssl_client_cert_filename = const_cast<char *>(value);
+ }
+ break;
+ case TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME:
+ if (value && length > 0) {
+ s->t_state.txn_conf->ssl_client_private_key_filename = const_cast<char *>(value);
+ }
+ break;
+ case TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME:
+ if (value && length > 0) {
+ s->t_state.txn_conf->ssl_client_ca_cert_filename = const_cast<char *>(value);
+ }
+ break;
+ case TS_CONFIG_SSL_CERT_FILEPATH:
+ /* noop */
+ break;
default: {
MgmtConverter const *conv;
void *dest = _conf_to_memberp(conf, s->t_state.txn_conf, conv);
@@ -8505,7 +8511,6 @@ static const std::unordered_map<std::string_view, std::tuple<const TSOverridable
{"proxy.config.http.slow.log.threshold", {TS_CONFIG_HTTP_SLOW_LOG_THRESHOLD, TS_RECORDDATATYPE_INT}},
{"proxy.config.http.cache.max_stale_age", {TS_CONFIG_HTTP_CACHE_MAX_STALE_AGE, TS_RECORDDATATYPE_INT}},
{"proxy.config.http.default_buffer_size", {TS_CONFIG_HTTP_DEFAULT_BUFFER_SIZE, TS_RECORDDATATYPE_INT}},
- {"proxy.config.ssl.client.cert.filename", {TS_CONFIG_SSL_CERT_FILENAME, TS_RECORDDATATYPE_STRING}},
{"proxy.config.http.response_server_str", {TS_CONFIG_HTTP_RESPONSE_SERVER_STR, TS_RECORDDATATYPE_STRING}},
{"proxy.config.http.keep_alive_post_out", {TS_CONFIG_HTTP_KEEP_ALIVE_POST_OUT, TS_RECORDDATATYPE_INT}},
{"proxy.config.net.sock_option_flag_out", {TS_CONFIG_NET_SOCK_OPTION_FLAG_OUT, TS_RECORDDATATYPE_INT}},
@@ -8621,7 +8626,11 @@ static const std::unordered_map<std::string_view, std::tuple<const TSOverridable
{"proxy.config.ssl.client.verify.server", {TS_CONFIG_SSL_CLIENT_VERIFY_SERVER, TS_RECORDDATATYPE_INT}},
{"proxy.config.ssl.client.verify.server.policy", {TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_POLICY, TS_RECORDDATATYPE_STRING}},
{"proxy.config.ssl.client.verify.server.properties", {TS_CONFIG_SSL_CLIENT_VERIFY_SERVER_PROPERTIES, TS_RECORDDATATYPE_STRING}},
- {"proxy.config.ssl.client.sni_policy", {TS_CONFIG_SSL_CLIENT_SNI_POLICY, TS_RECORDDATATYPE_STRING}}});
+ {"proxy.config.ssl.client.sni_policy", {TS_CONFIG_SSL_CLIENT_SNI_POLICY, TS_RECORDDATATYPE_STRING}},
+ {"proxy.config.ssl.client.cert.filename", {TS_CONFIG_SSL_CLIENT_CERT_FILENAME, TS_RECORDDATATYPE_STRING}},
+ {"proxy.config.ssl.client.cert.path", {TS_CONFIG_SSL_CERT_FILEPATH, TS_RECORDDATATYPE_STRING}},
+ {"proxy.config.ssl.client.private_key.filename", {TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME, TS_RECORDDATATYPE_STRING}},
+ {"proxy.config.ssl.client.CA.cert.filename", {TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME, TS_RECORDDATATYPE_STRING}}});
TSReturnCode
TSHttpTxnConfigFind(const char *name, int length, TSOverridableConfigKey *conf, TSRecordDataType *type)
diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc
index 6d38500..811f1be 100644
--- a/src/traffic_server/InkAPITest.cc
+++ b/src/traffic_server/InkAPITest.cc
@@ -8693,7 +8693,9 @@ std::array<std::string_view, TS_CONFIG_LAST_ENTRY> SDK_Overridable_Configs = {
"proxy.config.ssl.client.verify.server",
"proxy.config.ssl.client.verify.server.policy",
"proxy.config.ssl.client.verify.server.properties",
- "proxy.config.ssl.client.sni_policy"}};
+ "proxy.config.ssl.client.sni_policy",
+ "proxy.config.ssl.client.private_key.filename",
+ "proxy.config.ssl.client.CA.cert.filename"}};
REGRESSION_TEST(SDK_API_OVERRIDABLE_CONFIGS)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
{
diff --git a/tests/gold_tests/tls/tls_client_cert_override.test.py b/tests/gold_tests/tls/tls_client_cert_override.test.py
new file mode 100644
index 0000000..e5d21b8
--- /dev/null
+++ b/tests/gold_tests/tls/tls_client_cert_override.test.py
@@ -0,0 +1,145 @@
+'''
+Test offering client cert to origin
+'''
+# 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 = '''
+Test conf_remp to specify different client certificates to offer to the origin
+'''
+
+Test.SkipUnless(Condition.HasProgram("grep", "grep needs to be installed on system for this test to work"))
+
+ts = Test.MakeATSProcess("ts", command="traffic_manager", select_ports=False)
+cafile = "{0}/signer.pem".format(Test.RunDirectory)
+cafile2 = "{0}/signer2.pem".format(Test.RunDirectory)
+server = Test.MakeOriginServer("server", ssl=True, options = { "--clientCA": cafile, "--clientverify": "true"}, 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": "true"}, clientcert="{0}/signed2-bar.pem".format(Test.RunDirectory), clientkey="{0}/signed-bar.key".format(Test.RunDirectory))
+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.Variables.ssl_port = 4443
+ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 0,
+ 'proxy.config.diags.debug.tags': '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.http.server_ports': '{0}'.format(ts.Variables.port),
+ '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.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.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.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.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.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, ready=When.PortOpen(ts.Variables.port))
+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.TimeOut = 5
+tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Check response")
+tr.TimeOut = 5
+
+#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.TimeOut = 5
+trfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Check response")
+trfail.TimeOut = 5
+
+# 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.TimeOut = 5
+trbar.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Check response")
+trbar.TimeOut = 5
+
+#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.TimeOut = 5
+trbarfail.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Check response")
+trbarfail.TimeOut = 5
+
+
diff --git a/tests/gold_tests/tls/tls_verify_ca_override.test.py b/tests/gold_tests/tls/tls_verify_ca_override.test.py
new file mode 100644
index 0000000..016ae99
--- /dev/null
+++ b/tests/gold_tests/tls/tls_verify_ca_override.test.py
@@ -0,0 +1,137 @@
+'''
+'''
+# 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 tls server certificate verification options. Exercise conf_remap for ca bundle
+'''
+
+# need Curl
+Test.SkipUnless(
+ Condition.HasProgram("curl", "Curl need to be installed on system for this test to work")
+)
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server1 = Test.MakeOriginServer("server1", ssl=True, options = {"--key": "{0}/signed-foo.key".format(Test.RunDirectory), "--cert": "{0}/signed-foo.pem".format(Test.RunDirectory)})
+server2 = Test.MakeOriginServer("server2", ssl=True, options = {"--key": "{0}/signed-foo.key".format(Test.RunDirectory), "--cert": "{0}/signed2-foo.pem".format(Test.RunDirectory)})
+
+request_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+request_bad_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+request_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bar.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+request_bad_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_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": ""}
+server1.addResponse("sessionlog.json", request_foo_header, response_header)
+server1.addResponse("sessionlog.json", request_bad_foo_header, response_header)
+server2.addResponse("sessionlog.json", request_bar_header, response_header)
+server2.addResponse("sessionlog.json", request_bad_bar_header, response_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/signed-foo.pem")
+ts.addSSLfile("ssl/signed2-foo.pem")
+ts.addSSLfile("ssl/signed-foo.key")
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+ts.addSSLfile("ssl/signer.pem")
+ts.addSSLfile("ssl/signer.key")
+ts.addSSLfile("ssl/signer2.pem")
+ts.addSSLfile("ssl/signer2.key")
+
+ts.Variables.ssl_port = 4443
+ts.Disk.remap_config.AddLine(
+ 'map /case1 https://127.0.0.1:{0}/ @plugin=conf_remap.so @pparam=proxy.config.ssl.client.CA.cert.filename={1}/{2}'.format(server1.Variables.Port, ts.Variables.SSLDir, "signer.pem")
+)
+ts.Disk.remap_config.AddLine(
+ 'map /badcase1 https://127.0.0.1:{0}/ @plugin=conf_remap.so @pparam=proxy.config.ssl.client.CA.cert.filename={1}/{2}'.format(server1.Variables.Port, ts.Variables.SSLDir, "signer2.pem")
+)
+ts.Disk.remap_config.AddLine(
+ 'map /case2 https://127.0.0.1:{0}/ @plugin=conf_remap.so @pparam=proxy.config.ssl.client.CA.cert.filename={1}/{2}'.format(server2.Variables.Port, ts.Variables.SSLDir, "signer2.pem")
+)
+ts.Disk.remap_config.AddLine(
+ 'map /badcase2 https://127.0.0.1:{0}/ @plugin=conf_remap.so @pparam=proxy.config.ssl.client.CA.cert.filename={1}/{2}'.format(server2.Variables.Port, ts.Variables.SSLDir, "signer.pem")
+)
+
+ts.Disk.ssl_multicert_config.AddLine(
+ 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+# Case 1, global config policy=permissive properties=signature
+# override for foo.com policy=enforced properties=all
+ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'http|ssl',
+ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+ 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
+ # enable ssl port
+ 'proxy.config.http.server_ports': '{0} {1}:proto=http2;http:ssl'.format(ts.Variables.port, ts.Variables.ssl_port),
+ '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',
+ # set global policy
+ 'proxy.config.ssl.client.verify.server.policy': 'ENFORCED',
+ 'proxy.config.ssl.client.verify.server.properties': 'SIGNATURE',
+ 'proxy.config.ssl.client.CA.cert.path': '/tmp',
+ 'proxy.config.ssl.client.CA.cert.filename': '{0}/signer.pem'.format(ts.Variables.SSLDir),
+ 'proxy.config.url_remap.pristine_host_hdr': 1
+})
+
+# Should succeed
+tr = Test.AddTestRun("Use corrcect ca bundle for server 1")
+tr.Processes.Default.Command = 'curl -k -H \"host: foo.com\" http://127.0.0.1:{0}/case1'.format(ts.Variables.port)
+tr.ReturnCode = 0
+tr.Setup.Copy("ssl/signed-foo.key")
+tr.Setup.Copy("ssl/signed-foo.pem")
+tr.Setup.Copy("ssl/signed2-foo.pem")
+tr.Processes.Default.StartBefore(server1)
+tr.Processes.Default.StartBefore(server2)
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.port))
+tr.StillRunningAfter = server1
+tr.StillRunningAfter = ts
+tr.Processes.Default.TimeOut = 5
+# Should succed. No message
+tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded")
+tr.TimeOut = 5
+
+tr2 = Test.AddTestRun("Use incorrect ca bundle for server 1")
+tr2.Processes.Default.Command = "curl -k -H \"host: bar.com\" http://127.0.0.1:{0}/badcase1".format(ts.Variables.port)
+tr2.ReturnCode = 0
+tr2.StillRunningAfter = server1
+tr2.Processes.Default.TimeOut = 5
+tr2.StillRunningAfter = ts
+# Should succeed, but will be message in log about name mismatch
+tr2.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have succeeded")
+tr2.TimeOut = 5
+
+tr2 = Test.AddTestRun("Use currect ca bundle for server 2")
+tr2.Processes.Default.Command = "curl -k -H \"host: random.com\" http://127.0.0.1:{0}/case2".format(ts.Variables.port)
+tr2.ReturnCode = 0
+tr2.StillRunningAfter = server2
+tr2.Processes.Default.TimeOut = 5
+tr2.StillRunningAfter = ts
+# Should succeed, but will be message in log about signature
+tr2.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded")
+tr2.TimeOut = 5
+
+tr3 = Test.AddTestRun("User incorrect ca bundle for server 2")
+tr3.Processes.Default.Command = "curl -k -H \"host: foo.com\" http://127.0.0.1:{0}/badcase2".format(ts.Variables.port)
+tr3.ReturnCode = 0
+tr3.StillRunningAfter = server2
+tr3.StillRunningAfter = ts
+# Should succeed. No error messages
+tr3.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have succeeded")
+tr3.Processes.Default.TimeOut = 5
+
+