From c55e2be2baae4f0caa0c4acbe8fe46b9509d47a5 Mon Sep 17 00:00:00 2001 From: Susan Hinrichs Date: Thu, 6 Dec 2018 23:18:47 +0000 Subject: [PATCH] Allow for override of ssl.client files --- doc/admin-guide/files/records.config.en.rst | 5 + .../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 ++++++++++++++++++ .../tls/tls_verify_ca_override.test.py | 137 +++++++++++++++++ 20 files changed, 489 insertions(+), 129 deletions(-) create mode 100644 tests/gold_tests/tls/tls_client_cert_override.test.py create mode 100644 tests/gold_tests/tls/tls_verify_ca_override.test.py diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst index 34d9e3a7635..e2443846b44 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 91eaaff7d8a..a38fd566786 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 76189d1eb6b..6ebd818dfd3 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -777,6 +777,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, @@ -802,6 +803,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 e9b0427a429..1a8d6013126 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(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 3bccf9f1516..5c93abbbd8f 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 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 a1d75045417..220de51736b 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>; diff --git a/iocore/net/P_UnixNetVConnection.h b/iocore/net/P_UnixNetVConnection.h index 1516ddda2f8..0a320319b1c 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 7f0ceb2875c..89842f543fd 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 a6dbed7eebb..2cdeca0ad63 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 96221d49696..1e5dccf8cc2 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 524d2ab9228..470a54419ce 100644 --- a/iocore/net/SSLSNIConfig.cc +++ b/iocore/net/SSLSNIConfig.cc @@ -41,7 +41,6 @@ static ConfigUpdateHandler *sniConfigUpdate; struct NetAccept; std::unordered_map 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 5e53b0bdd28..d0ae28386c5 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 e9cb40fda08..7c329f1fab0 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 74bc20169f3..c7f46cbd4ac 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 f274168cc7f..59f0faf7bfa 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 75095d9d698..18dad8e8ea9 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 c537e166292..61de6059a50 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(value); - } - break; - case TS_CONFIG_SSL_CERT_FILEPATH: - if (value && length > 0) { - s->t_state.txn_conf->client_cert_filepath = const_cast(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(value); } break; + case TS_CONFIG_SSL_CLIENT_CERT_FILENAME: + if (value && length > 0) { + s->t_state.txn_conf->ssl_client_cert_filename = const_cast(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(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(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 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 00000000000..e5d21b8f442 --- /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 00000000000..016ae9935be --- /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 + +