This is an automated email from the ASF dual-hosted git repository. mmerli pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/pulsar-client-cpp.git
The following commit(s) were added to refs/heads/main by this push: new 9eb9011 Add TLS transport config (#12) 9eb9011 is described below commit 9eb9011745f44b4526cff5135710f598691e348b Author: Zixuan Liu <node...@gmail.com> AuthorDate: Sat Oct 1 05:25:41 2022 +0800 Add TLS transport config (#12) Co-authored-by: Matteo Merli <mme...@apache.org> --- include/pulsar/ClientConfiguration.h | 24 +++++++ lib/ClientConfiguration.cc | 16 +++++ lib/ClientConfigurationImpl.h | 2 + lib/ClientConnection.cc | 26 ++++---- lib/HTTPLookupService.cc | 7 +++ lib/HTTPLookupService.h | 2 + lib/c/c_ClientConfiguration.cc | 18 ++++++ tests/AuthBasicTest.cc | 117 +++++++++++++++++++++++++++++++++++ 8 files changed, 201 insertions(+), 11 deletions(-) diff --git a/include/pulsar/ClientConfiguration.h b/include/pulsar/ClientConfiguration.h index 451ab4e..32ad32b 100644 --- a/include/pulsar/ClientConfiguration.h +++ b/include/pulsar/ClientConfiguration.h @@ -156,6 +156,30 @@ class PULSAR_PUBLIC ClientConfiguration { */ bool isUseTls() const; + /** + * Set the path to the TLS private key file. + * + * @param tlsPrivateKeyFilePath + */ + ClientConfiguration& setTlsPrivateKeyFilePath(const std::string& tlsKeyFilePath); + + /** + * @return the path to the TLS private key file + */ + const std::string& getTlsPrivateKeyFilePath() const; + + /** + * Set the path to the TLS certificate file. + * + * @param tlsCertificateFilePath + */ + ClientConfiguration& setTlsCertificateFilePath(const std::string& tlsCertificateFilePath); + + /** + * @return the path to the TLS certificate file + */ + const std::string& getTlsCertificateFilePath() const; + /** * Set the path to the trusted TLS certificate file. * diff --git a/lib/ClientConfiguration.cc b/lib/ClientConfiguration.cc index 4072f63..ce6944b 100644 --- a/lib/ClientConfiguration.cc +++ b/lib/ClientConfiguration.cc @@ -82,6 +82,22 @@ ClientConfiguration& ClientConfiguration::setValidateHostName(bool validateHostN bool ClientConfiguration::isValidateHostName() const { return impl_->validateHostName; } +ClientConfiguration& ClientConfiguration::setTlsPrivateKeyFilePath(const std::string& filePath) { + impl_->tlsPrivateKeyFilePath = filePath; + return *this; +} + +const std::string& ClientConfiguration::getTlsPrivateKeyFilePath() const { return impl_->tlsPrivateKeyFilePath; } + +ClientConfiguration& ClientConfiguration::setTlsCertificateFilePath(const std::string& filePath) { + impl_->tlsCertificateFilePath = filePath; + return *this; +} + +const std::string& ClientConfiguration::getTlsCertificateFilePath() const { + return impl_->tlsCertificateFilePath; +} + ClientConfiguration& ClientConfiguration::setTlsTrustCertsFilePath(const std::string& filePath) { impl_->tlsTrustCertsFilePath = filePath; return *this; diff --git a/lib/ClientConfigurationImpl.h b/lib/ClientConfigurationImpl.h index 887ecf2..f6f2a85 100644 --- a/lib/ClientConfigurationImpl.h +++ b/lib/ClientConfigurationImpl.h @@ -32,6 +32,8 @@ struct ClientConfigurationImpl { int concurrentLookupRequest{50000}; std::string logConfFilePath; bool useTls{false}; + std::string tlsPrivateKeyFilePath; + std::string tlsCertificateFilePath; std::string tlsTrustCertsFilePath; bool tlsAllowInsecureConnection{false}; unsigned int statsIntervalInSeconds{600}; // 10 minutes diff --git a/lib/ClientConnection.cc b/lib/ClientConnection.cc index 20e3458..a037ff3 100644 --- a/lib/ClientConnection.cc +++ b/lib/ClientConnection.cc @@ -228,26 +228,30 @@ ClientConnection::ClientConnection(const std::string& logicalAddress, const std: return; } + std::string tlsCertificates = clientConfiguration.getTlsCertificateFilePath(); + std::string tlsPrivateKey = clientConfiguration.getTlsPrivateKeyFilePath(); + AuthenticationDataPtr authData; if (authentication_->getAuthData(authData) == ResultOk && authData->hasDataForTls()) { - std::string tlsCertificates = authData->getTlsCertificates(); - std::string tlsPrivateKey = authData->getTlsPrivateKey(); - - if (file_exists(tlsCertificates)) { - ctx.use_certificate_file(tlsCertificates, boost::asio::ssl::context::pem); - } else { + tlsCertificates = authData->getTlsCertificates(); + tlsPrivateKey = authData->getTlsPrivateKey(); + if (!file_exists(tlsCertificates)) { LOG_ERROR(tlsCertificates << ": No such tlsCertificates"); close(); return; } - - if (file_exists(tlsPrivateKey)) { - ctx.use_private_key_file(tlsPrivateKey, boost::asio::ssl::context::pem); - } else { - LOG_ERROR(tlsPrivateKey << ": No such tlsPrivateKey"); + if (!file_exists(tlsCertificates)) { + LOG_ERROR(tlsCertificates << ": No such tlsCertificates"); close(); return; } + ctx.use_private_key_file(tlsPrivateKey, boost::asio::ssl::context::pem); + ctx.use_certificate_file(tlsCertificates, boost::asio::ssl::context::pem); + } else { + if (file_exists(tlsPrivateKey) && file_exists(tlsCertificates)) { + ctx.use_private_key_file(tlsPrivateKey, boost::asio::ssl::context::pem); + ctx.use_certificate_file(tlsCertificates, boost::asio::ssl::context::pem); + } } tlsSocket_ = ExecutorService::createTlsSocket(socket_, ctx); diff --git a/lib/HTTPLookupService.cc b/lib/HTTPLookupService.cc index 554d755..91f5d79 100644 --- a/lib/HTTPLookupService.cc +++ b/lib/HTTPLookupService.cc @@ -55,6 +55,8 @@ HTTPLookupService::HTTPLookupService(ServiceNameResolver &serviceNameResolver, serviceNameResolver_(serviceNameResolver), authenticationPtr_(authData), lookupTimeoutInSeconds_(clientConfiguration.getOperationTimeoutSeconds()), + tlsPrivateFilePath_(clientConfiguration.getTlsPrivateKeyFilePath()), + tlsCertificateFilePath_(clientConfiguration.getTlsCertificateFilePath()), tlsTrustCertsFilePath_(clientConfiguration.getTlsTrustCertsFilePath()), isUseTls_(clientConfiguration.isUseTls()), tlsAllowInsecure_(clientConfiguration.isTlsAllowInsecureConnection()), @@ -231,6 +233,11 @@ Result HTTPLookupService::sendHTTPRequest(std::string completeUrl, std::string & if (authDataContent->hasDataForTls()) { curl_easy_setopt(handle, CURLOPT_SSLCERT, authDataContent->getTlsCertificates().c_str()); curl_easy_setopt(handle, CURLOPT_SSLKEY, authDataContent->getTlsPrivateKey().c_str()); + } else { + if (!tlsPrivateFilePath_.empty() && !tlsCertificateFilePath_.empty()) { + curl_easy_setopt(handle, CURLOPT_SSLCERT, tlsCertificateFilePath_.c_str()); + curl_easy_setopt(handle, CURLOPT_SSLKEY, tlsPrivateFilePath_.c_str()); + } } } diff --git a/lib/HTTPLookupService.h b/lib/HTTPLookupService.h index 0656d11..c9dfc57 100644 --- a/lib/HTTPLookupService.h +++ b/lib/HTTPLookupService.h @@ -46,6 +46,8 @@ class HTTPLookupService : public LookupService, public std::enable_shared_from_t ServiceNameResolver& serviceNameResolver_; AuthenticationPtr authenticationPtr_; int lookupTimeoutInSeconds_; + std::string tlsPrivateFilePath_; + std::string tlsCertificateFilePath_; std::string tlsTrustCertsFilePath_; bool isUseTls_; bool tlsAllowInsecure_; diff --git a/lib/c/c_ClientConfiguration.cc b/lib/c/c_ClientConfiguration.cc index 8f4051d..86bee89 100644 --- a/lib/c/c_ClientConfiguration.cc +++ b/lib/c/c_ClientConfiguration.cc @@ -119,6 +119,24 @@ int pulsar_client_configuration_is_validate_hostname(pulsar_client_configuration return conf->conf.isValidateHostName(); } +void pulsar_client_configuration_set_tls_private_key_file_path(pulsar_client_configuration_t *conf, + const char *tlsPrivateKeyFilePath) { + conf->conf.setTlsPrivateKeyFilePath(tlsPrivateKeyFilePath); +} + +const char *pulsar_client_configuration_get_tls_private_key_file_path(pulsar_client_configuration_t *conf) { + return conf->conf.getTlsPrivateKeyFilePath().c_str(); +} + +void pulsar_client_configuration_set_tls_certificate_file_path(pulsar_client_configuration_t *conf, + const char *tlsCertificateFilePath) { + conf->conf.setTlsCertificateFilePath(tlsCertificateFilePath); +} + +const char *pulsar_client_configuration_get_tls_certificate_file_path(pulsar_client_configuration_t *conf) { + return conf->conf.getTlsCertificateFilePath().c_str(); +} + void pulsar_client_configuration_set_tls_trust_certs_file_path(pulsar_client_configuration_t *conf, const char *tlsTrustCertsFilePath) { conf->conf.setTlsTrustCertsFilePath(tlsTrustCertsFilePath); diff --git a/tests/AuthBasicTest.cc b/tests/AuthBasicTest.cc index 296eff3..dc9246e 100644 --- a/tests/AuthBasicTest.cc +++ b/tests/AuthBasicTest.cc @@ -28,6 +28,13 @@ using namespace pulsar; static const std::string serviceUrl = "pulsar://localhost:6650"; static const std::string serviceUrlHttp = "http://localhost:8080"; +static const std::string serviceUrlTls = "pulsar+ssl://localhost:6651"; +static const std::string serviceUrlHttps = "https://localhost:8443"; +static const std::string caPath = "../../pulsar-broker/src/test/resources/authentication/tls/cacert.pem"; +static const std::string clientCertificatePath = + "../../pulsar-broker/src/test/resources/authentication/tls/client-cert.pem"; +static const std::string clientPrivateKeyPath = + "../../pulsar-broker/src/test/resources/authentication/tls/client-key.pem"; TEST(AuthPluginBasic, testBasic) { ClientConfiguration config = ClientConfiguration(); @@ -138,3 +145,113 @@ TEST(AuthPluginBasic, testLoadAuth) { ASSERT_EQ(data->hasDataForTls(), false); ASSERT_EQ(data->hasDataForHttp(), true); } + +TEST(AuthPluginBasic, testAuthBasicWithServiceUrlTlsWithTlsTransport) { + ClientConfiguration config = ClientConfiguration(); + + config.setTlsPrivateKeyFilePath(clientPrivateKeyPath); + config.setTlsCertificateFilePath(clientCertificatePath); + config.setTlsTrustCertsFilePath(caPath); + + AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456"); + + ASSERT_TRUE(auth != NULL); + ASSERT_EQ(auth->getAuthMethodName(), "basic"); + + pulsar::AuthenticationDataPtr data; + ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk); + ASSERT_EQ(data->hasDataFromCommand(), true); + ASSERT_EQ(data->getCommandData(), "admin:123456"); + ASSERT_EQ(data->hasDataForTls(), false); + ASSERT_EQ(data->hasDataForHttp(), true); + + config.setAuth(auth); + Client client(serviceUrlTls, config); + + std::string topicName = "persistent://private/auth/test-basic"; + + Producer producer; + Result result = client.createProducer(topicName, producer); + ASSERT_EQ(ResultOk, result); + producer.close(); +} + +TEST(AuthPluginBasic, testAuthBasicWithServiceUrlHttpsWithTlsTransport) { + ClientConfiguration config = ClientConfiguration(); + + config.setTlsPrivateKeyFilePath(clientPrivateKeyPath); + config.setTlsCertificateFilePath(clientCertificatePath); + config.setTlsTrustCertsFilePath(caPath); + + AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456"); + + ASSERT_TRUE(auth != NULL); + ASSERT_EQ(auth->getAuthMethodName(), "basic"); + + pulsar::AuthenticationDataPtr data; + ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk); + ASSERT_EQ(data->hasDataFromCommand(), true); + ASSERT_EQ(data->getCommandData(), "admin:123456"); + ASSERT_EQ(data->hasDataForTls(), false); + ASSERT_EQ(data->hasDataForHttp(), true); + + config.setAuth(auth); + Client client(serviceUrlHttps, config); + + std::string topicName = "persistent://private/auth/test-basic"; + + Producer producer; + Result result = client.createProducer(topicName, producer); + ASSERT_EQ(ResultOk, result); + producer.close(); +} + +TEST(AuthPluginBasic, testAuthBasicWithServiceUrlTlsNoTlsTransport) { + ClientConfiguration config = ClientConfiguration(); + + AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456"); + + ASSERT_TRUE(auth != NULL); + ASSERT_EQ(auth->getAuthMethodName(), "basic"); + + pulsar::AuthenticationDataPtr data; + ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk); + ASSERT_EQ(data->hasDataFromCommand(), true); + ASSERT_EQ(data->getCommandData(), "admin:123456"); + ASSERT_EQ(data->hasDataForTls(), false); + ASSERT_EQ(data->hasDataForHttp(), true); + + config.setAuth(auth); + Client client(serviceUrlTls, config); + + std::string topicName = "persistent://private/auth/test-basic"; + + Producer producer; + Result result = client.createProducer(topicName, producer); + ASSERT_EQ(ResultConnectError, result); +} + +TEST(AuthPluginBasic, testAuthBasicWithServiceUrlHttpsNoTlsTransport) { + ClientConfiguration config = ClientConfiguration(); + + AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456"); + + ASSERT_TRUE(auth != NULL); + ASSERT_EQ(auth->getAuthMethodName(), "basic"); + + pulsar::AuthenticationDataPtr data; + ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk); + ASSERT_EQ(data->hasDataFromCommand(), true); + ASSERT_EQ(data->getCommandData(), "admin:123456"); + ASSERT_EQ(data->hasDataForTls(), false); + ASSERT_EQ(data->hasDataForHttp(), true); + + config.setAuth(auth); + Client client(serviceUrlHttps, config); + + std::string topicName = "persistent://private/auth/test-basic"; + + Producer producer; + Result result = client.createProducer(topicName, producer); + ASSERT_EQ(ResultConnectError, result); +}