You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by mm...@apache.org on 2022/09/30 21:25:46 UTC

[pulsar-client-cpp] branch main updated: Add TLS transport config (#12)

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 <no...@gmail.com>
AuthorDate: Sat Oct 1 05:25:41 2022 +0800

    Add TLS transport config (#12)
    
    Co-authored-by: Matteo Merli <mm...@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);
+}