diff --git a/doc/admin-guide/files/records.yaml.en.rst b/doc/admin-guide/files/records.yaml.en.rst index b319c15b1b0..9ea67b7ccd2 100644 --- a/doc/admin-guide/files/records.yaml.en.rst +++ b/doc/admin-guide/files/records.yaml.en.rst @@ -4407,6 +4407,7 @@ Client-Related Configuration .. ts:cv:: CONFIG proxy.config.ssl.client.CA.cert.path STRING NULL :reloadable: + :overridable: 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 9a175f4e15f..623b40f3a13 100644 --- a/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst +++ b/doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst @@ -190,6 +190,7 @@ TSOverridableConfigKey Value Config :enumerator:`TS_CONFIG_SSL_CLIENT_CERT_FILENAME` :ts:cv:`proxy.config.ssl.client.cert.filename` :enumerator:`TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME` :ts:cv:`proxy.config.ssl.client.private_key.filename` :enumerator:`TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME` :ts:cv:`proxy.config.ssl.client.CA.cert.filename` +:enumerator:`TS_CONFIG_SSL_CLIENT_CA_CERT_PATH` :ts:cv:`proxy.config.ssl.client.CA.cert.path` :enumerator:`TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE` :ts:cv:`proxy.config.hostdb.ip_resolve` :enumerator:`TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_INDEX` :ts:cv:`proxy.config.plugin.vc.default_buffer_index` :enumerator:`TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_WATER_MARK` :ts:cv:`proxy.config.plugin.vc.default_buffer_water_mark` diff --git a/doc/developer-guide/api/types/TSOverridableConfigKey.en.rst b/doc/developer-guide/api/types/TSOverridableConfigKey.en.rst index 56d325e6192..9d200845c66 100644 --- a/doc/developer-guide/api/types/TSOverridableConfigKey.en.rst +++ b/doc/developer-guide/api/types/TSOverridableConfigKey.en.rst @@ -154,6 +154,7 @@ Enumeration Members .. enumerator:: TS_CONFIG_SSL_CLIENT_SNI_POLICY .. enumerator:: TS_CONFIG_SSL_CLIENT_PRIVATE_KEY_FILENAME .. enumerator:: TS_CONFIG_SSL_CLIENT_CA_CERT_FILENAME +.. enumerator:: TS_CONFIG_SSL_CLIENT_CA_CERT_PATH .. enumerator:: TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE .. enumerator:: TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_INDEX .. enumerator:: TS_CONFIG_PLUGIN_VC_DEFAULT_BUFFER_WATER_MARK diff --git a/include/cripts/Configs.hpp b/include/cripts/Configs.hpp index 6826f64247f..c2f4597a6a7 100644 --- a/include/cripts/Configs.hpp +++ b/include/cripts/Configs.hpp @@ -357,6 +357,7 @@ class Proxy { public: cripts::StringConfig filename{"proxy.config.ssl.client.CA.cert.filename"}; + cripts::StringConfig path{"proxy.config.ssl.client.CA.cert.path"}; }; // End class Cert public: diff --git a/include/proxy/http/HttpConfig.h b/include/proxy/http/HttpConfig.h index 05a7cc511f3..c5989c3620a 100644 --- a/include/proxy/http/HttpConfig.h +++ b/include/proxy/http/HttpConfig.h @@ -773,6 +773,7 @@ struct OverridableHttpConfigParams { char *ssl_client_cert_filename = nullptr; char *ssl_client_private_key_filename = nullptr; char *ssl_client_ca_cert_filename = nullptr; + char *ssl_client_ca_cert_path = nullptr; char *ssl_client_alpn_protocols = nullptr; // Host Resolution order @@ -1031,6 +1032,7 @@ inline HttpConfigParams::~HttpConfigParams() 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.ssl_client_ca_cert_path); ats_free(connect_ports_string); ats_free(reverse_proxy_no_host_redirect); ats_free(redirect_actions_string); diff --git a/include/proxy/http/OverridableConfigDefs.h b/include/proxy/http/OverridableConfigDefs.h index d70c4c54caa..a21e57d70aa 100644 --- a/include/proxy/http/OverridableConfigDefs.h +++ b/include/proxy/http/OverridableConfigDefs.h @@ -250,6 +250,7 @@ X(HTTP_CONNECT_ATTEMPTS_RETRY_BACKOFF_BASE, connect_attempts_retry_backoff_base, "proxy.config.http.connect_attempts_retry_backoff_base", INT, GENERIC) \ X(HTTP_NEGATIVE_REVALIDATING_LIST, negative_revalidating_list, "proxy.config.http.negative_revalidating_list", STRING, HttpStatusCodeList_Conv) \ X(HTTP_CACHE_POST_METHOD, cache_post_method, "proxy.config.http.cache.post_method", INT, GENERIC) \ - X(HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS, targeted_cache_control_headers, "proxy.config.http.cache.targeted_cache_control_headers", STRING, TargetedCacheControlHeaders_Conv) + X(HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS, targeted_cache_control_headers, "proxy.config.http.cache.targeted_cache_control_headers", STRING, TargetedCacheControlHeaders_Conv) \ + X(SSL_CLIENT_CA_CERT_PATH, ssl_client_ca_cert_path, "proxy.config.ssl.client.CA.cert.path", STRING, NONE) // clang-format on diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index f458884779e..bce145c8a49 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -916,6 +916,7 @@ enum TSOverridableConfigKey { TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_LIST, TS_CONFIG_HTTP_CACHE_POST_METHOD, TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS, + TS_CONFIG_SSL_CLIENT_CA_CERT_PATH, TS_CONFIG_LAST_ENTRY, }; diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index 2f62c45f755..bf18b4fc799 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -7545,6 +7545,11 @@ TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char s->t_state.my_txn_conf().ssl_client_ca_cert_filename = const_cast(value); } break; + case TS_CONFIG_SSL_CLIENT_CA_CERT_PATH: + if (value && length > 0) { + s->t_state.my_txn_conf().ssl_client_ca_cert_path = const_cast(value); + } + break; case TS_CONFIG_SSL_CLIENT_ALPN_PROTOCOLS: if (value && length > 0) { s->t_state.my_txn_conf().ssl_client_alpn_protocols = const_cast(value); @@ -7624,6 +7629,10 @@ TSHttpTxnConfigStringGet(TSHttpTxn txnp, TSOverridableConfigKey conf, const char *value = sm->t_state.txn_conf->server_session_sharing_match_str; *length = *value ? strlen(*value) : 0; break; + case TS_CONFIG_SSL_CLIENT_CA_CERT_PATH: + *value = sm->t_state.txn_conf->ssl_client_ca_cert_path; + *length = *value ? strlen(*value) : 0; + break; default: { MgmtConverter const *conv; const void *src = _conf_to_memberp(conf, sm->t_state.txn_conf, conv); diff --git a/src/iocore/net/SSLNetVConnection.cc b/src/iocore/net/SSLNetVConnection.cc index ab5eb9d32bd..43f945176e8 100644 --- a/src/iocore/net/SSLNetVConnection.cc +++ b/src/iocore/net/SSLNetVConnection.cc @@ -92,6 +92,17 @@ DbgCtl dbg_ctl_ssl_alpn{"ssl_alpn"}; DbgCtl dbg_ctl_ssl_origin_session_cache{"ssl.origin_session_cache"}; DbgCtl dbg_ctl_proxyprotocol{"proxyprotocol"}; +const char * +resolve_client_ca_cert_path(const SSLConfigParams *params, const char *path, std::string &storage) +{ + if (path == nullptr) { + return params->clientCACertPath; + } + + storage = Layout::get()->relative_to(Layout::get()->prefix, path); + return storage.c_str(); +} + } // namespace // @@ -1129,6 +1140,8 @@ SSLNetVConnection::_sslStartHandShake(int event, int &err) auto nps = sniParam->get_property_config(serverKey); shared_SSL_CTX sharedCTX = nullptr; SSL_CTX *clientCTX = nullptr; + std::string caCertPathStorage; + const char *caCertPath = resolve_client_ca_cert_path(params, options.ssl_client_ca_cert_path, caCertPathStorage); // First Look to see if there are override parameters Dbg(dbg_ctl_ssl, "Checking for outbound client cert override [%p]", options.ssl_client_cert_name.get()); @@ -1144,18 +1157,21 @@ SSLNetVConnection::_sslStartHandShake(int event, int &err) keyFilePath = Layout::get()->relative_to(params->clientKeyPathOnly, options.ssl_client_private_key_name); } if (options.ssl_client_ca_cert_name) { - caCertFilePath = Layout::get()->relative_to(params->clientCACertPath, options.ssl_client_ca_cert_name); + caCertFilePath = Layout::get()->relative_to(caCertPath, options.ssl_client_ca_cert_name); } Dbg(dbg_ctl_ssl, "Using outbound client cert `%s'", options.ssl_client_cert_name.get()); } else { Dbg(dbg_ctl_ssl, "Clearing outbound client cert"); } - sharedCTX = - params->getCTX(certFilePath, keyFilePath, 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); - sharedCTX = params->getCTX(params->clientCertPath, params->clientKeyPath, caCertFilePath.c_str(), params->clientCACertPath); + sharedCTX = params->getCTX(certFilePath, keyFilePath, + caCertFilePath.empty() ? params->clientCACertFilename : caCertFilePath.c_str(), caCertPath); + } else if (options.ssl_client_ca_cert_name || options.ssl_client_ca_cert_path) { + std::string caCertFilePath; + if (options.ssl_client_ca_cert_name) { + caCertFilePath = Layout::get()->relative_to(caCertPath, options.ssl_client_ca_cert_name); + } + sharedCTX = params->getCTX(params->clientCertPath, params->clientKeyPath, + caCertFilePath.empty() ? params->clientCACertFilename : caCertFilePath.c_str(), caCertPath); } else if (nps && !nps->client_cert_file.empty()) { // If no overrides available, try the available nextHopProperty by reading from context mappings sharedCTX = diff --git a/src/proxy/http/HttpSM.cc b/src/proxy/http/HttpSM.cc index 524a1405748..cc2223d70bc 100644 --- a/src/proxy/http/HttpSM.cc +++ b/src/proxy/http/HttpSM.cc @@ -5804,6 +5804,7 @@ HttpSM::do_http_server_open(bool raw, bool only_direct) opt.set_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; + opt.ssl_client_ca_cert_path = t_state.txn_conf->ssl_client_ca_cert_path; if (is_private()) { // If the connection to origin is private, don't try to negotiate the higher overhead H2 opt.alpn_protocols_array_size = -1; diff --git a/src/proxy/http/PreWarmManager.cc b/src/proxy/http/PreWarmManager.cc index 5fca8030913..594fc242237 100644 --- a/src/proxy/http/PreWarmManager.cc +++ b/src/proxy/http/PreWarmManager.cc @@ -576,6 +576,7 @@ PreWarmSM::_connect(const IpEndpoint &addr) opt.ssl_client_cert_name = http_conf_params->oride.ssl_client_cert_filename; opt.ssl_client_private_key_name = http_conf_params->oride.ssl_client_private_key_filename; opt.ssl_client_ca_cert_name = http_conf_params->oride.ssl_client_ca_cert_filename; + opt.ssl_client_ca_cert_path = http_conf_params->oride.ssl_client_ca_cert_path; SCOPED_MUTEX_LOCK(lock, mutex, this_ethread()); connect_action_handle = sslNetProcessor.connect_re(this, &addr.sa, opt); diff --git a/tests/gold_tests/tls/tls_verify_ca_override.test.py b/tests/gold_tests/tls/tls_verify_ca_override.test.py index 610bc016031..1774f18150f 100644 --- a/tests/gold_tests/tls/tls_verify_ca_override.test.py +++ b/tests/gold_tests/tls/tls_verify_ca_override.test.py @@ -17,7 +17,7 @@ # limitations under the License. Test.Summary = ''' -Test tls server certificate verification options. Exercise conf_remap for ca bundle +Test tls server certificate verification options. Exercise conf_remap for ca bundle path and file. ''' # Define default ATS @@ -58,18 +58,25 @@ ts.addSSLfile("ssl/signer2.pem") ts.addSSLfile("ssl/signer2.key") + +def ca_cert_overrides(filename): + return ( + f'@pparam=proxy.config.ssl.client.CA.cert.path={ts.Variables.SSLDir} ' + f'@pparam=proxy.config.ssl.client.CA.cert.filename={filename}') + + 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.SSL_Port, ts.Variables.SSLDir, "signer.pem")) + f'map /case1 https://127.0.0.1:{server1.Variables.SSL_Port}/ ' + f'@plugin=conf_remap.so {ca_cert_overrides("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.SSL_Port, ts.Variables.SSLDir, "signer2.pem")) + f'map /badcase1 https://127.0.0.1:{server1.Variables.SSL_Port}/ ' + f'@plugin=conf_remap.so {ca_cert_overrides("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.SSL_Port, ts.Variables.SSLDir, "signer2.pem")) + f'map /case2 https://127.0.0.1:{server2.Variables.SSL_Port}/ ' + f'@plugin=conf_remap.so {ca_cert_overrides("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.SSL_Port, ts.Variables.SSLDir, "signer.pem")) + f'map /badcase2 https://127.0.0.1:{server2.Variables.SSL_Port}/ ' + f'@plugin=conf_remap.so {ca_cert_overrides("signer.pem")}') ts.Disk.ssl_multicert_yaml.AddLines( """