From 48e9be375813e15317848a5bd766d6b46e1c804b Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Thu, 30 Apr 2020 23:15:04 +0800 Subject: [PATCH 01/11] tls with keystore type config to support multi CAs --- buildtools/src/main/resources/log4j2.xml | 2 +- pom.xml | 2 + .../pulsar/broker/ServiceConfiguration.java | 51 +++ .../AuthenticationDataHttps.java | 1 - .../OneStageAuthenticationState.java | 2 +- .../service/PulsarChannelInitializer.java | 60 ++- .../apache/pulsar/broker/web/WebService.java | 45 ++- .../client/api/TlsProducerConsumerBase.java | 10 + .../client/admin/PulsarAdminBuilder.java | 58 +++ .../internal/PulsarAdminBuilderImpl.java | 43 +++ .../internal/http/AsyncHttpConnector.java | 47 ++- .../api/AuthenticationDataProvider.java | 9 + .../pulsar/client/api/ClientBuilder.java | 64 ++++ .../pulsar/client/api/KeyStoreParams.java | 35 ++ pulsar-client-auth-keystoretls/pom.xml | 103 ++++++ .../auth/AuthenticationDataKeyStoreTls.java | 45 +++ .../impl/auth/AuthenticationKeyStoreTls.java | 136 +++++++ .../client/TlsProducerConsumerBase.java | 150 ++++++++ .../client/TlsProducerConsumerTest.java | 135 +++++++ .../src/test/resources/broker.keystore.jks | Bin 0 -> 2767 bytes .../src/test/resources/broker.truststore.jks | Bin 0 -> 731 bytes .../src/test/resources/brokerKeyStorePW.txt | 1 + .../src/test/resources/brokerTrustStorePW.txt | 1 + .../src/test/resources/client.keystore.jks | Bin 0 -> 2767 bytes .../src/test/resources/client.truststore.jks | Bin 0 -> 731 bytes .../src/test/resources/clientKeyStorePW.txt | 1 + .../src/test/resources/clientTrustStorePW.txt | 1 + .../pulsar/client/impl/ClientBuilderImpl.java | 47 ++- .../apache/pulsar/client/impl/HttpClient.java | 68 ++-- .../pulsar/client/impl/HttpLookupService.java | 3 +- .../client/impl/PulsarChannelInitializer.java | 38 +- .../client/impl/auth/AuthenticationTls.java | 4 +- .../impl/conf/ClientConfigurationData.java | 12 + pulsar-common/pom.xml | 4 + .../util/ClientSslContextRefresher.java | 67 ---- .../common/util/DefaultSslContextBuilder.java | 18 +- .../util/NettyClientSslContextRefresher.java | 74 ++++ ...java => NettyServerSslContextBuilder.java} | 33 +- .../util/SslContextAutoRefreshBuilder.java | 46 +-- .../util/keystoretls/KeyStoreSSLContext.java | 348 ++++++++++++++++++ .../keystoretls/NetSslContextBuilder.java | 92 +++++ .../NettySSLEngineAutoRefreshBuilder.java | 145 ++++++++ .../SSLContextValidatorEngine.java | 176 +++++++++ .../SslContextFactoryWithAutoRefresh.java | 63 ++++ .../common/util/keystoretls/package-info.java | 22 ++ .../src/test/resources/broker.keystore.jks | Bin 0 -> 2767 bytes .../src/test/resources/broker.truststore.jks | Bin 0 -> 731 bytes .../src/test/resources/brokerKeyStorePW.txt | 1 + .../src/test/resources/brokerTrustStorePW.txt | 1 + pulsar-common/src/test/resources/ca-cert | 16 + pulsar-common/src/test/resources/ca-cert.srl | 1 + pulsar-common/src/test/resources/ca-key | 30 ++ pulsar-common/src/test/resources/cert-file | 17 + pulsar-common/src/test/resources/cert-signed | 22 ++ .../src/test/resources/client.keystore.jks | Bin 0 -> 2767 bytes .../src/test/resources/client.truststore.jks | Bin 0 -> 731 bytes .../src/test/resources/clientKeyStorePW.txt | 1 + .../src/test/resources/clientTrustStorePW.txt | 1 + .../test/resources/old/broker.keystore.jks | Bin 0 -> 2928 bytes .../test/resources/old/broker.truststore.jks | Bin 0 -> 797 bytes .../test/resources/old/brokerKeyStorePW.txt | 1 + .../test/resources/old/brokerTrustStorePW.txt | 1 + .../test/resources/old/client.keystore.jks | Bin 0 -> 2926 bytes .../test/resources/old/client.truststore.jks | Bin 0 -> 797 bytes .../test/resources/old/clientKeyStorePW.txt | 1 + .../test/resources/old/clientTrustStorePW.txt | 1 + .../service/ServiceChannelInitializer.java | 47 ++- .../service/server/ServerManager.java | 36 +- .../service/server/ServiceConfig.java | 234 ++---------- .../proxy/server/DirectProxyHandler.java | 11 +- .../proxy/server/ProxyConfiguration.java | 116 +++++- .../pulsar/proxy/server/ProxyConnection.java | 9 +- .../server/ServiceChannelInitializer.java | 84 ++++- .../apache/pulsar/proxy/server/WebServer.java | 37 +- 74 files changed, 2460 insertions(+), 470 deletions(-) create mode 100644 pulsar-client-api/src/main/java/org/apache/pulsar/client/api/KeyStoreParams.java create mode 100644 pulsar-client-auth-keystoretls/pom.xml create mode 100644 pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java create mode 100644 pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/broker.keystore.jks create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/broker.truststore.jks create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/client.keystore.jks create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/client.truststore.jks create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt create mode 100644 pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt delete mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/ClientSslContextRefresher.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyClientSslContextRefresher.java rename pulsar-common/src/main/java/org/apache/pulsar/common/util/{NettySslContextBuilder.java => NettyServerSslContextBuilder.java} (52%) create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NetSslContextBuilder.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SSLContextValidatorEngine.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SslContextFactoryWithAutoRefresh.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/package-info.java create mode 100644 pulsar-common/src/test/resources/broker.keystore.jks create mode 100644 pulsar-common/src/test/resources/broker.truststore.jks create mode 100644 pulsar-common/src/test/resources/brokerKeyStorePW.txt create mode 100644 pulsar-common/src/test/resources/brokerTrustStorePW.txt create mode 100644 pulsar-common/src/test/resources/ca-cert create mode 100644 pulsar-common/src/test/resources/ca-cert.srl create mode 100644 pulsar-common/src/test/resources/ca-key create mode 100644 pulsar-common/src/test/resources/cert-file create mode 100644 pulsar-common/src/test/resources/cert-signed create mode 100644 pulsar-common/src/test/resources/client.keystore.jks create mode 100644 pulsar-common/src/test/resources/client.truststore.jks create mode 100644 pulsar-common/src/test/resources/clientKeyStorePW.txt create mode 100644 pulsar-common/src/test/resources/clientTrustStorePW.txt create mode 100644 pulsar-common/src/test/resources/old/broker.keystore.jks create mode 100644 pulsar-common/src/test/resources/old/broker.truststore.jks create mode 100644 pulsar-common/src/test/resources/old/brokerKeyStorePW.txt create mode 100644 pulsar-common/src/test/resources/old/brokerTrustStorePW.txt create mode 100644 pulsar-common/src/test/resources/old/client.keystore.jks create mode 100644 pulsar-common/src/test/resources/old/client.truststore.jks create mode 100644 pulsar-common/src/test/resources/old/clientKeyStorePW.txt create mode 100644 pulsar-common/src/test/resources/old/clientTrustStorePW.txt diff --git a/buildtools/src/main/resources/log4j2.xml b/buildtools/src/main/resources/log4j2.xml index 2fdc2d05ae32e..4da6f96997931 100644 --- a/buildtools/src/main/resources/log4j2.xml +++ b/buildtools/src/main/resources/log4j2.xml @@ -30,7 +30,7 @@ - + diff --git a/pom.xml b/pom.xml index ddeda8fc081da..949155f9937e1 100644 --- a/pom.xml +++ b/pom.xml @@ -108,6 +108,8 @@ flexible messaging model and an intuitive client API. pulsar-broker-auth-sasl pulsar-client-auth-sasl + pulsar-client-auth-keystoretls + pulsar-transaction diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java index b9d6dcb6889ad..f46edd8527150 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java @@ -77,6 +77,8 @@ public class ServiceConfiguration implements PulsarConfiguration { @Category private static final String CATEGORY_TLS = "TLS"; @Category + private static final String CATEGORY_KEYSTORE_TLS = "KeyStoreTLS"; + @Category private static final String CATEGORY_AUTHENTICATION = "Authentication"; @Category private static final String CATEGORY_AUTHORIZATION = "Authorization"; @@ -1576,6 +1578,55 @@ public class ServiceConfiguration implements PulsarConfiguration { private String transactionMetadataStoreProviderClassName = "org.apache.pulsar.transaction.coordinator.impl.InMemTransactionMetadataStoreProvider"; + /**** --- KeyStore TLS config variables --- ****/ + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Enable TLS with KeyStore type configuration in broker" + ) + private boolean tlsEnabledWithKeyStore = false; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS Provider (JDK or OpenSSL)" + ) + private String tlsProvider = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore type configuration in broker: JKS, PKCS12" + ) + private String tlsKeyStoreType = "JKS"; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore path in broker" + ) + private String tlsKeyStore = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore password in broker" + ) + private String tlsKeyStorePassword = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore type configuration in broker: JKS, PKCS12" + ) + private String tlsTrustStoreType = "JKS"; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore path in broker" + ) + private String tlsTrustStore = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore password in broker" + ) + private String tlsTrustStorePassword = null; + /** * @deprecated See {@link #getConfigurationStoreServers} */ diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java index 03a9bd3ce4d4c..4e1d33b5ec5e7 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataHttps.java @@ -34,7 +34,6 @@ public AuthenticationDataHttps(HttpServletRequest request) { /* * TLS */ - @Override public boolean hasDataFromTls() { return (certificates != null); diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/OneStageAuthenticationState.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/OneStageAuthenticationState.java index 06b17496b5189..f2667c3b46c52 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/OneStageAuthenticationState.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/OneStageAuthenticationState.java @@ -42,7 +42,7 @@ public OneStageAuthenticationState(AuthData authData, SSLSession sslSession, AuthenticationProvider provider) throws AuthenticationException { this.authenticationDataSource = new AuthenticationDataCommand( - new String(authData.getBytes(), UTF_8), remoteAddress, sslSession);; + new String(authData.getBytes(), UTF_8), remoteAddress, sslSession); this.authRole = provider.authenticate(authenticationDataSource); } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java index ce16a7ea99f42..3e1f533956589 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java @@ -20,23 +20,24 @@ import static org.apache.bookkeeper.util.SafeRunnable.safeRun; -import java.net.SocketAddress; -import java.util.concurrent.TimeUnit; - -import org.apache.pulsar.broker.PulsarService; -import org.apache.pulsar.broker.ServiceConfiguration; -import org.apache.pulsar.common.protocol.ByteBufPair; -import org.apache.pulsar.common.protocol.Commands; -import org.apache.pulsar.common.util.NettySslContextBuilder; - import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; - import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.flow.FlowControlHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslHandler; +import java.net.SocketAddress; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.PulsarService; +import org.apache.pulsar.broker.ServiceConfiguration; +import org.apache.pulsar.common.protocol.ByteBufPair; +import org.apache.pulsar.common.protocol.Commands; +import org.apache.pulsar.common.util.NettyServerSslContextBuilder; +import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; +import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; @Slf4j public class PulsarChannelInitializer extends ChannelInitializer { @@ -45,8 +46,10 @@ public class PulsarChannelInitializer extends ChannelInitializer private final PulsarService pulsar; private final boolean enableTls; - private final NettySslContextBuilder sslCtxRefresher; + private final boolean tlsEnabledWithKeyStore; + private SslContextAutoRefreshBuilder sslCtxRefresher; private final ServiceConfiguration brokerConf; + private NettySSLEngineAutoRefreshBuilder nettySSLEngineRefreshBuilder; // This cache is used to maintain a list of active connections to iterate over them // We keep weak references to have the cache to be auto cleaned up when the connections @@ -66,13 +69,30 @@ public PulsarChannelInitializer(PulsarService pulsar, boolean enableTLS) throws super(); this.pulsar = pulsar; this.enableTls = enableTLS; + ServiceConfiguration serviceConfig = pulsar.getConfiguration(); + this.tlsEnabledWithKeyStore = serviceConfig.isTlsEnabledWithKeyStore(); if (this.enableTls) { - ServiceConfiguration serviceConfig = pulsar.getConfiguration(); - sslCtxRefresher = new NettySslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), - serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), - serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), - serviceConfig.isTlsRequireTrustedClientCertOnConnect(), - serviceConfig.getTlsCertRefreshCheckDurationSec()); + if (tlsEnabledWithKeyStore) { + nettySSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + serviceConfig.getTlsProvider(), + serviceConfig.getTlsKeyStoreType(), + serviceConfig.getTlsKeyStore(), + serviceConfig.getTlsKeyStorePassword(), + serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustStoreType(), + serviceConfig.getTlsTrustStore(), + serviceConfig.getTlsTrustStorePassword(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCiphers(), + serviceConfig.getTlsProtocols(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } else { + sslCtxRefresher = new NettyServerSslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), + serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } } else { this.sslCtxRefresher = null; } @@ -86,7 +106,11 @@ public PulsarChannelInitializer(PulsarService pulsar, boolean enableTLS) throws @Override protected void initChannel(SocketChannel ch) throws Exception { if (this.enableTls) { - ch.pipeline().addLast(TLS_HANDLER, sslCtxRefresher.get().newHandler(ch.alloc())); + if (this.tlsEnabledWithKeyStore) { + ch.pipeline().addLast(TLS_HANDLER, new SslHandler(nettySSLEngineRefreshBuilder.get())); + } else { + ch.pipeline().addLast(TLS_HANDLER, sslCtxRefresher.get().newHandler(ch.alloc())); + } ch.pipeline().addLast("ByteBufPairEncoder", ByteBufPair.COPYING_ENCODER); } else { ch.pipeline().addLast("ByteBufPairEncoder", ByteBufPair.ENCODER); diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/WebService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/WebService.java index e111fbcd1af5c..feb0e497f8e91 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/WebService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/WebService.java @@ -19,29 +19,19 @@ package org.apache.pulsar.broker.web; import com.google.common.collect.Lists; - import io.prometheus.client.jetty.JettyStatisticsCollector; - -import java.io.IOException; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TimeZone; - import javax.servlet.DispatcherType; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; - import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.PulsarService; +import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -105,13 +95,30 @@ public WebService(PulsarService pulsar) throws PulsarServerException { Optional tlsPort = pulsar.getConfiguration().getWebServicePortTls(); if (tlsPort.isPresent()) { try { - SslContextFactory sslCtxFactory = SecurityUtility.createSslContextFactory( - pulsar.getConfiguration().isTlsAllowInsecureConnection(), - pulsar.getConfiguration().getTlsTrustCertsFilePath(), - pulsar.getConfiguration().getTlsCertificateFilePath(), - pulsar.getConfiguration().getTlsKeyFilePath(), - pulsar.getConfiguration().isTlsRequireTrustedClientCertOnConnect(), true, - pulsar.getConfiguration().getTlsCertRefreshCheckDurationSec()); + SslContextFactory sslCtxFactory; + ServiceConfiguration config = pulsar.getConfiguration(); + if (config.isTlsEnabledWithKeyStore()) { + sslCtxFactory = KeyStoreSSLContext.createSslContextFactory( + config.getTlsProvider(), + config.getTlsKeyStoreType(), + config.getTlsKeyStore(), + config.getTlsKeyStorePassword(), + config.isTlsAllowInsecureConnection(), + config.getTlsTrustStoreType(), + config.getTlsTrustStore(), + config.getTlsTrustStorePassword(), + config.isTlsRequireTrustedClientCertOnConnect(), + config.getTlsCertRefreshCheckDurationSec() + ); + } else { + sslCtxFactory = SecurityUtility.createSslContextFactory( + config.isTlsAllowInsecureConnection(), + config.getTlsTrustCertsFilePath(), + config.getTlsCertificateFilePath(), + config.getTlsKeyFilePath(), + config.isTlsRequireTrustedClientCertOnConnect(), true, + config.getTlsCertRefreshCheckDurationSec()); + } httpsConnector = new PulsarServerConnector(server, 1, 1, sslCtxFactory); httpsConnector.setPort(tlsPort.get()); httpsConnector.setHost(pulsar.getBindAddress()); diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java index b49be05f61aa6..ba25be4761ddd 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java @@ -21,11 +21,13 @@ import static org.mockito.Mockito.spy; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.impl.auth.AuthenticationTls; import org.apache.pulsar.common.policies.data.ClusterData; @@ -70,6 +72,14 @@ protected void internalSetUpForBroker() throws Exception { Set tlsProtocols = Sets.newConcurrentHashSet(); tlsProtocols.add("TLSv1.2"); conf.setTlsProtocols(tlsProtocols); + + + conf.setSuperUserRoles(Sets.newHashSet("a-super-user")); + conf.setAuthenticationEnabled(true); + conf.setAuthorizationEnabled(true); + Set providers = new HashSet<>(); + providers.add(AuthenticationProviderTls.class.getName()); + conf.setAuthenticationProviders(providers); } protected void internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdminBuilder.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdminBuilder.java index 3f6dbf477394f..5bcc7257a78fc 100644 --- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdminBuilder.java +++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/PulsarAdminBuilder.java @@ -19,6 +19,7 @@ package org.apache.pulsar.client.admin; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.pulsar.client.api.Authentication; @@ -170,6 +171,63 @@ PulsarAdminBuilder authentication(String authPluginClassName, Map tlsCiphers); + + /** + * The SSL protocol used to generate the SSLContext. + * Default setting is TLS, which is fine for most cases. + * Allowed values in recent JVMs are TLS, TLSv1.1 and TLSv1.2. SSL, SSLv2. + * + * @param tlsProtocols + */ + PulsarAdminBuilder tlsProtocols(Set tlsProtocols); + /** * This sets the connection time out for the pulsar admin client. * diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PulsarAdminBuilderImpl.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PulsarAdminBuilderImpl.java index 6cc4d69c32823..d62ac334d36ec 100644 --- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PulsarAdminBuilderImpl.java +++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/PulsarAdminBuilderImpl.java @@ -19,6 +19,7 @@ package org.apache.pulsar.client.admin.internal; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.pulsar.client.admin.PulsarAdmin; @@ -103,6 +104,48 @@ public PulsarAdminBuilder enableTlsHostnameVerification(boolean enableTlsHostnam return this; } + @Override + public PulsarAdminBuilder useKeyStoreTls(boolean useKeyStoreTls) { + conf.setUseKeyStoreTls(useKeyStoreTls); + return this; + } + + @Override + public PulsarAdminBuilder sslProvider(String sslProvider) { + conf.setSslProvider(sslProvider); + return this; + } + + @Override + public PulsarAdminBuilder tlsTrustStoreType(String tlsTrustStoreType) { + conf.setTlsTrustStoreType(tlsTrustStoreType); + return this; + } + + @Override + public PulsarAdminBuilder tlsTrustStorePath(String tlsTrustStorePath) { + conf.setTlsTrustStorePath(tlsTrustStorePath); + return this; + } + + @Override + public PulsarAdminBuilder tlsTrustStorePassword(String tlsTrustStorePassword) { + conf.setTlsTrustStorePassword(tlsTrustStorePassword); + return this; + } + + @Override + public PulsarAdminBuilder tlsCiphers(Set tlsCiphers) { + conf.setTlsCiphers(tlsCiphers); + return this; + } + + @Override + public PulsarAdminBuilder tlsProtocols(Set tlsProtocols) { + conf.setTlsProtocols(tlsProtocols); + return this; + } + @Override public PulsarAdminBuilder connectionTimeout(int connectionTimeout, TimeUnit connectionTimeoutUnit) { this.connectTimeout = connectionTimeout; diff --git a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/http/AsyncHttpConnector.java b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/http/AsyncHttpConnector.java index dd4a83ed900bd..27bc25cf65b6f 100644 --- a/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/http/AsyncHttpConnector.java +++ b/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/http/AsyncHttpConnector.java @@ -38,6 +38,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import javax.net.ssl.SSLContext; import javax.ws.rs.client.Client; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response.Status; @@ -50,9 +51,11 @@ import org.apache.pulsar.PulsarVersion; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.api.AuthenticationDataProvider; +import org.apache.pulsar.client.api.KeyStoreParams; import org.apache.pulsar.client.impl.PulsarServiceNameResolver; import org.apache.pulsar.client.impl.conf.ClientConfigurationData; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.BoundRequestBuilder; import org.asynchttpclient.DefaultAsyncHttpClient; @@ -60,6 +63,7 @@ import org.asynchttpclient.Request; import org.asynchttpclient.Response; import org.asynchttpclient.channel.DefaultKeepAliveStrategy; +import org.asynchttpclient.netty.ssl.JsseSslEngineFactory; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientRequest; import org.glassfish.jersey.client.ClientResponse; @@ -109,24 +113,41 @@ public boolean keepAlive(Request ahcRequest, HttpRequest request, HttpResponse r if (conf != null && StringUtils.isNotBlank(conf.getServiceUrl())) { serviceNameResolver.updateServiceUrl(conf.getServiceUrl()); if (conf.getServiceUrl().startsWith("https://")) { - - SslContext sslCtx = null; - // Set client key and certificate if available AuthenticationDataProvider authData = conf.getAuthentication().getAuthData(); - if (authData.hasDataForTls()) { - sslCtx = SecurityUtility.createNettySslContextForClient( + + if (conf.isUseKeyStoreTls()) { + KeyStoreParams params = authData.hasDataForTls() ? authData.getTlsKeyStoreParams() : null; + + final SSLContext sslCtx = KeyStoreSSLContext.createClientSslContext( + conf.getSslProvider(), + params != null ? params.getKeyStoreType() : null, + params != null ? params.getKeyStorePath() : null, + params != null ? params.getKeyStorePassword() : null, conf.isTlsAllowInsecureConnection() || !conf.isTlsHostnameVerificationEnable(), - conf.getTlsTrustCertsFilePath(), - authData.getTlsCertificates(), - authData.getTlsPrivateKey()); + conf.getTlsTrustStoreType(), + conf.getTlsTrustStorePath(), + conf.getTlsTrustStorePassword(), + conf.getTlsCiphers(), + conf.getTlsProtocols()); + + JsseSslEngineFactory sslEngineFactory = new JsseSslEngineFactory(sslCtx); + confBuilder.setSslEngineFactory(sslEngineFactory); } else { - sslCtx = SecurityUtility.createNettySslContextForClient( - conf.isTlsAllowInsecureConnection() || !conf.isTlsHostnameVerificationEnable(), - conf.getTlsTrustCertsFilePath()); + SslContext sslCtx = null; + if (authData.hasDataForTls()) { + sslCtx = SecurityUtility.createNettySslContextForClient( + conf.isTlsAllowInsecureConnection() || !conf.isTlsHostnameVerificationEnable(), + conf.getTlsTrustCertsFilePath(), + authData.getTlsCertificates(), + authData.getTlsPrivateKey()); + } else { + sslCtx = SecurityUtility.createNettySslContextForClient( + conf.isTlsAllowInsecureConnection() || !conf.isTlsHostnameVerificationEnable(), + conf.getTlsTrustCertsFilePath()); + } + confBuilder.setSslContext(sslCtx); } - - confBuilder.setSslContext(sslCtx); } } httpClient = new DefaultAsyncHttpClient(confBuilder.build()); diff --git a/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/AuthenticationDataProvider.java b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/AuthenticationDataProvider.java index 122dd5b25bba4..77eafe54dfa17 100644 --- a/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/AuthenticationDataProvider.java +++ b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/AuthenticationDataProvider.java @@ -63,6 +63,15 @@ default PrivateKey getTlsPrivateKey() { return null; } + /** + * Used for TLS authentication with keystore type. + * + * @return a KeyStoreParams for the client certificate chain, or null if the data are not available + */ + default KeyStoreParams getTlsKeyStoreParams() { + return null; + } + /* * HTTP */ diff --git a/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/ClientBuilder.java b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/ClientBuilder.java index addedaa6c20c0..e84f8ba5091b3 100644 --- a/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/ClientBuilder.java +++ b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/ClientBuilder.java @@ -20,6 +20,7 @@ import java.time.Clock; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.pulsar.client.api.PulsarClientException.UnsupportedAuthenticationException; @@ -289,6 +290,69 @@ ClientBuilder authentication(String authPluginClassName, Map aut */ ClientBuilder enableTlsHostnameVerification(boolean enableTlsHostnameVerification); + /** + * If Tls is enabled, whether use KeyStore type as tls configuration parameter. + * False means use default pem type configuration. + * + * @param useKeyStoreTls + * @return the client builder instance + */ + ClientBuilder useKeyStoreTls(boolean useKeyStoreTls); + + /** + * The name of the security provider used for SSL connections. + * Default value is the default security provider of the JVM. + * + * @param sslProvider + * @return the client builder instance + */ + ClientBuilder sslProvider(String sslProvider); + + /** + * The file format of the trust store file. + * + * @param tlsTrustStoreType + * @return the client builder instance + */ + ClientBuilder tlsTrustStoreType(String tlsTrustStoreType); + + /** + * The location of the trust store file. + * + * @param tlsTrustStorePath + * @return the client builder instance + */ + ClientBuilder tlsTrustStorePath(String tlsTrustStorePath); + + /** + * The store password for the key store file. + * + * @param tlsTrustStorePassword + * @return the client builder instance + */ + ClientBuilder tlsTrustStorePassword(String tlsTrustStorePassword); + + /** + * A list of cipher suites. + * This is a named combination of authentication, encryption, MAC and key exchange algorithm + * used to negotiate the security settings for a network connection using TLS or SSL network protocol. + * By default all the available cipher suites are supported. + * + * @param tlsCiphers + * @return the client builder instance + */ + ClientBuilder tlsCiphers(Set tlsCiphers); + + /** + * The SSL protocol used to generate the SSLContext. + * Default setting is TLS, which is fine for most cases. + * Allowed values in recent JVMs are TLS, TLSv1.1 and TLSv1.2. SSL, SSLv2. + * + * @param tlsProtocols + * @return the client builder instance + */ + ClientBuilder tlsProtocols(Set tlsProtocols); + /** * Set the interval between each stat info (default: 60 seconds) Stats will be activated with positive * statsInterval It should be set to at least 1 second. diff --git a/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/KeyStoreParams.java b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/KeyStoreParams.java new file mode 100644 index 0000000000000..5759801acfebf --- /dev/null +++ b/pulsar-client-api/src/main/java/org/apache/pulsar/client/api/KeyStoreParams.java @@ -0,0 +1,35 @@ +/** + * 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. + */ +package org.apache.pulsar.client.api; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +/** + * KeyStore parameters used for tls authentication. + */ +@Data +@Builder +@AllArgsConstructor +public class KeyStoreParams{ + private String keyStoreType; + private String keyStorePath; + private String keyStorePassword; +} diff --git a/pulsar-client-auth-keystoretls/pom.xml b/pulsar-client-auth-keystoretls/pom.xml new file mode 100644 index 0000000000000..fb6f010fd3eb3 --- /dev/null +++ b/pulsar-client-auth-keystoretls/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + + org.apache.pulsar + pulsar + 2.6.0-SNAPSHOT + .. + + + pulsar-client-auth-keystoretls + jar + TLS authentication plugin with keystore type for java client + + + + + ${project.groupId} + pulsar-client-original + ${project.parent.version} + true + + + + com.google.guava + guava + + + + org.apache.commons + commons-lang3 + + + + org.projectlombok + lombok + + + + javax.ws.rs + javax.ws.rs-api + + + + org.apache.pulsar + testmocks + ${project.version} + test + + + + ${project.groupId} + pulsar-broker + ${project.version} + test-jar + test + + + + ${project.groupId} + pulsar-broker + ${project.version} + test + + + + ${project.groupId} + pulsar-proxy + ${project.version} + test-jar + test + + + + ${project.groupId} + pulsar-proxy + ${project.version} + test + + + + diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java new file mode 100644 index 0000000000000..fc59dfd7873eb --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java @@ -0,0 +1,45 @@ +/** + * 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. + */ +package org.apache.pulsar.client.impl.auth; + +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.client.api.AuthenticationDataProvider; +import org.apache.pulsar.client.api.KeyStoreParams; + +@Slf4j +public class AuthenticationDataKeyStoreTls implements AuthenticationDataProvider { + private final KeyStoreParams keyStoreParams; + + public AuthenticationDataKeyStoreTls(KeyStoreParams keyStoreParams) throws Exception { + this.keyStoreParams = keyStoreParams; + } + + /* + * TLS + */ + @Override + public boolean hasDataForTls() { + return true; + } + + @Override + public KeyStoreParams getTlsKeyStoreParams() { + return this.keyStoreParams; + } +} diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java new file mode 100644 index 0000000000000..93514fa0ae810 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java @@ -0,0 +1,136 @@ +/** + * 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. + */ +package org.apache.pulsar.client.impl.auth; + +import com.google.common.base.Joiner; +import com.google.common.base.Strings; +import java.io.IOException; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.client.api.Authentication; +import org.apache.pulsar.client.api.AuthenticationDataProvider; +import org.apache.pulsar.client.api.EncodedAuthenticationParameterSupport; +import org.apache.pulsar.client.api.KeyStoreParams; +import org.apache.pulsar.client.api.PulsarClientException; +import org.apache.pulsar.client.impl.AuthenticationUtil; + +/** + * This plugin requires these parameters: keyStoreType, keyStorePath, and keyStorePassword. + * This parameter will construct a AuthenticationDataProvider + */ +@Slf4j +public class AuthenticationKeyStoreTls implements Authentication, EncodedAuthenticationParameterSupport { + private static final long serialVersionUID = 1L; + + private final static String AUTH_NAME = "tls"; + + // parameter name + public final static String KEYSTORE_TYPE = "keyStoreType"; + public final static String KEYSTORE_PATH= "keyStorePath"; + public final static String KEYSTORE_PW = "keyStorePassword"; + private final static String DEFAULT_KEYSTORE_TYPE = "JKS"; + + private KeyStoreParams keyStoreParams; + + public AuthenticationKeyStoreTls() { + } + + public AuthenticationKeyStoreTls(String keyStoreType, String keyStorePath, String keyStorePassword) { + this.keyStoreParams = KeyStoreParams.builder() + .keyStoreType(keyStoreType) + .keyStorePath(keyStorePath) + .keyStorePassword(keyStorePassword) + .build(); + } + + @Override + public void close() throws IOException { + // noop + } + + @Override + public String getAuthMethodName() { + return AUTH_NAME; + } + + @Override + public AuthenticationDataProvider getAuthData() throws PulsarClientException { + try { + return new AuthenticationDataKeyStoreTls(this.keyStoreParams); + } catch (Exception e) { + throw new PulsarClientException(e); + } + } + + // passed in KEYSTORE_TYPE/KEYSTORE_PATH/KEYSTORE_PW to construct parameters. + // e.g. {"keyStoreType":"JKS","keyStorePath":"/path/to/keystorefile","keyStorePassword":"keystorepw"} + // or: "keyStoreType":"JKS","keyStorePath":"/path/to/keystorefile","keyStorePassword":"keystorepw" + @Override + public void configure(String paramsString) { + Map params = null; + try { + params = AuthenticationUtil.configureFromJsonString(paramsString); + } catch (Exception e) { + // auth-param is not in json format + log.info("parameter not in Json format: ", paramsString); + } + + // in ":" "," format. + params = (params == null || params.isEmpty()) + ? AuthenticationUtil.configureFromPulsar1AuthParamString(paramsString) + : params; + + configure(params); + } + + @Override + public void configure(Map params) { + String keyStoreType = params.get(KEYSTORE_TYPE); + String keyStorePath = params.get(KEYSTORE_PATH); + String keyStorePassword = params.get(KEYSTORE_PW); + + if (Strings.isNullOrEmpty(keyStorePath) + || Strings.isNullOrEmpty(keyStorePassword)) { + throw new IllegalArgumentException("Passed in parameter empty. " + + KEYSTORE_PATH + ": " + keyStorePath + + " " + KEYSTORE_PW + ": " + keyStorePassword); + } + + if (Strings.isNullOrEmpty(keyStoreType)) { + keyStoreType = DEFAULT_KEYSTORE_TYPE; + } + + this.keyStoreParams = KeyStoreParams.builder() + .keyStoreType(keyStoreType) + .keyStorePath(keyStorePath) + .keyStorePassword(keyStorePassword) + .build(); + } + + @Override + public void start() throws PulsarClientException { + // noop + } + + // return strings like : "key1":"value1", "key2":"value2", ... + public static String mapToString(Map map) { + return Joiner.on(',').withKeyValueSeparator(':').join(map); + } +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java new file mode 100644 index 0000000000000..9535aca0890d0 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java @@ -0,0 +1,150 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static org.mockito.Mockito.spy; + +import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; +import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.api.ClientBuilder; +import org.apache.pulsar.client.api.ProducerConsumerBase; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; +import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; + +// Base class for TLS authentication and authorization based on KeyStore type config. +public class TlsProducerConsumerBase extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_CN = "clientuser"; + protected final String KEYSTORE_TYPE = "JKS"; + + private final String clusterName = "use"; + Set tlsProtocols = Sets.newConcurrentHashSet(); + + @BeforeMethod + @Override + protected void setup() throws Exception { + // TLS configuration for Broker + internalSetUpForBroker(); + + // Start Broker + super.init(); + } + + @AfterMethod + @Override + protected void cleanup() throws Exception { + super.internalCleanup(); + } + + protected void internalSetUpForBroker() throws Exception { + conf.setBrokerServicePortTls(Optional.of(0)); + conf.setWebServicePortTls(Optional.of(0)); + conf.setTlsEnabledWithKeyStore(true); + + conf.setTlsKeyStoreType(KEYSTORE_TYPE); + conf.setTlsKeyStore(BROKER_KEYSTORE_FILE_PATH); + conf.setTlsKeyStorePassword(BROKER_KEYSTORE_PW); + + conf.setTlsTrustStoreType(KEYSTORE_TYPE); + conf.setTlsTrustStore(CLIENT_TRUSTSTORE_FILE_PATH); + conf.setTlsTrustStorePassword(CLIENT_TRUSTSTORE_PW); + + conf.setClusterName(clusterName); + conf.setTlsRequireTrustedClientCertOnConnect(true); + tlsProtocols.add("TLSv1.2"); + conf.setTlsProtocols(tlsProtocols); + + // config for authentication and authorization. + conf.setSuperUserRoles(Sets.newHashSet(CLIENT_KEYSTORE_CN)); + conf.setAuthenticationEnabled(true); + conf.setAuthorizationEnabled(true); + Set providers = new HashSet<>(); + providers.add(AuthenticationProviderTls.class.getName()); + conf.setAuthenticationProviders(providers); + } + + protected void internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { + if (pulsarClient != null) { + pulsarClient.close(); + } + + Set tlsProtocols = Sets.newConcurrentHashSet(); + tlsProtocols.add("TLSv1.2"); + + ClientBuilder clientBuilder = PulsarClient.builder().serviceUrl(lookupUrl) + .enableTls(true) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(false) + .tlsProtocols(tlsProtocols) + .operationTimeout(1000, TimeUnit.MILLISECONDS); + if (addCertificates) { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_TYPE, KEYSTORE_TYPE); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + clientBuilder.authentication(AuthenticationKeyStoreTls.class.getName(), authParams); + } + pulsarClient = clientBuilder.build(); + } + + protected void internalSetUpForNamespace() throws Exception { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + + if (admin != null) { + admin.close(); + } + + admin = spy(PulsarAdmin.builder().serviceHttpUrl(brokerUrlTls.toString()) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(true) + .authentication(AuthenticationKeyStoreTls.class.getName(), authParams).build()); + admin.clusters().createCluster(clusterName, new ClusterData(brokerUrl.toString(), brokerUrlTls.toString(), + pulsar.getBrokerServiceUrl(), pulsar.getBrokerServiceUrlTls())); + admin.tenants().createTenant("my-property", + new TenantInfo(Sets.newHashSet("appid1", "appid2"), Sets.newHashSet("use"))); + admin.namespaces().createNamespace("my-property/my-ns"); + } + +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java new file mode 100644 index 0000000000000..d61ef64d0e17c --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java @@ -0,0 +1,135 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.api.SubscriptionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +// TLS authentication and authorization based on KeyStore type config. +public class TlsProducerConsumerTest extends TlsProducerConsumerBase { + private static final Logger log = LoggerFactory.getLogger(TlsProducerConsumerTest.class); + + /** + * verifies that messages whose size is larger than 2^14 bytes (max size of single TLS chunk) can be + * produced/consumed + * + * @throws Exception + */ + @Test(timeOut = 30000) + public void testTlsLargeSizeMessage() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + internalSetUpForNamespace(); + + Consumer consumer = pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") + .subscriptionName("my-subscriber-name").subscribe(); + + Producer producer = pulsarClient.newProducer().topic("persistent://my-property/use/my-ns/my-topic1") + .create(); + for (int i = 0; i < 10; i++) { + byte[] message = new byte[MESSAGE_SIZE]; + Arrays.fill(message, (byte) i); + producer.send(message); + } + + Message msg = null; + for (int i = 0; i < 10; i++) { + msg = consumer.receive(5, TimeUnit.SECONDS); + byte[] expected = new byte[MESSAGE_SIZE]; + Arrays.fill(expected, (byte) i); + Assert.assertEquals(expected, msg.getData()); + } + // Acknowledge the consumption of all messages at once + consumer.acknowledgeCumulative(msg); + consumer.close(); + log.info("-- Exiting {} test --", methodName); + } + + @Test(timeOut = 300000) + public void testTlsClientAuthOverBinaryProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + + internalSetUpForNamespace(); + + // Test 1 - Using TLS on binary protocol without sending certs - expect failure + internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); + try { + pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Test 2 - Using TLS on binary protocol - sending certs + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + + try { + pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + + @Test(timeOut = 30000) + public void testTlsClientAuthOverHTTPProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + internalSetUpForNamespace(); + + // Test 1 - Using TLS on https without sending certs - expect failure + internalSetUpForClient(false, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Test 2 - Using TLS on https - sending certs + internalSetUpForClient(true, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + + +} diff --git a/pulsar-client-auth-keystoretls/src/test/resources/broker.keystore.jks b/pulsar-client-auth-keystoretls/src/test/resources/broker.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..b4fec69ac2361e28da306de053e544c900fb18dc GIT binary patch literal 2767 zcmeH|c{J2}AIE>c!7wHdVo*sLaxHOw#-8!Sl_I(fVz{`JU72Ld5;G%_eZ2_TT}zhC zxUyF$V;M|#w@{YJk|L6=J39Bdx2Jo~^Z#?s{p0(`_jSJ8`F!8!_1RnATLu6C_UC|K zcDPO;_y7Rt^=KV24{+cWR3Hu3iY$l>ONYoH5rzQL9epD>IDXECc;E=x+xoXZ@_-NgxOZwkX@k!o~uhv;0e3e@a-s8fiem0YBQn1%jZU6jJ6}{W%^y|>z zn%{=hIt^zo_m=Kekz>Sw%R}K|A7oWl<3#>D{SyX=11fE&qruV!W#g<)7J7 zC>rc~*78`N8NXQ7;-Csiv@!KfpUi~g@bo*$ApO>IuJ+2vurp2@n8@#4eQ3tPUg8Zh z^lN%cE;JY$2!LN#h*fk}g+L&gG8_~CMkCSNBS^kODOg+2Coe-wG<>=9m(}>J9ir1} z_s|_h$LXRzUQINGbb0MD=( zYLpIqvPLK#)p2REu8zNOKxdHAnRjcfIglHs!wAIBsf0ZAf-sWeR!k4=E>9CKp(rsb)uRi?_2;mSzg+=hH5G{@_4}wHN4HVX68BB z_`l?I%Ga@^%&Rb%@t;}o1p&kr!exgmt_1Ht((7l(q6w_<#6bUobIZX&c2E|^53*9l zP6lsy#v4odQp_dO9-pf{!P$#>QEig>l)W_M%CTZ(C64!xIZTl_NSF29m z3T}3@(ObMmQHXl!oTGI9tz?B_4?UaDPFHDNbIi<)Q)(aVrEb{nT5u(V_idRcEq#=G z1%%+HhVJJ+&=5&cs93X#O~4%g%@j9tmS$ujH&Z+rYA@qZwkye(a-su)O52qu$q4+a z+4e#YSUHhl{lT(@i!SKv+$;3@-ksiYPxqyP>o4r{YF4KS;`m4UZr|NkPRi^te{{jJ~NkBuMDe?E**>*_+py>ScNSaBD2M_%;L|oN&(NpL_9-t>Fa=V{BNsZl+^#* zD*FKPRODm*_!kXvUi9?vB>4YHxCFi>oPQ(SeL)ioR;4>HsNV7MIM@b-iYLe>7`HQ!Z z@9?7Q`%n8gof|%!sr9nEsoX~Op=EtQN>$_5sRi;UOCzKfC)ovOWf|8nXk|R-#mPq^ zQVoQ>B$!6hLIQPt2uw_8@ibN_FxosgR(h(SQcR9g?3?IW5Q^LqSGn|l`%)k%so7Kb zzNG}0aHk|}a@u+}2c8t|Y)1^bc)}4h(%kDakYyu<1N-k7I8uT#WQ45gYq-~r`W9yS zFE4J}Xz6O`>4tj0&635Q%c<5jqN63onxQU%LAbYbkvxat^s+S)p#}{%ytw|r(1a2o z0jn(1YVsOd8R#$5#doBe8$Q1hN`1aqKVI5U#}IayXy@m~_@OJ6ga(axxmO|f4-Zp$1%&aqeAP5aULhB2WVeN|AL zFJ-)Tf@`5M@|wt@n)2KqHR$Vo?rV$L9e$gJN-k#}Bp&3Dd^OiGvFXwZN6~fFyV~ip z;#rsy&+IFw<6vD-OzW1UUi)51e%r|7sqB>olLVTiF7CiNLMbH{>#933P*CxJy6Bp@ z?V3tC5+VFzMKvY>X@DHN(`X+%l()B0i1)lJ_(Q-7&kg)}-O4F$u)JCa9(N|zLN3;J r`3UWJKf1f;+CM%2pPv7J(DPsCSENGW!FJm!fAA-4f18*?ZNn=n&yo`HfmuaSX)iIIh&v5}#H zL6ii)ks(OHzyvCQuAzxh3E4`f0myAd&vDNhf4^vvg_Pxt+NZAr-l+`dtw?`)`zqlh-ZS$1w6&GG^iI`M;Fn4thHd_Rgw(x$*s@}ghG59Limdcdw)cHT(8I{X8U%z0p zS97na_{2nq_K)k9F6Er`!Ro6Nlefr;L)5{xyY)yEM-gxV}k0$;8aaz=#|| zz(5Cv5F^9n&DVPRUs_Hq&aT!yYFy*sTC#AatxW6N6U#sK&-v4k`swu5PQ#RkH|I)l z$w^HK*i@VJV}66hVnI!V%Zp&|eY18p97oRvZ-mqDq^R4FT;Vqj# zuo}xf&)Q>nDX_YE-c*URV&_EN=1gsz8gRY7&4^w3iob3~)+!IF?!wcHBQAwq-K|k| z=yU$E8;~e@`979R+uTO!vHAEwS`}k8QQGQYiopRWjuO literal 0 HcmV?d00001 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt b/pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt b/pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/client.keystore.jks b/pulsar-client-auth-keystoretls/src/test/resources/client.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..499c8bec41b32febb3846428d27294e2b77ef487 GIT binary patch literal 2767 zcmeH|c{J4PAIHDn!Avu*EE8HRC1h`A3|YnvmFOnMHn=6*7)zOq8I#7nvseq)mPod- zMGGz?vbAVL2t$@g(Jewi4_9U%&1-zyI$!_m9tc&htLc^Ld{0`Ml5Tv-W!J zH2?sR{{+;>i$W$-0RRlS{Q>OP3Gy6S=EQ`*GhWm*zMA z=Ds{UCXkh_o1o)P4vjmeUVT2kwC5GiH8u3k$yygU4clxpkB@wI!BMMG&(qBC-um&1 zhaJ?t&zTWbqXRTs!c_63d-En0^QqgF?Nava*f-dNYtn2okWcY4kDV_sE{3PhnJj6rm zKd6wUUurH=i14=}D0e$bD^<+&8?Y4`(qDCJj`$9Yx3Z(ZGRuRJbtKXDloXX#IE zw~GkbQLp*%)PY%2J46Hoz^^MLEV{5lAdpr)kdXAsI3+ZQq085gFh4-$UNlpPe%*F? zAu0PKi@4D7=q%cId^l$_LTT%H`>LP3-{rB43N38pDeXg|a`U(==kT$$W|DgZRreQr ze=}zf;qrBsBVM%ds^`S?l_mol(E>apGBQ@Yc*S~a zd`L&3tH9+^gT|S=Z^>$T75^^|0F9k{FvC zc+*T{UN{VU%l5Q z=W!iAIQXK**oLGQF1oCpZ7IziC-Ijk=AyU}nV%QEd>9uR6hkU9QpIP9(U>03tR)qL z`W~l*JNczBhPbpV_QCyJ<)^J$-1T;Am`XHo+3<~gl*h=y%hTzj0r=^_hR2o_*MMd!{1m?)pjuYq<-Fa!v5qGZ8Wk*(`b5c%()*5-_^-}+#MQ{SL@)3_hW`) zCrI^Z%XU`+bng*5ChL3bi_*d)Yafr*P*0yGIX`(hjC(Ftud~%-uKU!l2VY&|d0TDC zj5&-uU0k=?*h3&QNwJ@zR5KX7_~+^ZT2P!044vZe2H7y*RzyH_8w;8~DU@WGm8SG##EE%GR6xZE{(<)&ckXexOBZFY5+gJ>bAG za~1rmn^~gkhgj*c)&sQ{jFq!7OCma?xgKPLGI%*XGA zcEV8SSZLTg95ud#$U{Wz#s};EGxPtM`G13%ZvuCi#Yc1=;KKCxfr5kC$Mb@Z{{c;5 BppgIo literal 0 HcmV?d00001 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/client.truststore.jks b/pulsar-client-auth-keystoretls/src/test/resources/client.truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..8eaa06ba5812f2440651d87080cdbec252b596e7 GIT binary patch literal 731 zcmezO_TO6u1_mY|W(3o0$%#ez`6WPZ;epRJKN(mf^h^ybfhy)0G%?LEXku(&;$)bS zQrgbSI&H22FB_*;n@8JsUPeZ4Rt5uJLv903Hs(+kHesgZJOc%BULyko6C(>lVesM>v+U6gVk4!hI-SJi=<;YZ=G5%T0VQ<5}BF|w{ZFM!Gdz@Utck8 zl-WJ?<$2B9Dc2r;P^tFVznbI7x{hX-)Ne)|P8lU9{c8#pcWIt0aD9`0l8Kp-fe|@` zfPoGSAx4JDo3HiszqFiKoL#MZ)VRjMwPfK;Tbb6kCzgNepYx|7_0#FAorWn7Z_btA zl9QSeu&Flb$NUD1#g3lhMJI2th<&tK)fL#j-Im|{(x&5IEBPd8n|rIcm>e&)I&y~3H4AtX=O%Huw|!pLP7j?f(a+g3 zXDgSA+f41)hmI|%Dv5jjwZUA4|5;_zWL=HYOMb1 rQ)9~Sv8OI_4B!0R^wNv|{|gnRGCu5TJ*a(QhWm!M^WR@jJN^~`qDeO@ literal 0 HcmV?d00001 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt b/pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt b/pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientBuilderImpl.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientBuilderImpl.java index 16ea6889a9b04..3283166e50a65 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientBuilderImpl.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientBuilderImpl.java @@ -20,6 +20,7 @@ import java.time.Clock; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; @@ -173,6 +174,48 @@ public ClientBuilder allowTlsInsecureConnection(boolean tlsAllowInsecureConnecti return this; } + @Override + public ClientBuilder useKeyStoreTls(boolean useKeyStoreTls) { + conf.setUseKeyStoreTls(useKeyStoreTls); + return this; + } + + @Override + public ClientBuilder sslProvider(String sslProvider) { + conf.setSslProvider(sslProvider); + return this; + } + + @Override + public ClientBuilder tlsTrustStoreType(String tlsTrustStoreType) { + conf.setTlsTrustStoreType(tlsTrustStoreType); + return this; + } + + @Override + public ClientBuilder tlsTrustStorePath(String tlsTrustStorePath) { + conf.setTlsTrustStorePath(tlsTrustStorePath); + return this; + } + + @Override + public ClientBuilder tlsTrustStorePassword(String tlsTrustStorePassword) { + conf.setTlsTrustStorePassword(tlsTrustStorePassword); + return this; + } + + @Override + public ClientBuilder tlsCiphers(Set tlsCiphers) { + conf.setTlsCiphers(tlsCiphers); + return this; + } + + @Override + public ClientBuilder tlsProtocols(Set tlsProtocols) { + conf.setTlsProtocols(tlsProtocols); + return this; + } + @Override public ClientBuilder statsInterval(long statsInterval, TimeUnit unit) { conf.setStatsIntervalSeconds(unit.toSeconds(statsInterval)); @@ -214,13 +257,13 @@ public ClientBuilder startingBackoffInterval(long duration, TimeUnit unit) { conf.setInitialBackoffIntervalNanos(unit.toNanos(duration)); return this; } - + @Override public ClientBuilder maxBackoffInterval(long duration, TimeUnit unit) { conf.setMaxBackoffIntervalNanos(unit.toNanos(duration)); return this; } - + public ClientConfigurationData getClientConfigurationData() { return conf; } diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpClient.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpClient.java index 845c741eb43c4..3e693ad11bf8c 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpClient.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpClient.java @@ -32,14 +32,18 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.ssl.SslContext; +import javax.net.ssl.SSLContext; import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.PulsarVersion; import org.apache.pulsar.client.api.Authentication; import org.apache.pulsar.client.api.AuthenticationDataProvider; +import org.apache.pulsar.client.api.KeyStoreParams; import org.apache.pulsar.client.api.PulsarClientException; import org.apache.pulsar.client.api.PulsarClientException.NotFoundException; +import org.apache.pulsar.client.impl.conf.ClientConfigurationData; import org.apache.pulsar.common.util.ObjectMapperFactory; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.BoundRequestBuilder; @@ -47,6 +51,7 @@ import org.asynchttpclient.DefaultAsyncHttpClientConfig; import org.asynchttpclient.Request; import org.asynchttpclient.channel.DefaultKeepAliveStrategy; +import org.asynchttpclient.netty.ssl.JsseSslEngineFactory; @Slf4j @@ -59,24 +64,15 @@ public class HttpClient implements Closeable { protected final ServiceNameResolver serviceNameResolver; protected final Authentication authentication; - protected HttpClient(String serviceUrl, Authentication authentication, - EventLoopGroup eventLoopGroup, boolean tlsAllowInsecureConnection, String tlsTrustCertsFilePath) - throws PulsarClientException { - this(serviceUrl, authentication, eventLoopGroup, tlsAllowInsecureConnection, - tlsTrustCertsFilePath, DEFAULT_CONNECT_TIMEOUT_IN_SECONDS, DEFAULT_READ_TIMEOUT_IN_SECONDS); - } - - protected HttpClient(String serviceUrl, Authentication authentication, - EventLoopGroup eventLoopGroup, boolean tlsAllowInsecureConnection, String tlsTrustCertsFilePath, - int connectTimeoutInSeconds, int readTimeoutInSeconds) throws PulsarClientException { - this.authentication = authentication; + protected HttpClient(ClientConfigurationData conf, EventLoopGroup eventLoopGroup) throws PulsarClientException { + this.authentication = conf.getAuthentication(); this.serviceNameResolver = new PulsarServiceNameResolver(); - this.serviceNameResolver.updateServiceUrl(serviceUrl); + this.serviceNameResolver.updateServiceUrl(conf.getServiceUrl()); DefaultAsyncHttpClientConfig.Builder confBuilder = new DefaultAsyncHttpClientConfig.Builder(); confBuilder.setFollowRedirect(true); - confBuilder.setConnectTimeout(connectTimeoutInSeconds * 1000); - confBuilder.setReadTimeout(readTimeoutInSeconds * 1000); + confBuilder.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_IN_SECONDS * 1000); + confBuilder.setReadTimeout(DEFAULT_READ_TIMEOUT_IN_SECONDS * 1000); confBuilder.setUserAgent(String.format("Pulsar-Java-v%s", PulsarVersion.getVersion())); confBuilder.setKeepAliveStrategy(new DefaultKeepAliveStrategy() { @Override @@ -88,19 +84,45 @@ public boolean keepAlive(Request ahcRequest, HttpRequest request, HttpResponse r if ("https".equals(serviceNameResolver.getServiceUri().getServiceName())) { try { - SslContext sslCtx = null; - // Set client key and certificate if available AuthenticationDataProvider authData = authentication.getAuthData(); - if (authData.hasDataForTls()) { - sslCtx = SecurityUtility.createNettySslContextForClient(tlsAllowInsecureConnection, tlsTrustCertsFilePath, - authData.getTlsCertificates(), authData.getTlsPrivateKey()); + + if (conf.isUseKeyStoreTls()) { + SSLContext sslCtx = null; + KeyStoreParams params = authData.hasDataForTls() ? authData.getTlsKeyStoreParams() : null; + + sslCtx = KeyStoreSSLContext.createClientSslContext( + conf.getSslProvider(), + params != null ? params.getKeyStoreType() : null, + params != null ? params.getKeyStorePath() : null, + params != null ? params.getKeyStorePassword() : null, + conf.isTlsAllowInsecureConnection(), + conf.getTlsTrustStoreType(), + conf.getTlsTrustStorePath(), + conf.getTlsTrustStorePassword(), + conf.getTlsCiphers(), + conf.getTlsProtocols()); + + JsseSslEngineFactory sslEngineFactory = new JsseSslEngineFactory(sslCtx); + confBuilder.setSslEngineFactory(sslEngineFactory); } else { - sslCtx = SecurityUtility.createNettySslContextForClient(tlsAllowInsecureConnection, tlsTrustCertsFilePath); + SslContext sslCtx = null; + if (authData.hasDataForTls()) { + sslCtx = SecurityUtility.createNettySslContextForClient( + conf.isTlsAllowInsecureConnection(), + conf.getTlsTrustCertsFilePath(), + authData.getTlsCertificates(), + authData.getTlsPrivateKey()); + } + else { + sslCtx = SecurityUtility.createNettySslContextForClient( + conf.isTlsAllowInsecureConnection(), + conf.getTlsTrustCertsFilePath()); + } + confBuilder.setSslContext(sslCtx); } - confBuilder.setSslContext(sslCtx); - confBuilder.setUseInsecureTrustManager(tlsAllowInsecureConnection); + confBuilder.setUseInsecureTrustManager(conf.isTlsAllowInsecureConnection()); } catch (Exception e) { throw new PulsarClientException.InvalidConfigurationException(e); } @@ -109,7 +131,7 @@ public boolean keepAlive(Request ahcRequest, HttpRequest request, HttpResponse r AsyncHttpClientConfig config = confBuilder.build(); httpClient = new DefaultAsyncHttpClient(config); - log.debug("Using HTTP url: {}", serviceUrl); + log.debug("Using HTTP url: {}", conf.getServiceUrl()); } String getServiceUrl() { diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpLookupService.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpLookupService.java index 602b7aab80587..da7f148eac300 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpLookupService.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/HttpLookupService.java @@ -57,8 +57,7 @@ public class HttpLookupService implements LookupService { public HttpLookupService(ClientConfigurationData conf, EventLoopGroup eventLoopGroup) throws PulsarClientException { - this.httpClient = new HttpClient(conf.getServiceUrl(), conf.getAuthentication(), - eventLoopGroup, conf.isTlsAllowInsecureConnection(), conf.getTlsTrustCertsFilePath()); + this.httpClient = new HttpClient(conf, eventLoopGroup); this.useTls = conf.isUseTls(); } diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java index a9322530637a8..2145dd403c571 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java @@ -18,30 +18,34 @@ */ package org.apache.pulsar.client.impl; -import java.security.cert.X509Certificate; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.ssl.SslContext; - +import io.netty.handler.ssl.SslHandler; +import java.security.cert.X509Certificate; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.client.api.AuthenticationDataProvider; import org.apache.pulsar.client.impl.conf.ClientConfigurationData; import org.apache.pulsar.client.util.ObjectCache; import org.apache.pulsar.common.protocol.ByteBufPair; import org.apache.pulsar.common.protocol.Commands; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; +@Slf4j public class PulsarChannelInitializer extends ChannelInitializer { public static final String TLS_HANDLER = "tls"; private final Supplier clientCnxSupplier; private final boolean tlsEnabled; + private final boolean tlsEnabledWithKeyStore; private final Supplier sslContextSupplier; + private NettySSLEngineAutoRefreshBuilder nettySSLEngineAutoRefreshBuilder; private static final long TLS_CERTIFICATE_CACHE_MILLIS = TimeUnit.MINUTES.toMillis(1); @@ -50,8 +54,24 @@ public PulsarChannelInitializer(ClientConfigurationData conf, Supplier(() -> { try { // Set client certificate if available @@ -76,7 +96,11 @@ public PulsarChannelInitializer(ClientConfigurationData conf, Supplier tlsCiphers = Sets.newTreeSet(); + private Set tlsProtocols = Sets.newTreeSet(); + @JsonIgnore private Clock clock = Clock.systemDefaultZone(); diff --git a/pulsar-common/pom.xml b/pulsar-common/pom.xml index 9208cfe4097b2..3bc1500125c1d 100644 --- a/pulsar-common/pom.xml +++ b/pulsar-common/pom.xml @@ -151,6 +151,10 @@ javax.ws.rs-api + + commons-io + commons-io + diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/ClientSslContextRefresher.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/ClientSslContextRefresher.java deleted file mode 100644 index 48ac937047cba..0000000000000 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/ClientSslContextRefresher.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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. - */ -package org.apache.pulsar.common.util; - -import io.netty.handler.ssl.SslContext; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.cert.X509Certificate; -import org.apache.pulsar.client.api.AuthenticationDataProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@SuppressWarnings("checkstyle:JavadocType") -public class ClientSslContextRefresher { - private volatile SslContext sslContext; - private boolean tlsAllowInsecureConnection; - private String tlsTrustCertsFilePath; - private AuthenticationDataProvider authData; - - public ClientSslContextRefresher(boolean allowInsecure, String trustCertsFilePath, - AuthenticationDataProvider authData) throws IOException, GeneralSecurityException { - this.tlsAllowInsecureConnection = allowInsecure; - this.tlsTrustCertsFilePath = trustCertsFilePath; - this.authData = authData; - - if (authData != null && authData.hasDataForTls()) { - this.sslContext = SecurityUtility.createNettySslContextForClient(this.tlsAllowInsecureConnection, - this.tlsTrustCertsFilePath, (X509Certificate[]) authData.getTlsCertificates(), - authData.getTlsPrivateKey()); - } else { - this.sslContext = SecurityUtility.createNettySslContextForClient(this.tlsAllowInsecureConnection, - this.tlsTrustCertsFilePath); - } - } - - public SslContext get() { - if (authData != null && authData.hasDataForTls()) { - try { - this.sslContext = SecurityUtility.createNettySslContextForClient(this.tlsAllowInsecureConnection, - this.tlsTrustCertsFilePath, (X509Certificate[]) authData.getTlsCertificates(), - authData.getTlsPrivateKey()); - } catch (GeneralSecurityException | IOException e) { - LOG.error("Exception occured while trying to refresh sslContext: ", e); - } - - } - return sslContext; - } - - private static final Logger LOG = LoggerFactory.getLogger(ClientSslContextRefresher.class); -} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/DefaultSslContextBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/DefaultSslContextBuilder.java index 3e888f4636c65..c49bdd69da749 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/DefaultSslContextBuilder.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/DefaultSslContextBuilder.java @@ -29,11 +29,19 @@ public class DefaultSslContextBuilder extends SslContextAutoRefreshBuilder { private volatile SSLContext sslContext; + protected final boolean tlsAllowInsecureConnection; + protected final FileModifiedTimeUpdater tlsTrustCertsFilePath, tlsCertificateFilePath, tlsKeyFilePath; + protected final boolean tlsRequireTrustedClientCertOnConnect; + public DefaultSslContextBuilder(boolean allowInsecure, String trustCertsFilePath, String certificateFilePath, String keyFilePath, boolean requireTrustedClientCertOnConnect, long certRefreshInSec) throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { - super(allowInsecure, trustCertsFilePath, certificateFilePath, keyFilePath, null, null, - requireTrustedClientCertOnConnect, certRefreshInSec); + super(certRefreshInSec); + this.tlsAllowInsecureConnection = allowInsecure; + this.tlsTrustCertsFilePath = new FileModifiedTimeUpdater(trustCertsFilePath); + this.tlsCertificateFilePath = new FileModifiedTimeUpdater(certificateFilePath); + this.tlsKeyFilePath = new FileModifiedTimeUpdater(keyFilePath); + this.tlsRequireTrustedClientCertOnConnect = requireTrustedClientCertOnConnect; } @Override @@ -49,4 +57,10 @@ public SSLContext getSslContext() { return this.sslContext; } + @Override + public boolean needUpdate() { + return tlsTrustCertsFilePath.checkAndRefresh() + || tlsCertificateFilePath.checkAndRefresh() + || tlsKeyFilePath.checkAndRefresh(); + } } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyClientSslContextRefresher.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyClientSslContextRefresher.java new file mode 100644 index 0000000000000..48cf992689d99 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyClientSslContextRefresher.java @@ -0,0 +1,74 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util; + +import io.netty.handler.ssl.SslContext; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLException; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.client.api.AuthenticationDataProvider; + +/** + * SSL context builder for Netty Client side. + */ +@Slf4j +public class NettyClientSslContextRefresher extends SslContextAutoRefreshBuilder { + private volatile SslContext sslNettyContext; + private boolean tlsAllowInsecureConnection; + protected final FileModifiedTimeUpdater tlsTrustCertsFilePath; + private AuthenticationDataProvider authData; + + public NettyClientSslContextRefresher(boolean allowInsecure, + String trustCertsFilePath, + AuthenticationDataProvider authData, + long delayInSeconds) + throws IOException, GeneralSecurityException { + super(delayInSeconds); + this.tlsAllowInsecureConnection = allowInsecure; + this.tlsTrustCertsFilePath = new FileModifiedTimeUpdater(trustCertsFilePath); + this.authData = authData; + } + + @Override + public synchronized SslContext update() + throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { + if (authData != null && authData.hasDataForTls()) { + this.sslNettyContext = SecurityUtility.createNettySslContextForClient(this.tlsAllowInsecureConnection, + this.tlsTrustCertsFilePath.getFileName(), (X509Certificate[]) authData.getTlsCertificates(), + authData.getTlsPrivateKey()); + } else { + this.sslNettyContext = SecurityUtility.createNettySslContextForClient(this.tlsAllowInsecureConnection, + this.tlsTrustCertsFilePath.getFileName()); + } + return this.sslNettyContext; + } + + @Override + public SslContext getSslContext() { + return this.sslNettyContext; + } + + @Override + public boolean needUpdate() { + return tlsTrustCertsFilePath.checkAndRefresh(); + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettySslContextBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyServerSslContextBuilder.java similarity index 52% rename from pulsar-common/src/main/java/org/apache/pulsar/common/util/NettySslContextBuilder.java rename to pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyServerSslContextBuilder.java index 713c52d41eb3c..250e628f0def7 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettySslContextBuilder.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/NettyServerSslContextBuilder.java @@ -26,16 +26,29 @@ import javax.net.ssl.SSLException; /** - * SSL context builder for Netty. + * SSL context builder for Netty Server side. */ -public class NettySslContextBuilder extends SslContextAutoRefreshBuilder { +public class NettyServerSslContextBuilder extends SslContextAutoRefreshBuilder { private volatile SslContext sslNettyContext; - public NettySslContextBuilder(boolean allowInsecure, String trustCertsFilePath, String certificateFilePath, - String keyFilePath, Set ciphers, Set protocols, boolean requireTrustedClientCertOnConnect, - long delayInSeconds) throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { - super(allowInsecure, trustCertsFilePath, certificateFilePath, keyFilePath, ciphers, protocols, - requireTrustedClientCertOnConnect, delayInSeconds); + protected final boolean tlsAllowInsecureConnection; + protected final FileModifiedTimeUpdater tlsTrustCertsFilePath, tlsCertificateFilePath, tlsKeyFilePath; + protected final Set tlsCiphers; + protected final Set tlsProtocols; + protected final boolean tlsRequireTrustedClientCertOnConnect; + + public NettyServerSslContextBuilder(boolean allowInsecure, String trustCertsFilePath, String certificateFilePath, + String keyFilePath, Set ciphers, Set protocols, + boolean requireTrustedClientCertOnConnect, + long delayInSeconds) { + super(delayInSeconds); + this.tlsAllowInsecureConnection = allowInsecure; + this.tlsTrustCertsFilePath = new FileModifiedTimeUpdater(trustCertsFilePath); + this.tlsCertificateFilePath = new FileModifiedTimeUpdater(certificateFilePath); + this.tlsKeyFilePath = new FileModifiedTimeUpdater(keyFilePath); + this.tlsCiphers = ciphers; + this.tlsProtocols = protocols; + this.tlsRequireTrustedClientCertOnConnect = requireTrustedClientCertOnConnect; } @Override @@ -52,4 +65,10 @@ public SslContext getSslContext() { return this.sslNettyContext; } + @Override + public boolean needUpdate() { + return tlsTrustCertsFilePath.checkAndRefresh() + || tlsCertificateFilePath.checkAndRefresh() + || tlsKeyFilePath.checkAndRefresh(); + } } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/SslContextAutoRefreshBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/SslContextAutoRefreshBuilder.java index 5fa9c1b4e1beb..a29f051e805ce 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/SslContextAutoRefreshBuilder.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/SslContextAutoRefreshBuilder.java @@ -18,16 +18,10 @@ */ package org.apache.pulsar.common.util; -import java.io.FileNotFoundException; import java.io.IOException; import java.security.GeneralSecurityException; -import java.util.Set; import java.util.concurrent.TimeUnit; - -import javax.net.ssl.SSLException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * Auto refresher and builder of SSLContext. @@ -35,30 +29,18 @@ * @param * type of SSLContext */ +@Slf4j public abstract class SslContextAutoRefreshBuilder { - protected final boolean tlsAllowInsecureConnection; - protected final FileModifiedTimeUpdater tlsTrustCertsFilePath, tlsCertificateFilePath, tlsKeyFilePath; - protected final Set tlsCiphers; - protected final Set tlsProtocols; - protected final boolean tlsRequireTrustedClientCertOnConnect; protected final long refreshTime; protected long lastRefreshTime; - public SslContextAutoRefreshBuilder(boolean allowInsecure, String trustCertsFilePath, String certificateFilePath, - String keyFilePath, Set ciphers, Set protocols, boolean requireTrustedClientCertOnConnect, - long certRefreshInSec) throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { - this.tlsAllowInsecureConnection = allowInsecure; - this.tlsTrustCertsFilePath = new FileModifiedTimeUpdater(trustCertsFilePath); - this.tlsCertificateFilePath = new FileModifiedTimeUpdater(certificateFilePath); - this.tlsKeyFilePath = new FileModifiedTimeUpdater(keyFilePath); - this.tlsCiphers = ciphers; - this.tlsProtocols = protocols; - this.tlsRequireTrustedClientCertOnConnect = requireTrustedClientCertOnConnect; + public SslContextAutoRefreshBuilder( + long certRefreshInSec) { this.refreshTime = TimeUnit.SECONDS.toMillis(certRefreshInSec); this.lastRefreshTime = -1; - if (LOG.isDebugEnabled()) { - LOG.debug("Certs will be refreshed every {} seconds", certRefreshInSec); + if (log.isDebugEnabled()) { + log.debug("Certs will be refreshed every {} seconds", certRefreshInSec); } } @@ -78,6 +60,13 @@ public SslContextAutoRefreshBuilder(boolean allowInsecure, String trustCertsFile */ protected abstract T getSslContext(); + /** + * Returns whether the key files modified after a refresh time, and context need update. + * + * @return true if files modified + */ + protected abstract boolean needUpdate(); + /** * It updates SSLContext at every configured refresh time and returns updated SSLContext. * @@ -91,24 +80,21 @@ public T get() { lastRefreshTime = System.currentTimeMillis(); return getSslContext(); } catch (GeneralSecurityException | IOException e) { - LOG.error("Execption while trying to refresh ssl Context {}", e.getMessage(), e); + log.error("Exception while trying to refresh ssl Context {}", e.getMessage(), e); } } else { long now = System.currentTimeMillis(); if (refreshTime <= 0 || now > (lastRefreshTime + refreshTime)) { - if (tlsTrustCertsFilePath.checkAndRefresh() || tlsCertificateFilePath.checkAndRefresh() - || tlsKeyFilePath.checkAndRefresh()) { + if (needUpdate()) { try { ctx = update(); lastRefreshTime = now; } catch (GeneralSecurityException | IOException e) { - LOG.error("Execption while trying to refresh ssl Context {} ", e.getMessage(), e); + log.error("Exception while trying to refresh ssl Context {} ", e.getMessage(), e); } } } } return ctx; } - - private static final Logger LOG = LoggerFactory.getLogger(SslContextAutoRefreshBuilder.class); } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java new file mode 100644 index 0000000000000..47d10954392b0 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java @@ -0,0 +1,348 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util.keystoretls; + +import static org.apache.pulsar.common.util.SecurityUtility.getProvider; + +import com.google.common.base.Strings; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.Provider; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManagerFactory; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +/** + * KeyStoreSSLContext that mainly wrap a SSLContext to provide SSL context for both webservice and netty. + */ +@Slf4j +public class KeyStoreSSLContext { + public static final String DEFAULT_KEYSTORE_TYPE = "JKS"; + public static final String DEFAULT_SSL_PROTOCOL = "TLS"; + public static final String DEFAULT_SSL_ENABLED_PROTOCOLS = "TLSv1.2,TLSv1.1,TLSv1"; + public static final String DEFAULT_SSL_KEYMANGER_ALGORITHM = KeyManagerFactory.getDefaultAlgorithm(); + public static final String DEFAULT_SSL_TRUSTMANAGER_ALGORITHM = TrustManagerFactory.getDefaultAlgorithm(); + + public static final Provider BC_PROVIDER = getProvider(); + + /** + * Connection Mode for TLS. + */ + public enum Mode { + CLIENT, + SERVER + } + + /** + * Supported Key File Types. + */ + public enum KeyStoreType { + PKCS12("PKCS12"), + JKS("JKS"); + + private String str; + + KeyStoreType(String str) { + this.str = str; + } + + @Override + public String toString() { + return this.str; + } + } + + private final Mode mode; + + private String sslProviderString; + private String keyStoreTypeString; + private String keyStorePath; + private String keyStorePassword; + private boolean allowInsecureConnection; + private String trustStoreTypeString; + private String trustStorePath; + private String trustStorePassword; + private boolean needClientAuth; + private Set ciphers; + private Set protocols; + private SSLContext sslContext; + + private String protocol = DEFAULT_SSL_PROTOCOL; + private String kmfAlgorithm = DEFAULT_SSL_KEYMANGER_ALGORITHM; + private String tmfAlgorithm = DEFAULT_SSL_TRUSTMANAGER_ALGORITHM; + + public KeyStoreSSLContext(Mode mode, + String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + Set ciphers, + Set protocols) { + this.mode = mode; + this.sslProviderString = sslProviderString; + this.keyStoreTypeString = Strings.isNullOrEmpty(keyStoreTypeString) + ? DEFAULT_KEYSTORE_TYPE + : keyStoreTypeString; + this.keyStorePath = keyStorePath; + this.keyStorePassword = keyStorePassword; + this.trustStoreTypeString = Strings.isNullOrEmpty(trustStoreTypeString) + ? DEFAULT_KEYSTORE_TYPE + : trustStoreTypeString; + this.trustStorePath = trustStorePath; + this.trustStorePassword = trustStorePassword; + this.needClientAuth = requireTrustedClientCertOnConnect; + this.ciphers = ciphers; + this.protocols = protocols; + + if (protocols != null && protocols.size() > 0) { + this.protocols = protocols; + } else { + this.protocols = new HashSet<>(Arrays.asList(DEFAULT_SSL_ENABLED_PROTOCOLS.split("\\s*,\\s*"))); + } + + if (ciphers != null && ciphers.size() > 0) { + this.ciphers = ciphers; + } else { + this.ciphers = null; + } + + this.allowInsecureConnection = allowInsecureConnection; + } + + public SSLContext createSSLContext() throws GeneralSecurityException, IOException { + SSLContext sslContext; + if (sslProviderString != null) { + sslContext = SSLContext.getInstance(protocol, sslProviderString); + } else { + sslContext = SSLContext.getInstance(protocol); + } + + // key store + KeyManager[] keyManagers = null; + if (!Strings.isNullOrEmpty(keyStorePath)) { + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(kmfAlgorithm); + KeyStore keyStore = KeyStore.getInstance(keyStoreTypeString); + char[] passwordChars = keyStorePassword.toCharArray(); + keyStore.load(new FileInputStream(keyStorePath), passwordChars); + keyManagerFactory.init(keyStore, passwordChars); + keyManagers = keyManagerFactory.getKeyManagers(); + } + + // trust store + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm); + KeyStore trustStore = KeyStore.getInstance(trustStoreTypeString); + char[] passwordChars = trustStorePassword.toCharArray(); + trustStore.load(new FileInputStream(trustStorePath), passwordChars); + trustManagerFactory.init(trustStore); + + // init + sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom()); + this.sslContext = sslContext; + return sslContext; + } + + // for netty server + public static SSLEngine createNettySSLEngineForServer(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + Set ciphers, + Set protocols) + throws GeneralSecurityException, IOException { + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.SERVER, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + requireTrustedClientCertOnConnect, + ciphers, + protocols); + + SSLContext sslContext = keyStoreSSLContext.createSSLContext(); + + SSLEngine sslEngine = sslContext.createSSLEngine(); + sslEngine.setUseClientMode(false); + + sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols()); + sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites()); + + if (keyStoreSSLContext.mode == Mode.SERVER) { + sslEngine.setNeedClientAuth(keyStoreSSLContext.needClientAuth); + sslEngine.setWantClientAuth(keyStoreSSLContext.needClientAuth); + } + return sslEngine; + } + + // for netty client + public static SSLEngine createNettySSLEngineForClient(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + Set ciphers, + Set protocols) + throws GeneralSecurityException, IOException { + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + false, + ciphers, + protocols); + + SSLContext sslContext = keyStoreSSLContext.createSSLContext(); + + SSLEngine sslEngine = sslContext.createSSLEngine(); + sslEngine.setUseClientMode(true); + + sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols()); + sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites()); + + return sslEngine; + } + + // for web server + public static SSLContext createServerSslContext(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + SslContextFactory ssl = new SslContextFactory(); + + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.SERVER, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + requireTrustedClientCertOnConnect, + null, + null); + + return keyStoreSSLContext.createSSLContext(); + } + + // for web client + public static SSLContext createClientSslContext(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + Set ciphers, + Set protocol) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + SslContextFactory ssl = new SslContextFactory(); + + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + false, + null, + null); + + return keyStoreSSLContext.createSSLContext(); + } + + // for web server. autoRefresh is default true. + public static SslContextFactory createSslContextFactory(String sslProviderString, + String keyStoreTypeString, + String keyStore, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + long certRefreshInSec) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + SslContextFactory sslCtxFactory; + + sslCtxFactory = new SslContextFactoryWithAutoRefresh( + sslProviderString, + keyStoreTypeString, + keyStore, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStore, + trustStorePassword, + requireTrustedClientCertOnConnect, + certRefreshInSec); + + if (requireTrustedClientCertOnConnect) { + sslCtxFactory.setNeedClientAuth(true); + } else { + sslCtxFactory.setWantClientAuth(true); + } + sslCtxFactory.setTrustAll(true); + + return sslCtxFactory; + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NetSslContextBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NetSslContextBuilder.java new file mode 100644 index 0000000000000..38ebdb452691c --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NetSslContextBuilder.java @@ -0,0 +1,92 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util.keystoretls; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import org.apache.pulsar.common.util.FileModifiedTimeUpdater; +import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; + +/** + * Similar to `DefaultSslContextBuilder`, which build `javax.net.ssl.SSLContext` for web service. + */ +public class NetSslContextBuilder extends SslContextAutoRefreshBuilder { + private volatile SSLContext sslContext; + + protected final boolean tlsAllowInsecureConnection; + protected final boolean tlsRequireTrustedClientCertOnConnect; + + protected final String tlsProvider; + protected final String tlsKeyStoreType; + protected final String tlsKeyStorePassword; + protected final FileModifiedTimeUpdater tlsKeyStore; + protected final String tlsTrustStoreType; + protected final String tlsTrustStorePassword; + protected final FileModifiedTimeUpdater tlsTrustStore; + + public NetSslContextBuilder(String sslProviderString, + String keyStoreTypeString, + String keyStore, + String keyStorePasswordPath, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePasswordPath, + boolean requireTrustedClientCertOnConnect, + long certRefreshInSec) { + super(certRefreshInSec); + + this.tlsAllowInsecureConnection = allowInsecureConnection; + this.tlsProvider = sslProviderString; + this.tlsKeyStoreType = keyStoreTypeString; + this.tlsKeyStore = new FileModifiedTimeUpdater(keyStore); + this.tlsKeyStorePassword = keyStorePasswordPath; + + this.tlsTrustStoreType = trustStoreTypeString; + this.tlsTrustStore = new FileModifiedTimeUpdater(trustStore); + this.tlsTrustStorePassword = trustStorePasswordPath; + + this.tlsRequireTrustedClientCertOnConnect = requireTrustedClientCertOnConnect; + } + + @Override + public synchronized SSLContext update() + throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { + this.sslContext = KeyStoreSSLContext.createServerSslContext(tlsProvider, + tlsKeyStoreType, tlsKeyStore.getFileName(), tlsKeyStorePassword, + tlsAllowInsecureConnection, + tlsTrustStoreType, tlsTrustStore.getFileName(), tlsTrustStorePassword, + tlsRequireTrustedClientCertOnConnect); + return this.sslContext; + } + + @Override + public SSLContext getSslContext() { + return this.sslContext; + } + + @Override + public boolean needUpdate() { + return tlsKeyStore.checkAndRefresh() + || tlsTrustStore.checkAndRefresh(); + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java new file mode 100644 index 0000000000000..6677fa950b9b4 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java @@ -0,0 +1,145 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util.keystoretls; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Set; +import javax.net.ssl.SSLEngine; +import org.apache.pulsar.client.api.AuthenticationDataProvider; +import org.apache.pulsar.client.api.KeyStoreParams; +import org.apache.pulsar.common.util.FileModifiedTimeUpdater; +import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; + +/** + * SSL context builder for Netty. + */ +public class NettySSLEngineAutoRefreshBuilder extends SslContextAutoRefreshBuilder { + private volatile SSLEngine sslEngine; + + protected final boolean tlsAllowInsecureConnection; + protected final Set tlsCiphers; + protected final Set tlsProtocols; + protected boolean tlsRequireTrustedClientCertOnConnect; + + protected final String tlsProvider; + protected final String tlsTrustStoreType; + protected final String tlsTrustStorePassword; + protected final FileModifiedTimeUpdater tlsTrustStore; + + // client context not need keystore at start time, keyStore is passed in by authData. + protected String tlsKeyStoreType; + protected String tlsKeyStorePassword; + protected FileModifiedTimeUpdater tlsKeyStore; + + protected AuthenticationDataProvider authData; + protected final boolean isServer; + + // for server + public NettySSLEngineAutoRefreshBuilder(String sslProviderString, + String keyStoreTypeString, + String keyStore, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + Set ciphers, + Set protocols, + long certRefreshInSec) { + super(certRefreshInSec); + + this.tlsAllowInsecureConnection = allowInsecureConnection; + this.tlsProvider = sslProviderString; + + this.tlsKeyStoreType = keyStoreTypeString; + this.tlsKeyStore = new FileModifiedTimeUpdater(keyStore); + this.tlsKeyStorePassword = keyStorePassword; + + this.tlsTrustStoreType = trustStoreTypeString; + this.tlsTrustStore = new FileModifiedTimeUpdater(trustStore); + this.tlsTrustStorePassword = trustStorePassword; + + this.tlsRequireTrustedClientCertOnConnect = requireTrustedClientCertOnConnect; + this.tlsCiphers = ciphers; + this.tlsProtocols = protocols; + + this.isServer = true; + } + + // for client + public NettySSLEngineAutoRefreshBuilder(String sslProviderString, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + Set ciphers, + Set protocols, + long certRefreshInSec, + AuthenticationDataProvider authData) { + super(certRefreshInSec); + + this.tlsAllowInsecureConnection = allowInsecureConnection; + this.tlsProvider = sslProviderString; + + this.authData = authData; + + this.tlsTrustStoreType = trustStoreTypeString; + this.tlsTrustStore = new FileModifiedTimeUpdater(trustStore); + this.tlsTrustStorePassword = trustStorePassword; + + this.tlsCiphers = ciphers; + this.tlsProtocols = protocols; + + this.isServer = false; + } + + @Override + public synchronized SSLEngine update() throws GeneralSecurityException, IOException { + if (isServer) { + this.sslEngine = KeyStoreSSLContext.createNettySSLEngineForServer(tlsProvider, + tlsKeyStoreType, tlsKeyStore.getFileName(), tlsKeyStorePassword, + tlsAllowInsecureConnection, + tlsTrustStoreType, tlsTrustStore.getFileName(), tlsTrustStorePassword, + tlsRequireTrustedClientCertOnConnect, tlsCiphers, tlsProtocols); + } else { + KeyStoreParams authParams = authData.getTlsKeyStoreParams(); + this.sslEngine = KeyStoreSSLContext.createNettySSLEngineForClient(tlsProvider, + authParams != null ? authParams.getKeyStoreType() : null, + authParams != null ? authParams.getKeyStorePath() : null, + authParams != null ? authParams.getKeyStorePassword() : null, + tlsAllowInsecureConnection, + tlsTrustStoreType, tlsTrustStore.getFileName(), tlsTrustStorePassword, + tlsCiphers, tlsProtocols); + } + return this.sslEngine; + } + + @Override + public SSLEngine getSslContext() { + return this.sslEngine; + } + + @Override + public boolean needUpdate() { + return tlsKeyStore.checkAndRefresh() + || tlsTrustStore.checkAndRefresh(); + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SSLContextValidatorEngine.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SSLContextValidatorEngine.java new file mode 100644 index 0000000000000..555d96e5cea86 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SSLContextValidatorEngine.java @@ -0,0 +1,176 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util.keystoretls; + +import java.nio.ByteBuffer; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLParameters; +import lombok.extern.slf4j.Slf4j; + +/** + * SSLContextValidatorEngine to validate 2 SSlContext. + */ +@Slf4j +public class SSLContextValidatorEngine { + /** + * Mode of peer. + */ + public enum Mode { + CLIENT, + SERVER + } + + private static final ByteBuffer EMPTY_BUF = ByteBuffer.allocate(0); + private final SSLEngine sslEngine; + private SSLEngineResult handshakeResult; + private ByteBuffer appBuffer; + private ByteBuffer netBuffer; + private Mode mode; + + public static void validate(SSLContext clientSslContext, SSLContext serverSslContext) throws SSLException { + SSLContextValidatorEngine clientEngine = new SSLContextValidatorEngine(clientSslContext, Mode.CLIENT); + SSLContextValidatorEngine serverEngine = new SSLContextValidatorEngine(serverSslContext, Mode.SERVER); + try { + clientEngine.beginHandshake(); + serverEngine.beginHandshake(); + while (!serverEngine.complete() || !clientEngine.complete()) { + clientEngine.handshake(serverEngine); + serverEngine.handshake(clientEngine); + } + } finally { + clientEngine.close(); + serverEngine.close(); + } + } + + private SSLContextValidatorEngine(SSLContext sslContext, Mode mode) { + this.mode = mode; + this.sslEngine = createSslEngine(sslContext, "localhost", 0); // these hints are not used for validation + sslEngine.setUseClientMode(mode == Mode.CLIENT); + appBuffer = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize()); + netBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize()); + } + + private SSLEngine createSslEngine(SSLContext sslContext, String peerHost, int peerPort) { + SSLEngine sslEngine = sslContext.createSSLEngine(peerHost, peerPort); + + if (mode == Mode.SERVER) { + sslEngine.setNeedClientAuth(true); + } else { + sslEngine.setUseClientMode(true); + SSLParameters sslParams = sslEngine.getSSLParameters(); + sslEngine.setSSLParameters(sslParams); + } + return sslEngine; + } + + void beginHandshake() throws SSLException { + sslEngine.beginHandshake(); + } + + void handshake(SSLContextValidatorEngine peerEngine) throws SSLException { + SSLEngineResult.HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus(); + while (true) { + switch (handshakeStatus) { + case NEED_WRAP: + handshakeResult = sslEngine.wrap(EMPTY_BUF, netBuffer); + switch (handshakeResult.getStatus()) { + case OK: break; + case BUFFER_OVERFLOW: + netBuffer.compact(); + netBuffer = ensureCapacity(netBuffer, sslEngine.getSession().getPacketBufferSize()); + netBuffer.flip(); + break; + case BUFFER_UNDERFLOW: + case CLOSED: + default: + throw new SSLException("Unexpected handshake status: " + handshakeResult.getStatus()); + } + return; + case NEED_UNWRAP: + if (peerEngine.netBuffer.position() == 0) { + return; + } + peerEngine.netBuffer.flip(); // unwrap the data from peer + handshakeResult = sslEngine.unwrap(peerEngine.netBuffer, appBuffer); + peerEngine.netBuffer.compact(); + handshakeStatus = handshakeResult.getHandshakeStatus(); + switch (handshakeResult.getStatus()) { + case OK: break; + case BUFFER_OVERFLOW: + appBuffer = ensureCapacity(appBuffer, sslEngine.getSession().getApplicationBufferSize()); + break; + case BUFFER_UNDERFLOW: + netBuffer = ensureCapacity(netBuffer, sslEngine.getSession().getPacketBufferSize()); + break; + case CLOSED: + default: + throw new SSLException("Unexpected handshake status: " + handshakeResult.getStatus()); + } + break; + case NEED_TASK: + sslEngine.getDelegatedTask().run(); + handshakeStatus = sslEngine.getHandshakeStatus(); + break; + case FINISHED: + return; + case NOT_HANDSHAKING: + if (handshakeResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED) { + throw new SSLException("Did not finish handshake"); + } + return; + default: + throw new IllegalStateException("Unexpected handshake status " + handshakeStatus); + } + } + } + + boolean complete() { + return sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED + || sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + } + + void close() { + sslEngine.closeOutbound(); + try { + sslEngine.closeInbound(); + } catch (Exception e) { + // ignore + } + } + + /** + * Check if the given ByteBuffer capacity. + * @param existingBuffer ByteBuffer capacity to check + * @param newLength new length for the ByteBuffer. + * returns ByteBuffer + */ + public static ByteBuffer ensureCapacity(ByteBuffer existingBuffer, int newLength) { + if (newLength > existingBuffer.capacity()) { + ByteBuffer newBuffer = ByteBuffer.allocate(newLength); + existingBuffer.flip(); + newBuffer.put(existingBuffer); + return newBuffer; + } + return existingBuffer; + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SslContextFactoryWithAutoRefresh.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SslContextFactoryWithAutoRefresh.java new file mode 100644 index 0000000000000..e18e8c616fbb7 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/SslContextFactoryWithAutoRefresh.java @@ -0,0 +1,63 @@ +/** + * 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. + */ +package org.apache.pulsar.common.util.keystoretls; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +/** + * SslContextFactoryWithAutoRefresh that create SSLContext for web server, and refresh in time. + */ +public class SslContextFactoryWithAutoRefresh extends SslContextFactory { + private final NetSslContextBuilder sslCtxRefresher; + + public SslContextFactoryWithAutoRefresh(String sslProviderString, + String keyStoreTypeString, + String keyStore, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + long certRefreshInSec) + throws SSLException, FileNotFoundException, GeneralSecurityException, IOException { + super(); + sslCtxRefresher = new NetSslContextBuilder( + sslProviderString, + keyStoreTypeString, + keyStore, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStore, + trustStorePassword, + requireTrustedClientCertOnConnect, + certRefreshInSec); + } + + @Override + public SSLContext getSslContext() { + return sslCtxRefresher.get(); + } +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/package-info.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/package-info.java new file mode 100644 index 0000000000000..11a8db4e0cee8 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/package-info.java @@ -0,0 +1,22 @@ +/** + * 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. + */ +/** + * Helpers to work with events from the non-blocking I/O client-server framework. + */ +package org.apache.pulsar.common.util.keystoretls; diff --git a/pulsar-common/src/test/resources/broker.keystore.jks b/pulsar-common/src/test/resources/broker.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..b4fec69ac2361e28da306de053e544c900fb18dc GIT binary patch literal 2767 zcmeH|c{J2}AIE>c!7wHdVo*sLaxHOw#-8!Sl_I(fVz{`JU72Ld5;G%_eZ2_TT}zhC zxUyF$V;M|#w@{YJk|L6=J39Bdx2Jo~^Z#?s{p0(`_jSJ8`F!8!_1RnATLu6C_UC|K zcDPO;_y7Rt^=KV24{+cWR3Hu3iY$l>ONYoH5rzQL9epD>IDXECc;E=x+xoXZ@_-NgxOZwkX@k!o~uhv;0e3e@a-s8fiem0YBQn1%jZU6jJ6}{W%^y|>z zn%{=hIt^zo_m=Kekz>Sw%R}K|A7oWl<3#>D{SyX=11fE&qruV!W#g<)7J7 zC>rc~*78`N8NXQ7;-Csiv@!KfpUi~g@bo*$ApO>IuJ+2vurp2@n8@#4eQ3tPUg8Zh z^lN%cE;JY$2!LN#h*fk}g+L&gG8_~CMkCSNBS^kODOg+2Coe-wG<>=9m(}>J9ir1} z_s|_h$LXRzUQINGbb0MD=( zYLpIqvPLK#)p2REu8zNOKxdHAnRjcfIglHs!wAIBsf0ZAf-sWeR!k4=E>9CKp(rsb)uRi?_2;mSzg+=hH5G{@_4}wHN4HVX68BB z_`l?I%Ga@^%&Rb%@t;}o1p&kr!exgmt_1Ht((7l(q6w_<#6bUobIZX&c2E|^53*9l zP6lsy#v4odQp_dO9-pf{!P$#>QEig>l)W_M%CTZ(C64!xIZTl_NSF29m z3T}3@(ObMmQHXl!oTGI9tz?B_4?UaDPFHDNbIi<)Q)(aVrEb{nT5u(V_idRcEq#=G z1%%+HhVJJ+&=5&cs93X#O~4%g%@j9tmS$ujH&Z+rYA@qZwkye(a-su)O52qu$q4+a z+4e#YSUHhl{lT(@i!SKv+$;3@-ksiYPxqyP>o4r{YF4KS;`m4UZr|NkPRi^te{{jJ~NkBuMDe?E**>*_+py>ScNSaBD2M_%;L|oN&(NpL_9-t>Fa=V{BNsZl+^#* zD*FKPRODm*_!kXvUi9?vB>4YHxCFi>oPQ(SeL)ioR;4>HsNV7MIM@b-iYLe>7`HQ!Z z@9?7Q`%n8gof|%!sr9nEsoX~Op=EtQN>$_5sRi;UOCzKfC)ovOWf|8nXk|R-#mPq^ zQVoQ>B$!6hLIQPt2uw_8@ibN_FxosgR(h(SQcR9g?3?IW5Q^LqSGn|l`%)k%so7Kb zzNG}0aHk|}a@u+}2c8t|Y)1^bc)}4h(%kDakYyu<1N-k7I8uT#WQ45gYq-~r`W9yS zFE4J}Xz6O`>4tj0&635Q%c<5jqN63onxQU%LAbYbkvxat^s+S)p#}{%ytw|r(1a2o z0jn(1YVsOd8R#$5#doBe8$Q1hN`1aqKVI5U#}IayXy@m~_@OJ6ga(axxmO|f4-Zp$1%&aqeAP5aULhB2WVeN|AL zFJ-)Tf@`5M@|wt@n)2KqHR$Vo?rV$L9e$gJN-k#}Bp&3Dd^OiGvFXwZN6~fFyV~ip z;#rsy&+IFw<6vD-OzW1UUi)51e%r|7sqB>olLVTiF7CiNLMbH{>#933P*CxJy6Bp@ z?V3tC5+VFzMKvY>X@DHN(`X+%l()B0i1)lJ_(Q-7&kg)}-O4F$u)JCa9(N|zLN3;J r`3UWJKf1f;+CM%2pPv7J(DPsCSENGW!FJm!fAA-4f18*?ZNn=n&yo`HfmuaSX)iIIh&v5}#H zL6ii)ks(OHzyvCQuAzxh3E4`f0myAd&vDNhf4^vvg_Pxt+NZAr-l+`dtw?`)`zqlh-ZS$1w6&GG^iI`M;Fn4thHd_Rgw(x$*s@}ghG59Limdcdw)cHT(8I{X8U%z0p zS97na_{2nq_K)k9F6Er`!Ro6Nlefr;L)5{xyY)yEM-gxV}k0$;8aaz=#|| zz(5Cv5F^9n&DVPRUs_Hq&aT!yYFy*sTC#AatxW6N6U#sK&-v4k`swu5PQ#RkH|I)l z$w^HK*i@VJV}66hVnI!V%Zp&|eY18p97oRvZ-mqDq^R4FT;Vqj# zuo}xf&)Q>nDX_YE-c*URV&_EN=1gsz8gRY7&4^w3iob3~)+!IF?!wcHBQAwq-K|k| z=yU$E8;~e@`979R+uTO!vHAEwS`}k8QQGQYiopRWjuO literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/brokerKeyStorePW.txt b/pulsar-common/src/test/resources/brokerKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-common/src/test/resources/brokerKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-common/src/test/resources/brokerTrustStorePW.txt b/pulsar-common/src/test/resources/brokerTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-common/src/test/resources/brokerTrustStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-common/src/test/resources/ca-cert b/pulsar-common/src/test/resources/ca-cert new file mode 100644 index 0000000000000..32c8d92d757eb --- /dev/null +++ b/pulsar-common/src/test/resources/ca-cert @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICmDCCAYACCQCYZHWHBQWWnTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJj +bjAgFw0yMDA0MjgxMzIxMDBaGA8yMTIwMDQwNDEzMjEwMFowDTELMAkGA1UEBhMC +Y24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5CuNhU2OYdwlrd/04 +FOgXbzGLs/RoWToWq39Q4WSEVr7uaEBktFEyaoCcLYwsWm9ziF0ms5SLqNDptFiS +c8Ftt1YJdTS+SfFRg+CWUyq8LmA9NsT/X6/Oy3Q/4398stzWdU1X6KXnWr4UIkDo +wLDJEFCBkJCqK6w0mg92rzu8pPfIzQ8kvTy0ECK0DfRqe7+9YVPVCrR5ZItln/nu +MnccQ9fQPL0pvTUXkWFAh/GupaUJkvA69SiAfzXJohtdCdrKrxV3m76kHHxoRlcf +z1MRHq/r1DWBHLuV6c8p22TW4fAke0i/qwjEroiDRGX2MohCaHTJT3xxIbopznBF +7GfJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJOz1oyP6TmRc2t7LcUzfEBFdKGZ +PRyF7cin8o+c/IBl8svViTFk4dmdGAoeGpRQsn1i+J+AOKNBSRdyydgEFvE8qopR +h7c9DzfSssf00eRAgdg8oCz2fOXDtLPwBTMe52q8MdJRe4OelRjNFs4VRpyVgZVQ +13+GMgcj1E8taGqqSBqLccujWNJW1bsoesLzb6bYQWe8WrCPTQxB1NLIoYTZvXoK +AkHShUEJV502UOxeRhjDjYeearlILIoV82sczXmlFNrhuiYxgIYa9tCywsagenRe +6/WANyQP5nmCky0odbK0Uh7XweppFdb76FrooWVcd94HZaJBV7PnNdLoj/8= +-----END CERTIFICATE----- diff --git a/pulsar-common/src/test/resources/ca-cert.srl b/pulsar-common/src/test/resources/ca-cert.srl new file mode 100644 index 0000000000000..aee9981385f77 --- /dev/null +++ b/pulsar-common/src/test/resources/ca-cert.srl @@ -0,0 +1 @@ +A30DEADB8FD23BED diff --git a/pulsar-common/src/test/resources/ca-key b/pulsar-common/src/test/resources/ca-key new file mode 100644 index 0000000000000..485d22098b8f7 --- /dev/null +++ b/pulsar-common/src/test/resources/ca-key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQII+lV8LqZ9n4CAggA +MB0GCWCGSAFlAwQBKgQQOGncQpnXEogdhApthe09awSCBNCErYMzOQEblSjp2HUq +whuE1l7EUp4Et3cCtMenXoGlNfzMG0llnCmbIJA4j13X2IfGGpYRKNEbkeUUAuX5 +Er8nwWBZw3ux3iD4zYUl2Q69tcnC62eQaA5+Zj5T58i0ptxYXNTZ0p+q9ytMSP30 +9gb8KwJQdXiKZU5UxflLb9TrU7OWi3Ucnjbw4YYmRkwRZGZ6P+fUSC2FpixzI0J6 +73yBI36zfTvFJR5bslIFw2CSHbIFZeJ535oiLVqzOzjJHEFZ+OTeuN+Vh8Cktz3Y +KAOdD30knci64vugxo5iLLWc/IXQRcuTBNHskPsym5ahYVSM9/+JqMw4OL2xyDbR ++YCdx3iER4PD9ErCXSCdMLsx4izuuULQGAyONtnj9awUwmzKMqEbtr6lLFChgyEc +TiWpm5sLZPP1SiyjGKnhesiWRyB25b6iC/fSktf10Nrl+Fb1YJeLLSufG+ZVpy5h +sN2cPkAnymRw9WyEaitkUqNI52GfClOYPJ0H0bU1LssUs2+d8HRHz6GWb3oW2IX8 +046PE6y0kIuUYEryTQ1lzmLdREIOG2yfkcL7ywN1WBwtqPZBV69peA8P3M3T9VEz +nnTtEL/5VU0M3Bsm6GM8j9fmJDBmBG+6E0hUD6JCuDeBBvTBuknZIbiBf704DjyI +qPDZhAkkVfD7dylm96GJLn38PrPk8sQPa3IR4zAXga7YHkXvh7HuYc0V9tZNn7mI +/f3XoDV2wk279TAr8NLDgLGQHK0K1tDTirJXf09KxWj4zZOC914SeASjEhzQZ6se +K3PbG4ZQnJ4+dAsY9K6Qgc9BxjyeInCdsDoDROtjqNfKSkcSelKhkp8VEJBYinWT +PcBHt6/1iQvB1fyp+OexBjq9CiUDg/Be2QTUZfVqCkyIVptPnSvPyTg67pvWl4M6 +uRHxsufQ35WsZAhqEr5eQ7mPAvem7XCUJ14hPz8/f5Qm2+llPUVIq9Rpa83GP/TI +P9W9F6tRj0qpW+QpPlxmyISf6oHiE9IOGmRkdFV0m54JiSQsqGqXr/NCH423LzFd +21TJVKH02+v0Tgu1A1+HT+dEKQOorBZ3/HQ2NuQuYi+rF3adNEcMVmfy6DlnHkYr +lDIG0exVC6Bveuzs4BCupKz/g7CbNzAEOO9XwcD/crNRnjE+nzdu/SDNtw5vIlKA +hSSzEcsgVkqGbeaV5fASgb3pAz50xJHwvX6O4cAOvbcemjUGyd16IxIdI5jPRVvh +u1BiK3YwSsdtg8sQ54YVbirgQ6SWKIXdN+79luksimbUVnEu8VJS1fu9H5ojefAd +J9hMeiGht+6LKvyPh6Sa++bCfYRjZmbkX4h6Afc3Wwibh7KnfpAUlt4QzqA7o+x4 +7rCaI/w/uK+EFaqtn67TowAg/iq6Lxd7i9l06JBSC/BA+Hsw6tS86f13qPg2OtTK +GydNfxnGtfZIMsUUtfldp9mB+afRFqX49joEGGmb2vnm4Q09QaGP5tagpJboIAqO +c/pxtGWzqokR8sdAwX0oAb1vsPrpY3sbUGqSYJfVR6s4SMXJdbSSiIKzuwrcO+HX +TSiq2yGGfJBl5bh9E8cnH4NifAJC4kXsBERwy+Ahq/64MRps3EW2tFl6nPuA+HMl +IXg4wepHtC7w7W9nJ5Tw3b6X4g== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/pulsar-common/src/test/resources/cert-file b/pulsar-common/src/test/resources/cert-file new file mode 100644 index 0000000000000..7d94877b49522 --- /dev/null +++ b/pulsar-common/src/test/resources/cert-file @@ -0,0 +1,17 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIICpTCCAmECAQAwbzEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93 +bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMH +VW5rbm93bjETMBEGA1UEAxMKY2xpZW50dXNlcjCCAbcwggEsBgcqhkjOOAQBMIIB +HwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6 +v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58ao +phUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvM +spK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4Jn +UVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1 +kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kq +A4GEAAKBgBmF8WdZ9Yv1Sf2qjqF19DUSY3YB67B0azz+689y8lZw0tlnSuej0bBE +NIP6lvgC/PIPFdxvkInZOgB3TsWwkpxHzKbFZTo2Yg2txZ1IH4KX1QggePeybi2m +E2soysZ2/r3nX2ZSOTdzDLicVo3yyKAuM8u14N0zBeJR9NMdOG1NoDAwLgYJKoZI +hvcNAQkOMSEwHzAdBgNVHQ4EFgQUXx44DNZ7cUAoduGpv/MC+d5noyIwCwYHKoZI +zjgEAwUAAzEAMC4CFQCQ2BDtunGs9G0Ra+16OHPaWAI6+QIVAIrGtZWtGka43D+3 +GqOEI5+wGsbh +-----END NEW CERTIFICATE REQUEST----- diff --git a/pulsar-common/src/test/resources/cert-signed b/pulsar-common/src/test/resources/cert-signed new file mode 100644 index 0000000000000..20db5c0703e7a --- /dev/null +++ b/pulsar-common/src/test/resources/cert-signed @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAncCCQCjDerbj9I77TANBgkqhkiG9w0BAQUFADANMQswCQYDVQQGEwJj +bjAgFw0yMDA0MjgxMzI4NDJaGA8yMTIwMDQwNDEzMjg0MlowbzEQMA4GA1UEBhMH +VW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4G +A1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjETMBEGA1UEAxMKY2xpZW50 +dXNlcjCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3 +UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQ +paSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864r +Ydcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3e +y7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYT +t88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJ +qIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgBmF8WdZ9Yv1Sf2qjqF19DUS +Y3YB67B0azz+689y8lZw0tlnSuej0bBENIP6lvgC/PIPFdxvkInZOgB3TsWwkpxH +zKbFZTo2Yg2txZ1IH4KX1QggePeybi2mE2soysZ2/r3nX2ZSOTdzDLicVo3yyKAu +M8u14N0zBeJR9NMdOG1NMA0GCSqGSIb3DQEBBQUAA4IBAQAlf/MlmkGXvOHi68LU +FoRoDh0UMUVUMYcpf+LicZkOveD0r5J5z6igDQZ5qT7RMfkSkM8pSl7xcuPNzNkT +teeH29QOaTiYax+T9yAT9p/i2/DwiLbrcSdPT8UKOy5CVPHEtlreHupiezaID0Op +IFaeuvBaI/HSbRZQ2IdCXTnXSQ+8rkrcoxDyIi9wjaEnWKwAqphgq0C9icNVMleu +Lz3Wz51Xn03DQTH9uOtZu6kQYzfAEi7Z0hKF98TQ3BmwEwCRf+h5kE3wFbuT9QFh +uHLeCvNlJoaajT2Qud0YWkIN+z1yVKzT3NdndmNm5SuM2Mzec3b4PHSLpmW0Cnwv +UW4t +-----END CERTIFICATE----- diff --git a/pulsar-common/src/test/resources/client.keystore.jks b/pulsar-common/src/test/resources/client.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..499c8bec41b32febb3846428d27294e2b77ef487 GIT binary patch literal 2767 zcmeH|c{J4PAIHDn!Avu*EE8HRC1h`A3|YnvmFOnMHn=6*7)zOq8I#7nvseq)mPod- zMGGz?vbAVL2t$@g(Jewi4_9U%&1-zyI$!_m9tc&htLc^Ld{0`Ml5Tv-W!J zH2?sR{{+;>i$W$-0RRlS{Q>OP3Gy6S=EQ`*GhWm*zMA z=Ds{UCXkh_o1o)P4vjmeUVT2kwC5GiH8u3k$yygU4clxpkB@wI!BMMG&(qBC-um&1 zhaJ?t&zTWbqXRTs!c_63d-En0^QqgF?Nava*f-dNYtn2okWcY4kDV_sE{3PhnJj6rm zKd6wUUurH=i14=}D0e$bD^<+&8?Y4`(qDCJj`$9Yx3Z(ZGRuRJbtKXDloXX#IE zw~GkbQLp*%)PY%2J46Hoz^^MLEV{5lAdpr)kdXAsI3+ZQq085gFh4-$UNlpPe%*F? zAu0PKi@4D7=q%cId^l$_LTT%H`>LP3-{rB43N38pDeXg|a`U(==kT$$W|DgZRreQr ze=}zf;qrBsBVM%ds^`S?l_mol(E>apGBQ@Yc*S~a zd`L&3tH9+^gT|S=Z^>$T75^^|0F9k{FvC zc+*T{UN{VU%l5Q z=W!iAIQXK**oLGQF1oCpZ7IziC-Ijk=AyU}nV%QEd>9uR6hkU9QpIP9(U>03tR)qL z`W~l*JNczBhPbpV_QCyJ<)^J$-1T;Am`XHo+3<~gl*h=y%hTzj0r=^_hR2o_*MMd!{1m?)pjuYq<-Fa!v5qGZ8Wk*(`b5c%()*5-_^-}+#MQ{SL@)3_hW`) zCrI^Z%XU`+bng*5ChL3bi_*d)Yafr*P*0yGIX`(hjC(Ftud~%-uKU!l2VY&|d0TDC zj5&-uU0k=?*h3&QNwJ@zR5KX7_~+^ZT2P!044vZe2H7y*RzyH_8w;8~DU@WGm8SG##EE%GR6xZE{(<)&ckXexOBZFY5+gJ>bAG za~1rmn^~gkhgj*c)&sQ{jFq!7OCma?xgKPLGI%*XGA zcEV8SSZLTg95ud#$U{Wz#s};EGxPtM`G13%ZvuCi#Yc1=;KKCxfr5kC$Mb@Z{{c;5 BppgIo literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/client.truststore.jks b/pulsar-common/src/test/resources/client.truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..8eaa06ba5812f2440651d87080cdbec252b596e7 GIT binary patch literal 731 zcmezO_TO6u1_mY|W(3o0$%#ez`6WPZ;epRJKN(mf^h^ybfhy)0G%?LEXku(&;$)bS zQrgbSI&H22FB_*;n@8JsUPeZ4Rt5uJLv903Hs(+kHesgZJOc%BULyko6C(>lVesM>v+U6gVk4!hI-SJi=<;YZ=G5%T0VQ<5}BF|w{ZFM!Gdz@Utck8 zl-WJ?<$2B9Dc2r;P^tFVznbI7x{hX-)Ne)|P8lU9{c8#pcWIt0aD9`0l8Kp-fe|@` zfPoGSAx4JDo3HiszqFiKoL#MZ)VRjMwPfK;Tbb6kCzgNepYx|7_0#FAorWn7Z_btA zl9QSeu&Flb$NUD1#g3lhMJI2th<&tK)fL#j-Im|{(x&5IEBPd8n|rIcm>e&)I&y~3H4AtX=O%Huw|!pLP7j?f(a+g3 zXDgSA+f41)hmI|%Dv5jjwZUA4|5;_zWL=HYOMb1 rQ)9~Sv8OI_4B!0R^wNv|{|gnRGCu5TJ*a(QhWm!M^WR@jJN^~`qDeO@ literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/clientKeyStorePW.txt b/pulsar-common/src/test/resources/clientKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-common/src/test/resources/clientKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-common/src/test/resources/clientTrustStorePW.txt b/pulsar-common/src/test/resources/clientTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-common/src/test/resources/clientTrustStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-common/src/test/resources/old/broker.keystore.jks b/pulsar-common/src/test/resources/old/broker.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..d4526ac7bb26ebe887f4201e4e27f2b03f6a7b0f GIT binary patch literal 2928 zcmeH}Yc$mB9>?cDGiER*m)S-t_e+d0#w~{I8g*t|b`-f(7%Kg_K1oVZQSE;NXIw|AuBmy^L>ezq>AM65%5a$2Zz;lxLx@3QQFPI?(=U2A*}tYzU}1k zTpxojl)-A@+DVW%wRrG@Xzyf29(pl&ZT!?Sr^aVF!z%NB&ePA>!q3pab_>?BwvfqYPXpen9n5$Z8VH^W1&0K zpY^1H54P)WK5~cShNbNMSssO*w2k zq0Gl+$b^t{iEp_JSZYp2W3xVEJ^+jSr3@Z$k|kGDeDR19vbTCZ)Vu1;3{JjA@Kzr)^(SKSug-JjPC7Puyu&t%EG`VOx+@?CSf` z`QOd(2q)&9d=HIUGI7g2_E#fnl7>D}4Io@;-)AtDq*E*uR?kV z(B5wzrnzF0b^@2W_vt}D{WiyGu}giW_6Rbc6apCC@(6ER_4S@46&fe6vWIq;9Pk^p zclHncQVpS+!xn3ewMtbZo)G0cZ%3a|`iDKydZ!-hH6vk=y;~CF`DIG?f^AAlL5^h} z-0(S{Hv0^`!=^+F6JVgFsT2$)4mr*8Yp-Pm_iu#T{BRIvbxoM_VhdGYu>`5WvLJUv zXvz>_>HKbOL4{Z=`P+Pp{cl(@!C_7R14~;1Hpoaul!z_R!CgawLTHg8UlnoG9~JnY z6v;m+h~E`yk^N0jqF}yZw4l>rqWC=$jXf~92(@i{p@3+i0%kHnYjH=JN)d9pN8a1z z7)%b2F39qZSWVl4o+;bJKXlfn96b5Gy7P*Mb&9+2Y2$nicA@2lEA$<&-L~foQE{SF;Kx5)d=)yXH5oEe_sF?7m?=&{A7=!t9Y- zbyWJj$3n}`tYtR`gf*NM;Oyacxu(nG@CY3)!;G&Yq3If94mxNQuU!In=|#z)6*~H& z%g-q~9}4d(>)kJjxDrwrhY+fn`u?`!8x3gUTZ?|H<2a`!bI!dV^!$_uV_zhD#mBg6 z`2!C4^>z{l!b}8h`e?&&Q;ORRXFm2-f4oiX_HJhBb+*hYwubq}Cb-u>zVL}ZTs4O8 zU5=Bx@9eeu)81tE@{{IBl|CcTbyUXWF0V{Zt=XNXvw3}poJh~zS#^&T43panw1Ql^ z^)2fF4lKj%7kKy{mOgw-)^Q`{<;iy45p(^uvsP)CNrHgWph3u%w&tb<<(K&G$1!Y0>%B zJKBAbK>$~q5vY>n%JsuG@pzWqGakDNtbuG1>du40{b|%ks|7 z?7}Nv?(Ma;h1ZX7o6w800(JCE$sMQsaP{zDpJpccWnf(_2VMAAOYv7r@qf`$kj{2j UN>L-@#9;S4Y`HuML>ImNpCPQYmjD0& literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/old/broker.truststore.jks b/pulsar-common/src/test/resources/old/broker.truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..0c0e694495282dba93e7a8643b6a5e5997c3b1cd GIT binary patch literal 797 zcmezO_TO6u1_mY|W(3o0$%#ez`6WPZ;l|&5pBPvp^h^ybfhz79G%?*WXkt9X#L4jF z@4ji(=iOc#@Un4gwRyCC=VfH%W@RwYGvqelWMd9xVH0Lb&Vz9{c$kv14228?Kr+lc zoH_Z)i8&eh#U%y`;=D!%1|~+PhNi|QMrKhG{6>Z#0Ruy*0LdmcF)ATDijkFpxrvdV z0qAZnrY1&4h8Z}nHw zSb4>|qf+hEuWKDQKIb(#{z*EY_Hu5b-W6fN{Y*|fud}b*mGb_J<_DKUd#pFyRk)Rw zqH*{1p^76JH;)~)?<#$NRlF+OqlBaI#`Nk5DZ;v@0vmGg{eJ#%q1TFU&U3W6b>af1 zCWVz8j}hH)Nxn$7UsFo)0Y~EEi%mHi^PklJ|2DIBitqJ?XN&hujZ?h+Oj2%7{XuW<~}^l-kG@iw4x=im~X%YE}rVgU;K04bTP4&^tWy-ylnq)xiP%-QEX&-U!vT5oo8ZRd=J>F4dgem4Ko^2=V?ShJ2rddlIO_d_>$E@L`+WYZS0 z58Yd)Z&UlnYURQH-|CxqU3668z3sW|Z(S2Mu6;h~DDSU^%yV)@63(j=Lmrzr&*?d2 zCByjRRHvB1^jBIB7>niR)(hP#_`LtEisos-6PYXexIM3Z;&wTwyvH*|Y-7ReC%2dM zi&q#AuPomVOs7#Eb3^9ry&dslMRcGA`c Q-7b+YyP8W~&gA|B03y#mzyJUM literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/old/brokerKeyStorePW.txt b/pulsar-common/src/test/resources/old/brokerKeyStorePW.txt new file mode 100644 index 0000000000000..bdc331e059838 --- /dev/null +++ b/pulsar-common/src/test/resources/old/brokerKeyStorePW.txt @@ -0,0 +1 @@ +broker diff --git a/pulsar-common/src/test/resources/old/brokerTrustStorePW.txt b/pulsar-common/src/test/resources/old/brokerTrustStorePW.txt new file mode 100644 index 0000000000000..bdc331e059838 --- /dev/null +++ b/pulsar-common/src/test/resources/old/brokerTrustStorePW.txt @@ -0,0 +1 @@ +broker diff --git a/pulsar-common/src/test/resources/old/client.keystore.jks b/pulsar-common/src/test/resources/old/client.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..e5d074e96e5fbc4ed017ab9779d369ccb86837c7 GIT binary patch literal 2926 zcmeH}XHb*b8prb{Apw%mLJ^SAya*`p28cl-QdJOKs#J9dL^?z;ETRHQ2r3;F1QrzC zuoz?%f)pvrE{f8GOA(|<5u_+AktUrR-RrJ*_tTyG?aqF9=FIayXU;tHod5aFvo*Ul z3xPnOzX!sb97d(mAP^w56m41q5pdNefcyC;hCn8?A7ldcP$9_p=93#2+7EvKF$f{e zOO}^DVE_On00Fh}C{PFiI}S%+q23{16G1f8D*!J6ihbUJp@o8|-sB)Z>RB47B!kff zL492Uo}j0%yU%@xs4o7q0OJ2G{MR=!0hMoy0t6s1CLjvo*DVZX0s!Puy4#&dS-2W= zrLDB_trEQ%lS&X)?GnPh$tw39JWDK7+&Fwv%;s@8c4FhsG<*1#@|*XSpa;2g+fj${ z1KrFe)q_Xb>@vrrgNO&?lF<25q;O;@vD{Wgy|9~fB~q<?YEUc!?Eo$&A>T5|QZd!s%>wDf{oDe?uO6Gi^B~38&b>@XM z3Ra6CRuVk4J~?{lC}-I`cModsDZ30WXWEMsQYD>=VG5}la>^ruWOfHLNIh>nW_vk1 zD*ae5eS-ZYdgE3F&u2Z-I4;;Y-C<%<8$ptAs)E7*2=Hx(_|L}g5CD-E zOm@Sp3D>&b*3r6AJYwhltft#YYw&7Oi}HSq^f9|MVf2NuMcbg61So=*8pjRoGF~dg zP&wU2vb0VLlf#80)`m*o!2;W@UM5wS$LwpmAD>0fY&Tw7ATF}jj8*kCqT%xCPy2?C zmsk}*8=jSxN%IoRGAq=!1Pm>a+lI?B(H`!hLzThE*+ZV(`;*riFl%)GHeA>a^CGg- zn7(;#QoZ3$V5K=;8q9pJIRc!;-HegwKeJFbyGx@*ywRVNg0kwFM_II~)>=`dxo19% z59Fr=nNkJo&xcJ$Aa5;-66nE^J|fsL9oocC7JhOJ8-Mdc)iZi$Xb~7O{s#ace^~Q3 z|EE^)*HpfF5Xuj%y7r7ZQbHpVx?mO21JtVL_ATgjkd4$S~?nXzAr=H z^|PII2{n6L+GqE}>6}Cx;=JdCX|&DR45#w;5#TK`DmfVK$P$<7Af-?w_xFhoaF_0- zeIh2+<`_SKHS;>+GSIAR~S{IH7S*fHggwfADQSyF|hHk2Os z#&w#Xe>pRkvktSrS8;cq#*y|DRx-O@&Pu;};^!TaM<`0UP4UMnQ>Y+ob(TvuaEKWP z>l4~cnlRUBII+ngXO;`)w18Ab|CEQZzBjqE)s}n2tyugtYsfQuu+`e>-54DY9qWL1 zI2lDP7W_i|CG39b?#^meSdX0K+Xlbq_C@kl61ma(m;B5FuJF;s$JJLX!HPj9tcZ`M z>wGL-*{!y~6=?PS*53mE8&`*k;?WY@L2hNWt7xh3a#P%nc-SJ0=<`*>CcX50Og zpO-fUzaXYkZ(CE?yH)vIeud;R`J7WAB?@1UUG8eYJ%tjSut-> znl;1s%vBc~lo68YYwzZgQr+!lI}wT$W6;a2q(fDwY}BQ(mF+^hNsR@g2c*v@I$caw zA7MYh?Ri)bel>(0gIZH2czs;=IuGn3wdQ|(!m&?_=fn<=y1&aqTubyhMZa+T9~8ib zwAF5<31hGzPzL=wsa+PquMaQjCd4Eob|-|Umyg(W=-jwd+{~-!s2VF#H>#7omnb1; z>p0_}9&h4nn)E8OUjcBJ^t!xceUySB4&`-rLmxgEXhtjA2_V5dK-MK(%p$#S$M-^# z2f~(r?Gw#DWwLk_uj$8zbib=X`icv3wN`%&*H7>vmXNakAaNHWcl|}q!fr|D`;SwK78O=W=u5M$Ip=yv z68U3X=YOiZd)ey^BkE_^gB$GT7tRKCellF6LVHxsx&e-uQLe+oXT7+5TU|05!|+II zlAkVv_*NY9{k4hxJbOAQxOFlYH|<~Xz91zkk8G+Y7>nKt|NZCwJy85TQ2aj+6r~vD WMS`me_p(`IN!OHt!s?S~v3~*dg{Tw& literal 0 HcmV?d00001 diff --git a/pulsar-common/src/test/resources/old/client.truststore.jks b/pulsar-common/src/test/resources/old/client.truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..36f9d726331bedbc4aa0cefdb6177c390b3578e6 GIT binary patch literal 797 zcmezO_TO6u1_mY|W(3o0$%#ez`6WPZ;l{uGObo0MdZq@JKo$24nwV}GG%+4x;$(R8 zci*(?^KP#Vc-c6$+C196^D;7WvoaXy8FCwNvN4CUun99I=fOA}JWNShhC&7cAQ@&J z&Yb+@#GH)$;t~S|ab6<>0}~@tLsMfDBeN(8ej`JWfPo=YfMgS!7?qG6#mLIQ+{DPw z0CYDOQxhX2!;UGD3o9jA)S7;r+jQcQLc=NEep4>x%bap|XKYQsQ*6FP{*PO%fY07i z;b(sqzFd5Np5onC+YO=&}+pv=Q-NkI&lG0 zlfp`l$B1sYBwr-kuPLSYfFp77#iksM`A_Qqf16o5#rJx{v&H+S#wp%@CMmZkf7-ll zAJTqT+PCJ~PYHVVT#wCKC}bxSGa~~datHyV4H!a<4ALCEk-WdS_Czeu*PXWhK|u16 z-B)aN?@U~MTG5hM%r{^H7f^Ubg?bk~e?FmDQ3Zmr|NmY-C~k zb#K#C=IryHXM1*Stv5TlwsS_q^z-&#KbwDP`DL$ctXan*J>~Gt`=J{=moXhZvT2Lh zhwd%Yx2gSOwen#9Z}m;QE;_34-u7Jfx2_2r*FK+gl=oLd<~g|{3Fp;`A&*U*=ky%1 zl41ODs#DBh`YWvmjKy+u>xFI=eBS?7Mf0@aiOdy!+@9Ayal4#T-s71fwz1&#liN%B zavbwH4pkLBFK3_kh2OLxw=#`S__2P;v!yN>QjI>D&MTD*j0;N2c?BHml8 public static final String TLS_HANDLER = "tls"; private final DiscoveryService discoveryService; private final boolean enableTls; - private final NettySslContextBuilder sslCtxRefresher; + private final boolean tlsEnabledWithKeyStore; + private SslContextAutoRefreshBuilder sslCtxRefresher; + private NettySSLEngineAutoRefreshBuilder nettySSLEngineRefreshBuilder; + public ServiceChannelInitializer(DiscoveryService discoveryService, ServiceConfig serviceConfig, boolean e) throws Exception { super(); this.discoveryService = discoveryService; this.enableTls = e; + this.tlsEnabledWithKeyStore = serviceConfig.isTlsEnabledWithKeyStore(); if (this.enableTls) { - sslCtxRefresher = new NettySslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), - serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), - serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), - serviceConfig.getTlsRequireTrustedClientCertOnConnect(), - serviceConfig.getTlsCertRefreshCheckDurationSec()); + if (tlsEnabledWithKeyStore) { + nettySSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + serviceConfig.getTlsProvider(), + serviceConfig.getTlsKeyStoreType(), + serviceConfig.getTlsKeyStore(), + serviceConfig.getTlsKeyStorePassword(), + serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustStoreType(), + serviceConfig.getTlsTrustStore(), + serviceConfig.getTlsTrustStorePassword(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCiphers(), + serviceConfig.getTlsProtocols(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } else { + sslCtxRefresher = new NettyServerSslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), + serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } } else { this.sslCtxRefresher = null; } @@ -57,9 +80,13 @@ public ServiceChannelInitializer(DiscoveryService discoveryService, ServiceConfi @Override protected void initChannel(SocketChannel ch) throws Exception { if (sslCtxRefresher != null && this.enableTls) { - SslContext sslContext = sslCtxRefresher.get(); - if (sslContext != null) { - ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); + if (this.tlsEnabledWithKeyStore) { + ch.pipeline().addLast(TLS_HANDLER, new SslHandler(nettySSLEngineRefreshBuilder.get())); + } else{ + SslContext sslContext = sslCtxRefresher.get(); + if (sslContext != null) { + ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); + } } } ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder( diff --git a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServerManager.java b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServerManager.java index fa4761c4f090d..0c0bb95251e3b 100644 --- a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServerManager.java +++ b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServerManager.java @@ -19,17 +19,15 @@ package org.apache.pulsar.discovery.service.server; import com.google.common.collect.Lists; - import java.net.URI; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.TimeZone; - import javax.servlet.Servlet; - import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -74,14 +72,30 @@ public ServerManager(ServiceConfig config) { if (config.getWebServicePortTls().isPresent()) { try { - SslContextFactory sslCtxFactory = SecurityUtility.createSslContextFactory( - config.isTlsAllowInsecureConnection(), - config.getTlsTrustCertsFilePath(), - config.getTlsCertificateFilePath(), - config.getTlsKeyFilePath(), - config.getTlsRequireTrustedClientCertOnConnect(), - true, - config.getTlsCertRefreshCheckDurationSec()); + SslContextFactory sslCtxFactory; + if (config.isTlsEnabledWithKeyStore()) { + sslCtxFactory = KeyStoreSSLContext.createSslContextFactory( + config.getTlsProvider(), + config.getTlsKeyStoreType(), + config.getTlsKeyStore(), + config.getTlsKeyStorePassword(), + config.isTlsAllowInsecureConnection(), + config.getTlsTrustStoreType(), + config.getTlsTrustStore(), + config.getTlsTrustStorePassword(), + config.isTlsRequireTrustedClientCertOnConnect(), + config.getTlsCertRefreshCheckDurationSec() + ); + } else { + sslCtxFactory = SecurityUtility.createSslContextFactory( + config.isTlsAllowInsecureConnection(), + config.getTlsTrustCertsFilePath(), + config.getTlsCertificateFilePath(), + config.getTlsKeyFilePath(), + config.isTlsRequireTrustedClientCertOnConnect(), + true, + config.getTlsCertRefreshCheckDurationSec()); + } connectorTls = new ServerConnector(server, 1, 1, sslCtxFactory); connectorTls.setPort(config.getWebServicePortTls().get()); connectors.add(connectorTls); diff --git a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java index 30269e4818ff9..a62d7556e7e13 100644 --- a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java +++ b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java @@ -22,6 +22,7 @@ import java.util.Properties; import java.util.Set; +import lombok.Data; import org.apache.pulsar.broker.authorization.PulsarAuthorizationProvider; import org.apache.pulsar.common.configuration.PulsarConfiguration; import org.apache.pulsar.discovery.service.web.DiscoveryServiceServlet; @@ -32,6 +33,7 @@ * Service Configuration to start :{@link DiscoveryServiceServlet} * */ +@Data public class ServiceConfig implements PulsarConfiguration { // Local-Zookeeper quorum connection string @@ -81,7 +83,7 @@ public class ServiceConfig implements PulsarConfiguration { /***** --- TLS --- ****/ @Deprecated private boolean tlsEnabled = false; - // Tls cert refresh duration in seconds (set 0 to check on every new connection) + // Tls cert refresh duration in seconds (set 0 to check on every new connection) private long tlsCertRefreshCheckDurationSec = 300; // Path for the TLS certificate file private String tlsCertificateFilePath; @@ -101,217 +103,23 @@ public class ServiceConfig implements PulsarConfiguration { // Reject the Connection if the Client Certificate is not trusted. private boolean tlsRequireTrustedClientCertOnConnect = false; - private Properties properties = new Properties(); - - public String getZookeeperServers() { - return zookeeperServers; - } - - public void setZookeeperServers(String zookeeperServers) { - this.zookeeperServers = zookeeperServers; - } - - @Deprecated - public String getGlobalZookeeperServers() { - return globalZookeeperServers; - } - - @Deprecated - public void setGlobalZookeeperServers(String globalZookeeperServers) { - this.globalZookeeperServers = globalZookeeperServers; - } - - public String getConfigurationStoreServers() { - return null == configurationStoreServers ? getGlobalZookeeperServers() : configurationStoreServers; - } - - public void setConfigurationStoreServers(String configurationStoreServers) { - this.configurationStoreServers = configurationStoreServers; - } - - public int getZookeeperSessionTimeoutMs() { - return zookeeperSessionTimeoutMs; - } - - public void setZookeeperSessionTimeoutMs(int zookeeperSessionTimeoutMs) { - this.zookeeperSessionTimeoutMs = zookeeperSessionTimeoutMs; - } - - public int getZooKeeperCacheExpirySeconds() { - return zooKeeperCacheExpirySeconds; - } - - public void setZooKeeperCacheExpirySeconds(int zooKeeperCacheExpirySeconds) { - this.zooKeeperCacheExpirySeconds = zooKeeperCacheExpirySeconds; - } - - public Optional getServicePort() { - return servicePort; - } - - public void setServicePort(Optional servicePort) { - this.servicePort = servicePort; - } - - public Optional getServicePortTls() { - return servicePortTls; - } - - public void setServicePortTls(Optional servicePortTls) { - this.servicePortTls = servicePortTls; - } - - public Optional getWebServicePort() { - return webServicePort; - } - - public void setWebServicePort(Optional webServicePort) { - this.webServicePort = webServicePort; - } - - public Optional getWebServicePortTls() { - return webServicePortTls; - } - - public void setWebServicePortTls(Optional webServicePortTls) { - this.webServicePortTls = webServicePortTls; - } - - @Deprecated - public boolean isTlsEnabled() { - return tlsEnabled || webServicePortTls.isPresent() || servicePortTls.isPresent(); - } - - @Deprecated - public void setTlsEnabled(boolean tlsEnabled) { - this.tlsEnabled = tlsEnabled; - } - - public String getTlsCertificateFilePath() { - return tlsCertificateFilePath; - } - - public void setTlsCertificateFilePath(String tlsCertificateFilePath) { - this.tlsCertificateFilePath = tlsCertificateFilePath; - } - - public String getTlsKeyFilePath() { - return tlsKeyFilePath; - } - - public void setTlsKeyFilePath(String tlsKeyFilePath) { - this.tlsKeyFilePath = tlsKeyFilePath; - } + /***** --- TLS with KeyStore--- ****/ + // Enable TLS with KeyStore type configuration in broker + private boolean tlsEnabledWithKeyStore = false; + // TLS Provider (JDK or OpenSSL) + private String tlsProvider = "JDK"; + // TLS KeyStore type configuration in broker: JKS, PKCS12 + private String tlsKeyStoreType = "JKS"; + // TLS KeyStore path in broker + private String tlsKeyStore = null; + // TLS KeyStore password in broker + private String tlsKeyStorePassword = null; + // TLS TrustStore type configuration in broker: JKS, PKCS12 + private String tlsTrustStoreType = "JKS"; + // TLS TrustStore path in broker + private String tlsTrustStore = null; + // TLS TrustStore password in broker" + private String tlsTrustStorePassword = null; - public String getTlsTrustCertsFilePath() { - return tlsTrustCertsFilePath; - } - - public void setTlsTrustCertsFilePath(String tlsTrustCertsFilePath) { - this.tlsTrustCertsFilePath = tlsTrustCertsFilePath; - } - - public boolean isTlsAllowInsecureConnection() { - return tlsAllowInsecureConnection; - } - - public void setTlsAllowInsecureConnection(boolean tlsAllowInsecureConnection) { - this.tlsAllowInsecureConnection = tlsAllowInsecureConnection; - } - - public boolean isBindOnLocalhost() { - return bindOnLocalhost; - } - - public void setBindOnLocalhost(boolean bindOnLocalhost) { - this.bindOnLocalhost = bindOnLocalhost; - } - - public boolean isAuthenticationEnabled() { - return authenticationEnabled; - } - - public void setAuthenticationEnabled(boolean authenticationEnabled) { - this.authenticationEnabled = authenticationEnabled; - } - - public Set getAuthenticationProviders() { - return authenticationProviders; - } - - public void setAuthenticationProviders(Set authenticationProviders) { - this.authenticationProviders = authenticationProviders; - } - - public boolean isAuthorizationEnabled() { - return authorizationEnabled; - } - - public void setAuthorizationEnabled(boolean authorizationEnabled) { - this.authorizationEnabled = authorizationEnabled; - } - - public String getAuthorizationProvider() { - return authorizationProvider; - } - - public void setAuthorizationProvider(String authorizationProvider) { - this.authorizationProvider = authorizationProvider; - } - - public Set getSuperUserRoles() { - return superUserRoles; - } - - public void setSuperUserRoles(Set superUserRoles) { - this.superUserRoles = superUserRoles; - } - - public boolean getAuthorizationAllowWildcardsMatching() { - return authorizationAllowWildcardsMatching; - } - - public void setAuthorizationAllowWildcardsMatching(boolean authorizationAllowWildcardsMatching) { - this.authorizationAllowWildcardsMatching = authorizationAllowWildcardsMatching; - } - - public Properties getProperties() { - return properties; - } - - public void setProperties(Properties properties) { - this.properties = properties; - } - - public Set getTlsProtocols() { - return tlsProtocols; - } - - public void setTlsProtocols(Set tlsProtocols) { - this.tlsProtocols = tlsProtocols; - } - - public long getTlsCertRefreshCheckDurationSec() { - return tlsCertRefreshCheckDurationSec; - } - - public void setTlsCertRefreshCheckDurationSec(long tlsCertRefreshCheckDurationSec) { - this.tlsCertRefreshCheckDurationSec = tlsCertRefreshCheckDurationSec; - } - - public Set getTlsCiphers() { - return tlsCiphers; - } - - public void setTlsCiphers(Set tlsCiphers) { - this.tlsCiphers = tlsCiphers; - } - - public boolean getTlsRequireTrustedClientCertOnConnect() { - return tlsRequireTrustedClientCertOnConnect; - } - - public void setTlsRequireTrustedClientCertOnConnect(boolean tlsRequireTrustedClientCertOnConnect) { - this.tlsRequireTrustedClientCertOnConnect = tlsRequireTrustedClientCertOnConnect; - } + private Properties properties = new Properties(); } diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java index 989b726747470..d1b5c18987aba 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java @@ -33,7 +33,6 @@ import io.netty.channel.ChannelOption; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; @@ -76,12 +75,12 @@ public class DirectProxyHandler { public static final String TLS_HANDLER = "tls"; private final Authentication authentication; - private final SslContext sslCtx; + private final SslHandler sslHandler; private AuthenticationDataProvider authenticationDataProvider; private ProxyService service; public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, String targetBrokerUrl, - int protocolVersion, SslContext sslCtx) { + int protocolVersion, SslHandler sslHandler) { this.service = service; this.authentication = proxyConnection.getClientAuthentication(); this.inboundChannel = proxyConnection.ctx().channel(); @@ -90,7 +89,7 @@ public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, this.clientAuthData = proxyConnection.clientAuthData; this.clientAuthMethod = proxyConnection.clientAuthMethod; this.protocolVersion = protocolVersion; - this.sslCtx = sslCtx; + this.sslHandler = sslHandler; ProxyConfiguration config = service.getConfiguration(); // Start the connection attempt. @@ -103,8 +102,8 @@ public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, b.handler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) throws Exception { - if (sslCtx != null) { - ch.pipeline().addLast(TLS_HANDLER, sslCtx.newHandler(ch.alloc())); + if (sslHandler != null) { + ch.pipeline().addLast(TLS_HANDLER, sslHandler); } ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder( Commands.DEFAULT_MAX_MESSAGE_SIZE + Commands.MESSAGE_SIZE_FRAME_PADDING, 0, 4, 0, 4)); diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java index e2f4644810cd4..af624486e04d9 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java @@ -60,6 +60,8 @@ public class ProxyConfiguration implements PulsarConfiguration { @Category private static final String CATEGORY_TLS = "TLS"; @Category + private static final String CATEGORY_KEYSTORE_TLS = "KeyStoreTLS"; + @Category private static final String CATEGORY_TOKEN_AUTH = "Token Authentication Provider"; @Category private static final String CATEGORY_HTTP = "HTTP"; @@ -319,7 +321,7 @@ public class ProxyConfiguration implements PulsarConfiguration { private Set tlsProtocols = Sets.newTreeSet(); @FieldContext( category = CATEGORY_TLS, - doc = "Specify the tls cipher the broker will use to negotiate during TLS Handshake" + doc = "Specify the tls cipher the proxy will use to negotiate during TLS Handshake" + " (a comma-separated list of ciphers).\n\n" + "Examples:- [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]" ) @@ -331,6 +333,118 @@ public class ProxyConfiguration implements PulsarConfiguration { ) private boolean tlsRequireTrustedClientCertOnConnect = false; + /**** --- KeyStore TLS config variables --- ****/ + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Enable TLS with KeyStore type configuration for proxy" + ) + private boolean tlsEnabledWithKeyStore = false; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS Provider (JDK or OpenSSL)" + ) + private String tlsProvider = "JDK"; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore type configuration for proxy: JKS, PKCS12" + ) + private String tlsKeyStoreType = "JKS"; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore path for proxy" + ) + private String tlsKeyStore = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore password for proxy" + ) + private String tlsKeyStorePassword = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore type configuration for proxy: JKS, PKCS12" + ) + private String tlsTrustStoreType = "JKS"; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore path for proxy" + ) + private String tlsTrustStore = null; + + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore password for proxy" + ) + private String tlsTrustStorePassword = null; + + /**** --- KeyStore TLS config variables used for proxy to auth with broker--- ****/ + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "The TLS Provider (JDK or OpenSSL) used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientSslProvider = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore type configuration for proxy: JKS, PKCS12," + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsKeyStoreType = "JKS"; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore file path configuration for proxy," + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsKeyStore = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS KeyStore password configuration for proxy," + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsKeyStorePassword = null; + // needed when client auth is required + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore type configuration for proxy: JKS, PKCS12 " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStoreType = "JKS"; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore path for proxy, " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStore = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore password for proxy, " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStorePassword = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Specify the tls cipher the proxy will use to negotiate during TLS Handshake" + + " (a comma-separated list of ciphers).\n\n" + + "Examples:- [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256].\n" + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private Set brokerClientTlsCiphers = Sets.newTreeSet(); + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Specify the tls protocols the broker will use to negotiate during TLS handshake" + + " (a comma-separated list of protocol names).\n\n" + + "Examples:- [TLSv1.2, TLSv1.1, TLSv1] \n" + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private Set brokerClientTlsProtocols = Sets.newTreeSet(); + + /***** --- HTTP --- ****/ + @FieldContext( category = CATEGORY_HTTP, doc = "Http directs to redirect to non-pulsar services" diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java index 19fd77169560d..6e719f657470f 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java @@ -55,7 +55,6 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; @@ -72,7 +71,7 @@ public class ProxyConnection extends PulsarHandler implements FutureListener public static final String TLS_HANDLER = "tls"; private final ProxyService proxyService; - private final NettySslContextBuilder serverSslCtxRefresher; - private final ClientSslContextRefresher clientSslCtxRefresher; private final boolean enableTls; + private final boolean tlsEnabledWithKeyStore; + + private SslContextAutoRefreshBuilder serverSslCtxRefresher; + private SslContextAutoRefreshBuilder clientSslCtxRefresher; + private NettySSLEngineAutoRefreshBuilder serverSSLEngineRefreshBuilder; + private NettySSLEngineAutoRefreshBuilder clientSSLEngineRefreshBuilder; public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration serviceConfig, boolean enableTls) throws Exception { super(); this.proxyService = proxyService; this.enableTls = enableTls; + this.tlsEnabledWithKeyStore = serviceConfig.isTlsEnabledWithKeyStore(); if (enableTls) { - serverSslCtxRefresher = new NettySslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), - serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), - serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), - serviceConfig.isTlsRequireTrustedClientCertOnConnect(), - serviceConfig.getTlsCertRefreshCheckDurationSec()); + if (tlsEnabledWithKeyStore) { + serverSSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + serviceConfig.getTlsProvider(), + serviceConfig.getTlsKeyStoreType(), + serviceConfig.getTlsKeyStore(), + serviceConfig.getTlsKeyStorePassword(), + serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustStoreType(), + serviceConfig.getTlsTrustStore(), + serviceConfig.getTlsTrustStorePassword(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCiphers(), + serviceConfig.getTlsProtocols(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } else { + serverSslCtxRefresher = new NettyServerSslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), + serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), + serviceConfig.isTlsRequireTrustedClientCertOnConnect(), + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } } else { this.serverSslCtxRefresher = null; } @@ -67,9 +91,24 @@ public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration s serviceConfig.getBrokerClientAuthenticationParameters()).getAuthData(); } - clientSslCtxRefresher = new ClientSslContextRefresher(serviceConfig.isTlsAllowInsecureConnection(), - serviceConfig.getBrokerClientTrustCertsFilePath(), authData); - + if (tlsEnabledWithKeyStore) { + clientSSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + serviceConfig.getBrokerClientSslProvider(), + serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getBrokerClientTlsTrustStoreType(), + serviceConfig.getBrokerClientTlsTrustStore(), + serviceConfig.getBrokerClientTlsTrustStorePassword(), + serviceConfig.getBrokerClientTlsCiphers(), + serviceConfig.getBrokerClientTlsProtocols(), + serviceConfig.getTlsCertRefreshCheckDurationSec(), + authData); + } else { + clientSslCtxRefresher = new NettyClientSslContextRefresher( + serviceConfig.isTlsAllowInsecureConnection(), + serviceConfig.getBrokerClientTrustCertsFilePath(), + authData, + serviceConfig.getTlsCertRefreshCheckDurationSec()); + } } else { this.clientSslCtxRefresher = null; } @@ -78,15 +117,26 @@ public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration s @Override protected void initChannel(SocketChannel ch) throws Exception { if (serverSslCtxRefresher != null && this.enableTls) { - SslContext sslContext = serverSslCtxRefresher.get(); - if (sslContext != null) { - ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); + if (this.tlsEnabledWithKeyStore) { + ch.pipeline().addLast(TLS_HANDLER, new SslHandler(serverSSLEngineRefreshBuilder.get())); + } else { + SslContext sslContext = serverSslCtxRefresher.get(); + if (sslContext != null) { + ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); + } } } ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder( Commands.DEFAULT_MAX_MESSAGE_SIZE + Commands.MESSAGE_SIZE_FRAME_PADDING, 0, 4, 0, 4)); - ch.pipeline().addLast("handler", - new ProxyConnection(proxyService, clientSslCtxRefresher == null ? null : clientSslCtxRefresher.get())); + + if (clientSSLEngineRefreshBuilder != null && tlsEnabledWithKeyStore) { + ch.pipeline().addLast("handler", + new ProxyConnection(proxyService, new SslHandler(clientSSLEngineRefreshBuilder.get()))); + } else { + ch.pipeline().addLast("handler", + new ProxyConnection(proxyService, + clientSslCtxRefresher == null ? null : clientSslCtxRefresher.get().newHandler(ch.alloc()))); + } } } diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java index 0b8dca2be9bc6..3a4539990df2b 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/WebServer.java @@ -19,9 +19,7 @@ package org.apache.pulsar.proxy.server; import com.google.common.collect.Lists; - import io.prometheus.client.jetty.JettyStatisticsCollector; - import java.io.IOException; import java.net.URI; import java.util.ArrayList; @@ -31,15 +29,14 @@ import java.util.List; import java.util.Optional; import java.util.TimeZone; - import javax.servlet.DispatcherType; - import org.apache.commons.lang3.tuple.Pair; import org.apache.pulsar.broker.authentication.AuthenticationService; import org.apache.pulsar.broker.web.AuthenticationFilter; import org.apache.pulsar.broker.web.JsonMapperProvider; import org.apache.pulsar.broker.web.WebExecutorThreadPool; import org.apache.pulsar.common.util.SecurityUtility; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; @@ -99,14 +96,30 @@ public WebServer(ProxyConfiguration config, AuthenticationService authentication } if (config.getWebServicePortTls().isPresent()) { try { - SslContextFactory sslCtxFactory = SecurityUtility.createSslContextFactory( - config.isTlsAllowInsecureConnection(), - config.getTlsTrustCertsFilePath(), - config.getTlsCertificateFilePath(), - config.getTlsKeyFilePath(), - config.isTlsRequireTrustedClientCertOnConnect(), - true, - config.getTlsCertRefreshCheckDurationSec()); + SslContextFactory sslCtxFactory; + if (config.isTlsEnabledWithKeyStore()) { + sslCtxFactory = KeyStoreSSLContext.createSslContextFactory( + config.getTlsProvider(), + config.getTlsKeyStoreType(), + config.getTlsKeyStore(), + config.getTlsKeyStorePassword(), + config.isTlsAllowInsecureConnection(), + config.getTlsTrustStoreType(), + config.getTlsTrustStore(), + config.getTlsTrustStorePassword(), + config.isTlsRequireTrustedClientCertOnConnect(), + config.getTlsCertRefreshCheckDurationSec() + ); + } else { + sslCtxFactory = SecurityUtility.createSslContextFactory( + config.isTlsAllowInsecureConnection(), + config.getTlsTrustCertsFilePath(), + config.getTlsCertificateFilePath(), + config.getTlsKeyFilePath(), + config.isTlsRequireTrustedClientCertOnConnect(), + true, + config.getTlsCertRefreshCheckDurationSec()); + } connectorTls = new ServerConnector(server, 1, 1, sslCtxFactory); connectorTls.setPort(config.getWebServicePortTls().get()); connectors.add(connectorTls); From 273095cedce72483f0a0bd5e9146997c2cae7329 Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Thu, 30 Apr 2020 23:34:43 +0800 Subject: [PATCH 02/11] change log level back --- buildtools/src/main/resources/log4j2.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildtools/src/main/resources/log4j2.xml b/buildtools/src/main/resources/log4j2.xml index 4da6f96997931..2fdc2d05ae32e 100644 --- a/buildtools/src/main/resources/log4j2.xml +++ b/buildtools/src/main/resources/log4j2.xml @@ -30,7 +30,7 @@ - + From 1e24abe562cd592d8fe3290ab607182f8c84127f Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Fri, 1 May 2020 08:27:50 +0800 Subject: [PATCH 03/11] fix compile issue --- pulsar-client-auth-keystoretls/pom.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pulsar-client-auth-keystoretls/pom.xml b/pulsar-client-auth-keystoretls/pom.xml index fb6f010fd3eb3..d159062e42df2 100644 --- a/pulsar-client-auth-keystoretls/pom.xml +++ b/pulsar-client-auth-keystoretls/pom.xml @@ -84,14 +84,6 @@ test - - ${project.groupId} - pulsar-proxy - ${project.version} - test-jar - test - - ${project.groupId} pulsar-proxy From db69ec68d5076f7392ec9fb816d9b851773e20ad Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Sat, 2 May 2020 00:42:55 +0800 Subject: [PATCH 04/11] add more tests --- .../pulsar/broker/ServiceConfiguration.java | 2 +- .../service/PulsarChannelInitializer.java | 12 +- .../client/api/TlsProducerConsumerBase.java | 10 - .../client/api/TlsProducerConsumerTest.java | 6 +- .../apache/pulsar/client/KeyStoreTlsTest.java | 76 ++++++ .../client/ProxyTlsTestWithoutAuth.java | 185 +++++++++++++ .../client/TlsProducerConsumerTest.java | 135 ---------- ...a => TlsProducerConsumerTestWithAuth.java} | 120 ++++++++- .../TlsProducerConsumerTestWithoutAuth.java | 251 ++++++++++++++++++ .../client/impl/PulsarChannelInitializer.java | 9 +- .../util/keystoretls/KeyStoreSSLContext.java | 87 +++++- ...=> NettySSLContextAutoRefreshBuilder.java} | 63 +++-- .../service/ServiceChannelInitializer.java | 10 +- .../service/server/ServiceConfig.java | 4 +- .../proxy/server/DirectProxyHandler.java | 11 +- .../proxy/server/ProxyConfiguration.java | 11 +- .../pulsar/proxy/server/ProxyConnection.java | 20 +- .../server/ServiceChannelInitializer.java | 52 ++-- 18 files changed, 825 insertions(+), 239 deletions(-) create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java delete mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/{TlsProducerConsumerBase.java => TlsProducerConsumerTestWithAuth.java} (56%) create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java rename pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/{NettySSLEngineAutoRefreshBuilder.java => NettySSLContextAutoRefreshBuilder.java} (64%) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java index f46edd8527150..49b83c76b0003 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java @@ -1587,7 +1587,7 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS Provider (JDK or OpenSSL)" + doc = "TLS Provider" ) private String tlsProvider = null; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java index 3e1f533956589..2a2d3d5c35c09 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/PulsarChannelInitializer.java @@ -37,7 +37,7 @@ import org.apache.pulsar.common.protocol.Commands; import org.apache.pulsar.common.util.NettyServerSslContextBuilder; import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; -import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; +import org.apache.pulsar.common.util.keystoretls.NettySSLContextAutoRefreshBuilder; @Slf4j public class PulsarChannelInitializer extends ChannelInitializer { @@ -49,7 +49,7 @@ public class PulsarChannelInitializer extends ChannelInitializer private final boolean tlsEnabledWithKeyStore; private SslContextAutoRefreshBuilder sslCtxRefresher; private final ServiceConfiguration brokerConf; - private NettySSLEngineAutoRefreshBuilder nettySSLEngineRefreshBuilder; + private NettySSLContextAutoRefreshBuilder nettySSLContextAutoRefreshBuilder; // This cache is used to maintain a list of active connections to iterate over them // We keep weak references to have the cache to be auto cleaned up when the connections @@ -73,7 +73,7 @@ public PulsarChannelInitializer(PulsarService pulsar, boolean enableTLS) throws this.tlsEnabledWithKeyStore = serviceConfig.isTlsEnabledWithKeyStore(); if (this.enableTls) { if (tlsEnabledWithKeyStore) { - nettySSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + nettySSLContextAutoRefreshBuilder = new NettySSLContextAutoRefreshBuilder( serviceConfig.getTlsProvider(), serviceConfig.getTlsKeyStoreType(), serviceConfig.getTlsKeyStore(), @@ -89,7 +89,8 @@ public PulsarChannelInitializer(PulsarService pulsar, boolean enableTLS) throws } else { sslCtxRefresher = new NettyServerSslContextBuilder(serviceConfig.isTlsAllowInsecureConnection(), serviceConfig.getTlsTrustCertsFilePath(), serviceConfig.getTlsCertificateFilePath(), - serviceConfig.getTlsKeyFilePath(), serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), + serviceConfig.getTlsKeyFilePath(), + serviceConfig.getTlsCiphers(), serviceConfig.getTlsProtocols(), serviceConfig.isTlsRequireTrustedClientCertOnConnect(), serviceConfig.getTlsCertRefreshCheckDurationSec()); } @@ -107,7 +108,8 @@ public PulsarChannelInitializer(PulsarService pulsar, boolean enableTLS) throws protected void initChannel(SocketChannel ch) throws Exception { if (this.enableTls) { if (this.tlsEnabledWithKeyStore) { - ch.pipeline().addLast(TLS_HANDLER, new SslHandler(nettySSLEngineRefreshBuilder.get())); + ch.pipeline().addLast(TLS_HANDLER, + new SslHandler(nettySSLContextAutoRefreshBuilder.get().createSSLEngine())); } else { ch.pipeline().addLast(TLS_HANDLER, sslCtxRefresher.get().newHandler(ch.alloc())); } diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java index ba25be4761ddd..b49be05f61aa6 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerBase.java @@ -21,13 +21,11 @@ import static org.mockito.Mockito.spy; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; -import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.impl.auth.AuthenticationTls; import org.apache.pulsar.common.policies.data.ClusterData; @@ -72,14 +70,6 @@ protected void internalSetUpForBroker() throws Exception { Set tlsProtocols = Sets.newConcurrentHashSet(); tlsProtocols.add("TLSv1.2"); conf.setTlsProtocols(tlsProtocols); - - - conf.setSuperUserRoles(Sets.newHashSet("a-super-user")); - conf.setAuthenticationEnabled(true); - conf.setAuthorizationEnabled(true); - Set providers = new HashSet<>(); - providers.add(AuthenticationProviderTls.class.getName()); - conf.setAuthenticationProviders(providers); } protected void internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerTest.java index 55bc4a7bfe520..9f1eac8660aff 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/TlsProducerConsumerTest.java @@ -182,14 +182,14 @@ public void testTlsCertsFromDynamicStream() throws Exception { /** * It verifies that AuthenticationTls provides cert refresh functionality. - * + * *
      *  a. Create Auth with invalid cert
      *  b. Consumer fails with invalid tls certs
      *  c. refresh cert in provider
      *  d. Consumer successfully gets created
      * 
- * + * * @throws Exception */ @Test @@ -234,5 +234,5 @@ private ByteArrayInputStream createByteInputStream(String filePath) throws IOExc private ByteArrayInputStream getStream(AtomicInteger index, ByteArrayInputStream... streams) { return streams[index.intValue()]; - } + } } diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java new file mode 100644 index 0000000000000..60bc8c4128e02 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java @@ -0,0 +1,76 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static org.apache.pulsar.common.util.SecurityUtility.getProvider; + +import java.security.Provider; +import javax.net.ssl.SSLContext; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; +import org.apache.pulsar.common.util.keystoretls.SSLContextValidatorEngine; +import org.testng.annotations.Test; + +public class KeyStoreTlsTest { + + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + protected final String KEYSTORE_TYPE = "JKS"; + + public static final Provider BC_PROVIDER = getProvider(); + + @Test(timeOut = 300000) + public void testValidate() throws Exception { + KeyStoreSSLContext serverSSLContext = new KeyStoreSSLContext(KeyStoreSSLContext.Mode.SERVER, + null, + KEYSTORE_TYPE, + BROKER_KEYSTORE_FILE_PATH, + BROKER_KEYSTORE_PW, + false, + KEYSTORE_TYPE, + BROKER_TRUSTSTORE_FILE_PATH, + BROKER_TRUSTSTORE_PW, + true, + null, + null); + SSLContext serverCnx = serverSSLContext.createSSLContext(); + + KeyStoreSSLContext clientSSLContext = new KeyStoreSSLContext(KeyStoreSSLContext.Mode.CLIENT, + null, + KEYSTORE_TYPE, + CLIENT_KEYSTORE_FILE_PATH, + CLIENT_KEYSTORE_PW, + false, + KEYSTORE_TYPE, + CLIENT_TRUSTSTORE_FILE_PATH, + CLIENT_TRUSTSTORE_PW, + false, + null, + null); + SSLContext clientCnx = clientSSLContext.createSSLContext(); + + SSLContextValidatorEngine.validate(clientCnx, serverCnx); + } +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java new file mode 100644 index 0000000000000..1606412c8ed35 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java @@ -0,0 +1,185 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.mockito.Mockito.doReturn; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import lombok.Cleanup; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.auth.MockedPulsarServiceBaseTest; +import org.apache.pulsar.broker.authentication.AuthenticationService; +import org.apache.pulsar.client.api.ClientBuilder; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.MessageRoutingMode; +import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.Schema; +import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; +import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.proxy.server.ProxyConfiguration; +import org.apache.pulsar.proxy.server.ProxyService; +import org.mockito.Mockito; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Slf4j +public class ProxyTlsTestWithoutAuth extends MockedPulsarServiceBaseTest { + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + + protected final String KEYSTORE_TYPE = "JKS"; + + private final String DUMMY_VALUE = "DUMMY_VALUE"; + + private ProxyService proxyService; + private ProxyConfiguration proxyConfig = new ProxyConfiguration(); + + @Override + @BeforeMethod + protected void setup() throws Exception { + internalSetup(); + + proxyConfig.setServicePort(Optional.of(0)); + proxyConfig.setServicePortTls(Optional.of(0)); + proxyConfig.setWebServicePort(Optional.of(0)); + proxyConfig.setWebServicePortTls(Optional.of(0)); + proxyConfig.setTlsEnabledWithBroker(false); + + proxyConfig.setTlsEnabledWithKeyStore(true); + proxyConfig.setTlsKeyStoreType(KEYSTORE_TYPE); + proxyConfig.setTlsKeyStore(BROKER_KEYSTORE_FILE_PATH); + proxyConfig.setTlsKeyStorePassword(BROKER_KEYSTORE_PW); + proxyConfig.setTlsTrustStoreType(KEYSTORE_TYPE); + proxyConfig.setTlsTrustStore(CLIENT_TRUSTSTORE_FILE_PATH); + proxyConfig.setTlsTrustStorePassword(CLIENT_TRUSTSTORE_PW); + proxyConfig.setTlsRequireTrustedClientCertOnConnect(true); + + proxyConfig.setZookeeperServers(DUMMY_VALUE); + proxyConfig.setConfigurationStoreServers(DUMMY_VALUE); + + proxyService = Mockito.spy(new ProxyService(proxyConfig, new AuthenticationService( + PulsarConfigurationLoader.convertFrom(proxyConfig)))); + doReturn(mockZooKeeperClientFactory).when(proxyService).getZooKeeperClientFactory(); + + proxyService.start(); + } + + protected PulsarClient internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { + ClientBuilder clientBuilder = PulsarClient.builder() + .serviceUrl(lookupUrl) + .enableTls(true) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(false) + .operationTimeout(1000, TimeUnit.MILLISECONDS); + if (addCertificates) { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_TYPE, KEYSTORE_TYPE); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + clientBuilder.authentication(AuthenticationKeyStoreTls.class.getName(), authParams); + } + return clientBuilder.build(); + } + + @Override + @AfterMethod + protected void cleanup() throws Exception { + internalCleanup(); + proxyService.close(); + } + + @Test + public void testProducer() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(true, proxyService.getServiceUrlTls()); + @Cleanup + Producer producer = client.newProducer(Schema.BYTES) + .topic("persistent://sample/test/local/topic" + System.currentTimeMillis()) + .create(); + + for (int i = 0; i < 10; i++) { + producer.send("test".getBytes()); + } + } + + @Test + public void testProducerFailed() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(false, proxyService.getServiceUrlTls()); + try { + @Cleanup + Producer producer = client.newProducer(Schema.BYTES) + .topic("persistent://sample/test/local/topic" + System.currentTimeMillis()) + .create(); + Assert.fail("Should failed since broker setTlsRequireTrustedClientCertOnConnect, " + + "while client not set keystore"); + } catch (Exception e) { + // expected + log.info("Expected failed since broker setTlsRequireTrustedClientCertOnConnect," + + " while client not set keystore"); + } + } + + @Test + public void testPartitions() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(true, proxyService.getServiceUrlTls()); + String topicName = "persistent://sample/test/local/partitioned-topic" + System.currentTimeMillis(); + TenantInfo tenantInfo = createDefaultTenantInfo(); + admin.tenants().createTenant("sample", tenantInfo); + admin.topics().createPartitionedTopic(topicName, 2); + + @Cleanup + Producer producer = client.newProducer(Schema.BYTES).topic(topicName) + .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create(); + + // Create a consumer directly attached to broker + @Cleanup + Consumer consumer = pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-sub").subscribe(); + + for (int i = 0; i < 10; i++) { + producer.send("test".getBytes()); + } + + for (int i = 0; i < 10; i++) { + Message msg = consumer.receive(1, TimeUnit.SECONDS); + checkNotNull(msg); + } + } + +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java deleted file mode 100644 index d61ef64d0e17c..0000000000000 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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. - */ -package org.apache.pulsar.client; - -import java.util.Arrays; -import java.util.concurrent.TimeUnit; -import org.apache.pulsar.client.api.Consumer; -import org.apache.pulsar.client.api.Message; -import org.apache.pulsar.client.api.Producer; -import org.apache.pulsar.client.api.SubscriptionType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.Test; - -// TLS authentication and authorization based on KeyStore type config. -public class TlsProducerConsumerTest extends TlsProducerConsumerBase { - private static final Logger log = LoggerFactory.getLogger(TlsProducerConsumerTest.class); - - /** - * verifies that messages whose size is larger than 2^14 bytes (max size of single TLS chunk) can be - * produced/consumed - * - * @throws Exception - */ - @Test(timeOut = 30000) - public void testTlsLargeSizeMessage() throws Exception { - log.info("-- Starting {} test --", methodName); - - final int MESSAGE_SIZE = 16 * 1024 + 1; - log.info("-- message size --", MESSAGE_SIZE); - - internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); - internalSetUpForNamespace(); - - Consumer consumer = pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") - .subscriptionName("my-subscriber-name").subscribe(); - - Producer producer = pulsarClient.newProducer().topic("persistent://my-property/use/my-ns/my-topic1") - .create(); - for (int i = 0; i < 10; i++) { - byte[] message = new byte[MESSAGE_SIZE]; - Arrays.fill(message, (byte) i); - producer.send(message); - } - - Message msg = null; - for (int i = 0; i < 10; i++) { - msg = consumer.receive(5, TimeUnit.SECONDS); - byte[] expected = new byte[MESSAGE_SIZE]; - Arrays.fill(expected, (byte) i); - Assert.assertEquals(expected, msg.getData()); - } - // Acknowledge the consumption of all messages at once - consumer.acknowledgeCumulative(msg); - consumer.close(); - log.info("-- Exiting {} test --", methodName); - } - - @Test(timeOut = 300000) - public void testTlsClientAuthOverBinaryProtocol() throws Exception { - log.info("-- Starting {} test --", methodName); - - final int MESSAGE_SIZE = 16 * 1024 + 1; - log.info("-- message size --", MESSAGE_SIZE); - - internalSetUpForNamespace(); - - // Test 1 - Using TLS on binary protocol without sending certs - expect failure - internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); - try { - pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") - .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); - Assert.fail("Server should have failed the TLS handshake since client didn't ."); - } catch (Exception ex) { - // OK - } - - // Test 2 - Using TLS on binary protocol - sending certs - internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); - - try { - pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") - .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); - } catch (Exception ex) { - Assert.fail("Should not fail since certs are sent."); - } - } - - @Test(timeOut = 30000) - public void testTlsClientAuthOverHTTPProtocol() throws Exception { - log.info("-- Starting {} test --", methodName); - - final int MESSAGE_SIZE = 16 * 1024 + 1; - log.info("-- message size --", MESSAGE_SIZE); - internalSetUpForNamespace(); - - // Test 1 - Using TLS on https without sending certs - expect failure - internalSetUpForClient(false, pulsar.getWebServiceAddressTls()); - try { - pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") - .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); - Assert.fail("Server should have failed the TLS handshake since client didn't ."); - } catch (Exception ex) { - // OK - } - - // Test 2 - Using TLS on https - sending certs - internalSetUpForClient(true, pulsar.getWebServiceAddressTls()); - try { - pulsarClient.newConsumer().topic("persistent://my-property/use/my-ns/my-topic1") - .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); - } catch (Exception ex) { - Assert.fail("Should not fail since certs are sent."); - } - } - - -} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java similarity index 56% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java rename to pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java index 9535aca0890d0..b410fa6a20acf 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerBase.java +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java @@ -21,25 +21,34 @@ import static org.mockito.Mockito.spy; import com.google.common.collect.Sets; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; import org.apache.pulsar.client.admin.PulsarAdmin; import org.apache.pulsar.client.api.ClientBuilder; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Producer; import org.apache.pulsar.client.api.ProducerConsumerBase; import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.SubscriptionType; import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; import org.apache.pulsar.common.policies.data.ClusterData; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; -// Base class for TLS authentication and authorization based on KeyStore type config. -public class TlsProducerConsumerBase extends ProducerConsumerBase { +// TLS authentication and authorization based on KeyStore type config. +@Slf4j +public class TlsProducerConsumerTestWithAuth extends ProducerConsumerBase { protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; @@ -63,6 +72,7 @@ protected void setup() throws Exception { internalSetUpForBroker(); // Start Broker + super.init(); } @@ -147,4 +157,110 @@ protected void internalSetUpForNamespace() throws Exception { admin.namespaces().createNamespace("my-property/my-ns"); } + /** + * verifies that messages whose size is larger than 2^14 bytes (max size of single TLS chunk) can be + * produced/consumed + * + * @throws Exception + */ + @Test(timeOut = 30000) + public void testTlsLargeSizeMessage() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsLargeSizeMessage" + + System.currentTimeMillis(); + + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + internalSetUpForNamespace(); + + Consumer consumer = pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscribe(); + + Producer producer = pulsarClient.newProducer().topic(topicName) + .create(); + for (int i = 0; i < 10; i++) { + byte[] message = new byte[MESSAGE_SIZE]; + Arrays.fill(message, (byte) i); + producer.send(message); + } + + Message msg = null; + for (int i = 0; i < 10; i++) { + msg = consumer.receive(5, TimeUnit.SECONDS); + byte[] expected = new byte[MESSAGE_SIZE]; + Arrays.fill(expected, (byte) i); + Assert.assertEquals(expected, msg.getData()); + } + // Acknowledge the consumption of all messages at once + consumer.acknowledgeCumulative(msg); + consumer.close(); + log.info("-- Exiting {} test --", methodName); + } + + @Test(timeOut = 300000) + public void testTlsClientAuthOverBinaryProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsClientAuthOverBinaryProtocol" + + System.currentTimeMillis(); + + internalSetUpForNamespace(); + + // Test 1 - Using TLS on binary protocol without sending certs - expect failure + internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); + + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Using TLS on binary protocol - sending certs + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + + @Test(timeOut = 30000) + public void testTlsClientAuthOverHTTPProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsClientAuthOverHTTPProtocol" + + System.currentTimeMillis(); + + internalSetUpForNamespace(); + + // Test 1 - Using TLS on https without sending certs - expect failure + internalSetUpForClient(false, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Test 2 - Using TLS on https - sending certs + internalSetUpForClient(true, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + } diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java new file mode 100644 index 0000000000000..614c428eff6e9 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java @@ -0,0 +1,251 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static org.mockito.Mockito.spy; + +import com.google.common.collect.Sets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; +import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.api.ClientBuilder; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.api.ProducerConsumerBase; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.SubscriptionType; +import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; +import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +// TLS test without authentication and authorization based on KeyStore type config. +@Slf4j +public class TlsProducerConsumerTestWithoutAuth extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + + protected final String KEYSTORE_TYPE = "JKS"; + + private final String clusterName = "use"; + Set tlsProtocols = Sets.newConcurrentHashSet(); + + @BeforeMethod + @Override + protected void setup() throws Exception { + // TLS configuration for Broker + internalSetUpForBroker(); + + // Start Broker + super.init(); + } + + @AfterMethod + @Override + protected void cleanup() throws Exception { + super.internalCleanup(); + } + + protected void internalSetUpForBroker() throws Exception { + conf.setBrokerServicePortTls(Optional.of(0)); + conf.setWebServicePortTls(Optional.of(0)); + conf.setTlsEnabledWithKeyStore(true); + + conf.setTlsKeyStoreType(KEYSTORE_TYPE); + conf.setTlsKeyStore(BROKER_KEYSTORE_FILE_PATH); + conf.setTlsKeyStorePassword(BROKER_KEYSTORE_PW); + + conf.setTlsTrustStoreType(KEYSTORE_TYPE); + conf.setTlsTrustStore(CLIENT_TRUSTSTORE_FILE_PATH); + conf.setTlsTrustStorePassword(CLIENT_TRUSTSTORE_PW); + + conf.setClusterName(clusterName); + conf.setTlsRequireTrustedClientCertOnConnect(true); + tlsProtocols.add("TLSv1.2"); + conf.setTlsProtocols(tlsProtocols); + } + + protected void internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { + if (pulsarClient != null) { + pulsarClient.close(); + } + + ClientBuilder clientBuilder = PulsarClient.builder().serviceUrl(lookupUrl) + .enableTls(true) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(false) + .operationTimeout(1000, TimeUnit.MILLISECONDS); + if (addCertificates) { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_TYPE, KEYSTORE_TYPE); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + clientBuilder.authentication(AuthenticationKeyStoreTls.class.getName(), authParams); + } + pulsarClient = clientBuilder.build(); + } + + protected void internalSetUpForNamespace() throws Exception { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + + if (admin != null) { + admin.close(); + } + + admin = spy(PulsarAdmin.builder().serviceHttpUrl(brokerUrlTls.toString()) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(true) + .authentication(AuthenticationKeyStoreTls.class.getName(), authParams).build()); + admin.clusters().createCluster(clusterName, new ClusterData(brokerUrl.toString(), brokerUrlTls.toString(), + pulsar.getBrokerServiceUrl(), pulsar.getBrokerServiceUrlTls())); + admin.tenants().createTenant("my-property", + new TenantInfo(Sets.newHashSet("appid1", "appid2"), Sets.newHashSet("use"))); + admin.namespaces().createNamespace("my-property/my-ns"); + } + + /** + * verifies that messages whose size is larger than 2^14 bytes (max size of single TLS chunk) can be + * produced/consumed + * + * @throws Exception + */ + @Test(timeOut = 30000) + public void testTlsLargeSizeMessage() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsLargeSizeMessage" + + System.currentTimeMillis(); + + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + internalSetUpForNamespace(); + + Consumer consumer = pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscribe(); + + Producer producer = pulsarClient.newProducer().topic(topicName) + .create(); + for (int i = 0; i < 10; i++) { + byte[] message = new byte[MESSAGE_SIZE]; + Arrays.fill(message, (byte) i); + producer.send(message); + } + + Message msg = null; + for (int i = 0; i < 10; i++) { + msg = consumer.receive(5, TimeUnit.SECONDS); + byte[] expected = new byte[MESSAGE_SIZE]; + Arrays.fill(expected, (byte) i); + Assert.assertEquals(expected, msg.getData()); + } + // Acknowledge the consumption of all messages at once + consumer.acknowledgeCumulative(msg); + consumer.close(); + log.info("-- Exiting {} test --", methodName); + } + + @Test(timeOut = 300000) + public void testTlsClientAuthOverBinaryProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsClientAuthOverBinaryProtocol" + + System.currentTimeMillis(); + + internalSetUpForNamespace(); + + // Test 1 - Using TLS on binary protocol without sending certs - expect failure + internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); + + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Test 2 - Using TLS on binary protocol - sending certs + internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + + @Test(timeOut = 30000) + public void testTlsClientAuthOverHTTPProtocol() throws Exception { + log.info("-- Starting {} test --", methodName); + + final int MESSAGE_SIZE = 16 * 1024 + 1; + log.info("-- message size --", MESSAGE_SIZE); + String topicName = "persistent://my-property/use/my-ns/testTlsClientAuthOverHTTPProtocol" + + System.currentTimeMillis(); + + internalSetUpForNamespace(); + + // Test 1 - Using TLS on https without sending certs - expect failure + internalSetUpForClient(false, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } + + // Test 2 - Using TLS on https - sending certs + internalSetUpForClient(true, pulsar.getWebServiceAddressTls()); + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + } catch (Exception ex) { + Assert.fail("Should not fail since certs are sent."); + } + } + +} diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java index 2145dd403c571..ed888b31064ca 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java @@ -33,7 +33,7 @@ import org.apache.pulsar.common.protocol.ByteBufPair; import org.apache.pulsar.common.protocol.Commands; import org.apache.pulsar.common.util.SecurityUtility; -import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; +import org.apache.pulsar.common.util.keystoretls.NettySSLContextAutoRefreshBuilder; @Slf4j public class PulsarChannelInitializer extends ChannelInitializer { @@ -45,7 +45,7 @@ public class PulsarChannelInitializer extends ChannelInitializer private final boolean tlsEnabledWithKeyStore; private final Supplier sslContextSupplier; - private NettySSLEngineAutoRefreshBuilder nettySSLEngineAutoRefreshBuilder; + private NettySSLContextAutoRefreshBuilder nettySSLContextAutoRefreshBuilder; private static final long TLS_CERTIFICATE_CACHE_MILLIS = TimeUnit.MINUTES.toMillis(1); @@ -60,7 +60,7 @@ public PulsarChannelInitializer(ClientConfigurationData conf, Supplier ciphers; private Set protocols; + @Getter private SSLContext sslContext; private String protocol = DEFAULT_SSL_PROTOCOL; @@ -174,6 +177,22 @@ public SSLContext createSSLContext() throws GeneralSecurityException, IOExceptio return sslContext; } + public SSLEngine createSSLEngine() { + SSLEngine sslEngine = sslContext.createSSLEngine(); + + sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols()); + sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites()); + + if (this.mode == Mode.SERVER) { + sslEngine.setNeedClientAuth(this.needClientAuth); + sslEngine.setUseClientMode(false); + } else { + sslEngine.setUseClientMode(true); + } + + return sslEngine; + } + // for netty server public static SSLEngine createNettySSLEngineForServer(String sslProviderString, String keyStoreTypeString, @@ -210,6 +229,7 @@ public static SSLEngine createNettySSLEngineForServer(String sslProviderString, if (keyStoreSSLContext.mode == Mode.SERVER) { sslEngine.setNeedClientAuth(keyStoreSSLContext.needClientAuth); + } else { sslEngine.setWantClientAuth(keyStoreSSLContext.needClientAuth); } return sslEngine; @@ -251,7 +271,65 @@ public static SSLEngine createNettySSLEngineForClient(String sslProviderString, return sslEngine; } - // for web server + public static KeyStoreSSLContext createClientKeyStoreSslContext(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + Set ciphers, + Set protocols) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + false, + ciphers, + protocols); + + keyStoreSSLContext.createSSLContext(); + return keyStoreSSLContext; + } + + + public static KeyStoreSSLContext createServerKeyStoreSslContext(String sslProviderString, + String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + Set ciphers, + Set protocols) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.SERVER, + sslProviderString, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + allowInsecureConnection, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + requireTrustedClientCertOnConnect, + ciphers, + protocols); + + keyStoreSSLContext.createSSLContext(); + return keyStoreSSLContext; + } + + // for web server use case, no need ciphers and protocols public static SSLContext createServerSslContext(String sslProviderString, String keyStoreTypeString, String keyStorePath, @@ -262,9 +340,8 @@ public static SSLContext createServerSslContext(String sslProviderString, String trustStorePassword, boolean requireTrustedClientCertOnConnect) throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { - SslContextFactory ssl = new SslContextFactory(); - KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.SERVER, + return createServerKeyStoreSslContext( sslProviderString, keyStoreTypeString, keyStorePath, @@ -275,9 +352,7 @@ public static SSLContext createServerSslContext(String sslProviderString, trustStorePassword, requireTrustedClientCertOnConnect, null, - null); - - return keyStoreSSLContext.createSSLContext(); + null).getSslContext(); } // for web client diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLContextAutoRefreshBuilder.java similarity index 64% rename from pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java rename to pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLContextAutoRefreshBuilder.java index 6677fa950b9b4..363fe1e75c49a 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLEngineAutoRefreshBuilder.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/NettySSLContextAutoRefreshBuilder.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Set; -import javax.net.ssl.SSLEngine; import org.apache.pulsar.client.api.AuthenticationDataProvider; import org.apache.pulsar.client.api.KeyStoreParams; import org.apache.pulsar.common.util.FileModifiedTimeUpdater; @@ -30,8 +29,8 @@ /** * SSL context builder for Netty. */ -public class NettySSLEngineAutoRefreshBuilder extends SslContextAutoRefreshBuilder { - private volatile SSLEngine sslEngine; +public class NettySSLContextAutoRefreshBuilder extends SslContextAutoRefreshBuilder { + private volatile KeyStoreSSLContext keyStoreSSLContext; protected final boolean tlsAllowInsecureConnection; protected final Set tlsCiphers; @@ -52,18 +51,18 @@ public class NettySSLEngineAutoRefreshBuilder extends SslContextAutoRefreshBuild protected final boolean isServer; // for server - public NettySSLEngineAutoRefreshBuilder(String sslProviderString, - String keyStoreTypeString, - String keyStore, - String keyStorePassword, - boolean allowInsecureConnection, - String trustStoreTypeString, - String trustStore, - String trustStorePassword, - boolean requireTrustedClientCertOnConnect, - Set ciphers, - Set protocols, - long certRefreshInSec) { + public NettySSLContextAutoRefreshBuilder(String sslProviderString, + String keyStoreTypeString, + String keyStore, + String keyStorePassword, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + boolean requireTrustedClientCertOnConnect, + Set ciphers, + Set protocols, + long certRefreshInSec) { super(certRefreshInSec); this.tlsAllowInsecureConnection = allowInsecureConnection; @@ -85,15 +84,15 @@ public NettySSLEngineAutoRefreshBuilder(String sslProviderString, } // for client - public NettySSLEngineAutoRefreshBuilder(String sslProviderString, - boolean allowInsecureConnection, - String trustStoreTypeString, - String trustStore, - String trustStorePassword, - Set ciphers, - Set protocols, - long certRefreshInSec, - AuthenticationDataProvider authData) { + public NettySSLContextAutoRefreshBuilder(String sslProviderString, + boolean allowInsecureConnection, + String trustStoreTypeString, + String trustStore, + String trustStorePassword, + Set ciphers, + Set protocols, + long certRefreshInSec, + AuthenticationDataProvider authData) { super(certRefreshInSec); this.tlsAllowInsecureConnection = allowInsecureConnection; @@ -112,16 +111,16 @@ public NettySSLEngineAutoRefreshBuilder(String sslProviderString, } @Override - public synchronized SSLEngine update() throws GeneralSecurityException, IOException { + public synchronized KeyStoreSSLContext update() throws GeneralSecurityException, IOException { if (isServer) { - this.sslEngine = KeyStoreSSLContext.createNettySSLEngineForServer(tlsProvider, + this.keyStoreSSLContext = KeyStoreSSLContext.createServerKeyStoreSslContext(tlsProvider, tlsKeyStoreType, tlsKeyStore.getFileName(), tlsKeyStorePassword, tlsAllowInsecureConnection, tlsTrustStoreType, tlsTrustStore.getFileName(), tlsTrustStorePassword, tlsRequireTrustedClientCertOnConnect, tlsCiphers, tlsProtocols); } else { KeyStoreParams authParams = authData.getTlsKeyStoreParams(); - this.sslEngine = KeyStoreSSLContext.createNettySSLEngineForClient(tlsProvider, + this.keyStoreSSLContext = KeyStoreSSLContext.createClientKeyStoreSslContext(tlsProvider, authParams != null ? authParams.getKeyStoreType() : null, authParams != null ? authParams.getKeyStorePath() : null, authParams != null ? authParams.getKeyStorePassword() : null, @@ -129,17 +128,17 @@ public synchronized SSLEngine update() throws GeneralSecurityException, IOExcept tlsTrustStoreType, tlsTrustStore.getFileName(), tlsTrustStorePassword, tlsCiphers, tlsProtocols); } - return this.sslEngine; + return this.keyStoreSSLContext; } @Override - public SSLEngine getSslContext() { - return this.sslEngine; + public KeyStoreSSLContext getSslContext() { + return this.keyStoreSSLContext; } @Override public boolean needUpdate() { - return tlsKeyStore.checkAndRefresh() - || tlsTrustStore.checkAndRefresh(); + return (tlsKeyStore != null && tlsKeyStore.checkAndRefresh()) + || (tlsTrustStore != null && tlsTrustStore.checkAndRefresh()); } } diff --git a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/ServiceChannelInitializer.java b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/ServiceChannelInitializer.java index 509713b484ec4..250259b611238 100644 --- a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/ServiceChannelInitializer.java +++ b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/ServiceChannelInitializer.java @@ -22,7 +22,7 @@ import org.apache.pulsar.common.protocol.Commands; import org.apache.pulsar.common.util.NettyServerSslContextBuilder; import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; -import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; +import org.apache.pulsar.common.util.keystoretls.NettySSLContextAutoRefreshBuilder; import org.apache.pulsar.discovery.service.server.ServiceConfig; import io.netty.channel.ChannelInitializer; @@ -41,8 +41,7 @@ public class ServiceChannelInitializer extends ChannelInitializer private final boolean enableTls; private final boolean tlsEnabledWithKeyStore; private SslContextAutoRefreshBuilder sslCtxRefresher; - private NettySSLEngineAutoRefreshBuilder nettySSLEngineRefreshBuilder; - + private NettySSLContextAutoRefreshBuilder nettySSLContextAutoRefreshBuilder; public ServiceChannelInitializer(DiscoveryService discoveryService, ServiceConfig serviceConfig, boolean e) throws Exception { @@ -52,7 +51,7 @@ public ServiceChannelInitializer(DiscoveryService discoveryService, ServiceConfi this.tlsEnabledWithKeyStore = serviceConfig.isTlsEnabledWithKeyStore(); if (this.enableTls) { if (tlsEnabledWithKeyStore) { - nettySSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + nettySSLContextAutoRefreshBuilder = new NettySSLContextAutoRefreshBuilder( serviceConfig.getTlsProvider(), serviceConfig.getTlsKeyStoreType(), serviceConfig.getTlsKeyStore(), @@ -81,7 +80,8 @@ public ServiceChannelInitializer(DiscoveryService discoveryService, ServiceConfi protected void initChannel(SocketChannel ch) throws Exception { if (sslCtxRefresher != null && this.enableTls) { if (this.tlsEnabledWithKeyStore) { - ch.pipeline().addLast(TLS_HANDLER, new SslHandler(nettySSLEngineRefreshBuilder.get())); + ch.pipeline().addLast(TLS_HANDLER, + new SslHandler(nettySSLContextAutoRefreshBuilder.get().createSSLEngine())); } else{ SslContext sslContext = sslCtxRefresher.get(); if (sslContext != null) { diff --git a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java index a62d7556e7e13..ea1c68f4026c4 100644 --- a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java +++ b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java @@ -106,8 +106,8 @@ public class ServiceConfig implements PulsarConfiguration { /***** --- TLS with KeyStore--- ****/ // Enable TLS with KeyStore type configuration in broker private boolean tlsEnabledWithKeyStore = false; - // TLS Provider (JDK or OpenSSL) - private String tlsProvider = "JDK"; + // TLS Provider + private String tlsProvider = null; // TLS KeyStore type configuration in broker: JKS, PKCS12 private String tlsKeyStoreType = "JKS"; // TLS KeyStore path in broker diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java index d1b5c18987aba..e786ba8b69208 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/DirectProxyHandler.java @@ -36,6 +36,7 @@ import io.netty.handler.ssl.SslHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; +import java.util.function.Supplier; import lombok.Getter; import java.net.URI; @@ -75,12 +76,12 @@ public class DirectProxyHandler { public static final String TLS_HANDLER = "tls"; private final Authentication authentication; - private final SslHandler sslHandler; + private final Supplier sslHandlerSupplier; private AuthenticationDataProvider authenticationDataProvider; private ProxyService service; public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, String targetBrokerUrl, - int protocolVersion, SslHandler sslHandler) { + int protocolVersion, Supplier sslHandlerSupplier) { this.service = service; this.authentication = proxyConnection.getClientAuthentication(); this.inboundChannel = proxyConnection.ctx().channel(); @@ -89,7 +90,7 @@ public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, this.clientAuthData = proxyConnection.clientAuthData; this.clientAuthMethod = proxyConnection.clientAuthMethod; this.protocolVersion = protocolVersion; - this.sslHandler = sslHandler; + this.sslHandlerSupplier = sslHandlerSupplier; ProxyConfiguration config = service.getConfiguration(); // Start the connection attempt. @@ -102,8 +103,8 @@ public DirectProxyHandler(ProxyService service, ProxyConnection proxyConnection, b.handler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) throws Exception { - if (sslHandler != null) { - ch.pipeline().addLast(TLS_HANDLER, sslHandler); + if (sslHandlerSupplier != null) { + ch.pipeline().addLast(TLS_HANDLER, sslHandlerSupplier.get()); } ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder( Commands.DEFAULT_MAX_MESSAGE_SIZE + Commands.MESSAGE_SIZE_FRAME_PADDING, 0, 4, 0, 4)); diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java index af624486e04d9..8aaecb0cc4ab4 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java @@ -343,9 +343,9 @@ public class ProxyConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS Provider (JDK or OpenSSL)" + doc = "TLS Provider" ) - private String tlsProvider = "JDK"; + private String tlsProvider = null; @FieldContext( category = CATEGORY_KEYSTORE_TLS, @@ -386,7 +386,12 @@ public class ProxyConfiguration implements PulsarConfiguration { /**** --- KeyStore TLS config variables used for proxy to auth with broker--- ****/ @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "The TLS Provider (JDK or OpenSSL) used by the Pulsar proxy to authenticate with Pulsar brokers" + doc = "Whether the Pulsar proxy use KeyStore type to authenticate with Pulsar brokers" + ) + private boolean brokerClientTlsEnabledWithKeyStore = false; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "The TLS Provider used by the Pulsar proxy to authenticate with Pulsar brokers" ) private String brokerClientSslProvider = null; @FieldContext( diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java index 6e719f657470f..d0bc217829edc 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConnection.java @@ -23,6 +23,7 @@ import java.net.SocketAddress; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import javax.naming.AuthenticationException; import javax.net.ssl.SSLSession; @@ -71,7 +72,7 @@ public class ProxyConnection extends PulsarHandler implements FutureListener sslHandlerSupplier; private LookupProxyHandler lookupProxyHandler = null; @Getter @@ -111,11 +112,11 @@ ConnectionPool getConnectionPool() { return client.getCnxPool(); } - public ProxyConnection(ProxyService proxyService, SslHandler sslHandler) { + public ProxyConnection(ProxyService proxyService, Supplier sslHandlerSupplier) { super(30, TimeUnit.SECONDS); this.service = proxyService; this.state = State.Init; - this.sslHandler = sslHandler; + this.sslHandlerSupplier = sslHandlerSupplier; } @Override @@ -213,7 +214,7 @@ private void completeConnect() { // connection there and just pass bytes in both directions state = State.ProxyConnectionToBroker; directProxyHandler = new DirectProxyHandler(service, this, proxyToBrokerUrl, - protocolVersionToAdvertise, sslHandler); + protocolVersionToAdvertise, sslHandlerSupplier); cancelKeepAliveTask(); } else { // Client is doing a lookup, we can consider the handshake complete @@ -412,8 +413,15 @@ ClientConfigurationData createClientConfiguration() throws UnsupportedAuthentica } if (proxyConfig.isTlsEnabledWithBroker()) { clientConf.setUseTls(true); - clientConf.setTlsTrustCertsFilePath(proxyConfig.getBrokerClientTrustCertsFilePath()); - clientConf.setTlsAllowInsecureConnection(proxyConfig.isTlsAllowInsecureConnection()); + if (proxyConfig.isBrokerClientTlsEnabledWithKeyStore()) { + clientConf.setUseKeyStoreTls(true); + clientConf.setTlsTrustStoreType(proxyConfig.getBrokerClientTlsTrustStoreType()); + clientConf.setTlsTrustStorePath(proxyConfig.getBrokerClientTlsTrustStore()); + clientConf.setTlsTrustStorePassword(proxyConfig.getBrokerClientTlsTrustStorePassword()); + } else { + clientConf.setTlsTrustCertsFilePath(proxyConfig.getBrokerClientTrustCertsFilePath()); + clientConf.setTlsAllowInsecureConnection(proxyConfig.isTlsAllowInsecureConnection()); + } } return clientConf; } diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ServiceChannelInitializer.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ServiceChannelInitializer.java index f560f3f59a240..42a5c07d911ce 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ServiceChannelInitializer.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ServiceChannelInitializer.java @@ -21,6 +21,7 @@ import static org.apache.commons.lang3.StringUtils.isEmpty; import io.netty.handler.ssl.SslHandler; +import java.util.function.Supplier; import org.apache.pulsar.client.api.AuthenticationDataProvider; import org.apache.pulsar.client.api.AuthenticationFactory; import org.apache.pulsar.common.protocol.Commands; @@ -32,7 +33,7 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.ssl.SslContext; import org.apache.pulsar.common.util.SslContextAutoRefreshBuilder; -import org.apache.pulsar.common.util.keystoretls.NettySSLEngineAutoRefreshBuilder; +import org.apache.pulsar.common.util.keystoretls.NettySSLContextAutoRefreshBuilder; /** * Initialize service channel handlers. @@ -47,8 +48,8 @@ public class ServiceChannelInitializer extends ChannelInitializer private SslContextAutoRefreshBuilder serverSslCtxRefresher; private SslContextAutoRefreshBuilder clientSslCtxRefresher; - private NettySSLEngineAutoRefreshBuilder serverSSLEngineRefreshBuilder; - private NettySSLEngineAutoRefreshBuilder clientSSLEngineRefreshBuilder; + private NettySSLContextAutoRefreshBuilder serverSSLContextAutoRefreshBuilder; + private NettySSLContextAutoRefreshBuilder clientSSLContextAutoRefreshBuilder; public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration serviceConfig, boolean enableTls) throws Exception { @@ -59,7 +60,7 @@ public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration s if (enableTls) { if (tlsEnabledWithKeyStore) { - serverSSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + serverSSLContextAutoRefreshBuilder = new NettySSLContextAutoRefreshBuilder( serviceConfig.getTlsProvider(), serviceConfig.getTlsKeyStoreType(), serviceConfig.getTlsKeyStore(), @@ -92,7 +93,7 @@ public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration s } if (tlsEnabledWithKeyStore) { - clientSSLEngineRefreshBuilder = new NettySSLEngineAutoRefreshBuilder( + clientSSLContextAutoRefreshBuilder = new NettySSLContextAutoRefreshBuilder( serviceConfig.getBrokerClientSslProvider(), serviceConfig.isTlsAllowInsecureConnection(), serviceConfig.getBrokerClientTlsTrustStoreType(), @@ -117,26 +118,37 @@ public ServiceChannelInitializer(ProxyService proxyService, ProxyConfiguration s @Override protected void initChannel(SocketChannel ch) throws Exception { if (serverSslCtxRefresher != null && this.enableTls) { - if (this.tlsEnabledWithKeyStore) { - ch.pipeline().addLast(TLS_HANDLER, new SslHandler(serverSSLEngineRefreshBuilder.get())); - } else { - SslContext sslContext = serverSslCtxRefresher.get(); - if (sslContext != null) { - ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); - } + SslContext sslContext = serverSslCtxRefresher.get(); + if (sslContext != null) { + ch.pipeline().addLast(TLS_HANDLER, sslContext.newHandler(ch.alloc())); } + } else if (this.tlsEnabledWithKeyStore && serverSSLContextAutoRefreshBuilder != null) { + ch.pipeline().addLast(TLS_HANDLER, + new SslHandler(serverSSLContextAutoRefreshBuilder.get().createSSLEngine())); } ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder( - Commands.DEFAULT_MAX_MESSAGE_SIZE + Commands.MESSAGE_SIZE_FRAME_PADDING, 0, 4, 0, 4)); + Commands.DEFAULT_MAX_MESSAGE_SIZE + Commands.MESSAGE_SIZE_FRAME_PADDING, 0, 4, 0, 4)); - if (clientSSLEngineRefreshBuilder != null && tlsEnabledWithKeyStore) { - ch.pipeline().addLast("handler", - new ProxyConnection(proxyService, new SslHandler(clientSSLEngineRefreshBuilder.get()))); - } else { - ch.pipeline().addLast("handler", - new ProxyConnection(proxyService, - clientSslCtxRefresher == null ? null : clientSslCtxRefresher.get().newHandler(ch.alloc()))); + Supplier sslHandlerSupplier = null; + if (clientSslCtxRefresher != null) { + sslHandlerSupplier = new Supplier() { + @Override + public SslHandler get() { + return clientSslCtxRefresher.get().newHandler(ch.alloc()); + } + }; + } else if (clientSSLContextAutoRefreshBuilder != null) { + sslHandlerSupplier = new Supplier() { + @Override + public SslHandler get() { + return new SslHandler(clientSSLContextAutoRefreshBuilder.get().createSSLEngine()); + } + }; } + + ch.pipeline().addLast("handler", + new ProxyConnection(proxyService, sslHandlerSupplier)); + } } From ed994c6e31aff3afcda2eb518e3970b3bf707c9c Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Sat, 2 May 2020 10:58:20 +0800 Subject: [PATCH 05/11] fix ut --- .../apache/pulsar/client/impl/PulsarChannelInitializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java index ed888b31064ca..4a145e7410126 100644 --- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java +++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/PulsarChannelInitializer.java @@ -57,9 +57,9 @@ public PulsarChannelInitializer(ClientConfigurationData conf, Supplier Date: Sat, 2 May 2020 13:17:59 +0800 Subject: [PATCH 06/11] fix discovery config ut, add keystore.ProxyTlsTestWithAuth --- .../auth/AuthenticationDataKeyStoreTls.java | 5 + .../pulsar/client/ProxyTlsTestWithAuth.java | 201 ++++++++++++++++++ .../TlsProducerConsumerTestWithAuth.java | 20 +- .../service/server/ServiceConfig.java | 4 + 4 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java index fc59dfd7873eb..6d78004a0678f 100644 --- a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java +++ b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java @@ -42,4 +42,9 @@ public boolean hasDataForTls() { public KeyStoreParams getTlsKeyStoreParams() { return this.keyStoreParams; } + + @Override + public String getCommandData() { + return null; + } } diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java new file mode 100644 index 0000000000000..3b85a52bf740c --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java @@ -0,0 +1,201 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.mockito.Mockito.doReturn; + +import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import lombok.Cleanup; +import lombok.extern.slf4j.Slf4j; +import org.apache.pulsar.broker.auth.MockedPulsarServiceBaseTest; +import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; +import org.apache.pulsar.broker.authentication.AuthenticationService; +import org.apache.pulsar.client.api.ClientBuilder; +import org.apache.pulsar.client.api.Consumer; +import org.apache.pulsar.client.api.Message; +import org.apache.pulsar.client.api.MessageRoutingMode; +import org.apache.pulsar.client.api.Producer; +import org.apache.pulsar.client.api.PulsarClient; +import org.apache.pulsar.client.api.Schema; +import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; +import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.proxy.server.ProxyConfiguration; +import org.apache.pulsar.proxy.server.ProxyService; +import org.mockito.Mockito; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Slf4j +public class ProxyTlsTestWithAuth extends MockedPulsarServiceBaseTest { + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_CN = "clientuser"; + protected final String KEYSTORE_TYPE = "JKS"; + + private final String DUMMY_VALUE = "DUMMY_VALUE"; + + private ProxyService proxyService; + private ProxyConfiguration proxyConfig = new ProxyConfiguration(); + + @Override + @BeforeMethod + protected void setup() throws Exception { + internalSetup(); + + proxyConfig.setServicePort(Optional.of(0)); + proxyConfig.setServicePortTls(Optional.of(0)); + proxyConfig.setWebServicePort(Optional.of(0)); + proxyConfig.setWebServicePortTls(Optional.of(0)); + proxyConfig.setTlsEnabledWithBroker(false); + + proxyConfig.setTlsEnabledWithKeyStore(true); + proxyConfig.setTlsKeyStoreType(KEYSTORE_TYPE); + proxyConfig.setTlsKeyStore(BROKER_KEYSTORE_FILE_PATH); + proxyConfig.setTlsKeyStorePassword(BROKER_KEYSTORE_PW); + proxyConfig.setTlsTrustStoreType(KEYSTORE_TYPE); + proxyConfig.setTlsTrustStore(CLIENT_TRUSTSTORE_FILE_PATH); + proxyConfig.setTlsTrustStorePassword(CLIENT_TRUSTSTORE_PW); + + proxyConfig.setZookeeperServers(DUMMY_VALUE); + proxyConfig.setConfigurationStoreServers(DUMMY_VALUE); + + + // config for authentication and authorization. + proxyConfig.setTlsRequireTrustedClientCertOnConnect(true); + proxyConfig.setSuperUserRoles(Sets.newHashSet(CLIENT_KEYSTORE_CN)); + proxyConfig.setAuthenticationEnabled(true); + proxyConfig.setAuthorizationEnabled(true); + Set providers = new HashSet<>(); + providers.add(AuthenticationProviderTls.class.getName()); + proxyConfig.setAuthenticationProviders(providers); + + proxyService = Mockito.spy(new ProxyService(proxyConfig, new AuthenticationService( + PulsarConfigurationLoader.convertFrom(proxyConfig)))); + doReturn(mockZooKeeperClientFactory).when(proxyService).getZooKeeperClientFactory(); + + proxyService.start(); + } + + @Override + @AfterMethod + protected void cleanup() throws Exception { + internalCleanup(); + + proxyService.close(); + } + + protected PulsarClient internalSetUpForClient(boolean addCertificates, String lookupUrl) throws Exception { + ClientBuilder clientBuilder = PulsarClient.builder() + .serviceUrl(lookupUrl) + .enableTls(true) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(false) + .operationTimeout(1000, TimeUnit.MILLISECONDS); + if (addCertificates) { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_TYPE, KEYSTORE_TYPE); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + clientBuilder.authentication(AuthenticationKeyStoreTls.class.getName(), authParams); + } + return clientBuilder.build(); + } + + @Test + public void testProducer() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(true, proxyService.getServiceUrlTls()); + @Cleanup + Producer producer = client.newProducer(Schema.BYTES) + .topic("persistent://sample/test/local/topic" + System.currentTimeMillis()) + .create(); + + for (int i = 0; i < 10; i++) { + producer.send("test".getBytes()); + } + } + + @Test + public void testProducerFailed() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(false, proxyService.getServiceUrlTls()); + try { + @Cleanup + Producer producer = client.newProducer(Schema.BYTES) + .topic("persistent://sample/test/local/topic" + System.currentTimeMillis()) + .create(); + Assert.fail("Should failed since broker setTlsRequireTrustedClientCertOnConnect, " + + "while client not set keystore"); + } catch (Exception e) { + // expected + log.info("Expected failed since broker setTlsRequireTrustedClientCertOnConnect," + + " while client not set keystore"); + } + } + + @Test + public void testPartitions() throws Exception { + @Cleanup + PulsarClient client = internalSetUpForClient(true, proxyService.getServiceUrlTls()); + String topicName = "persistent://sample/test/local/partitioned-topic" + System.currentTimeMillis(); + TenantInfo tenantInfo = createDefaultTenantInfo(); + admin.tenants().createTenant("sample", tenantInfo); + admin.topics().createPartitionedTopic(topicName, 2); + + @Cleanup + Producer producer = client.newProducer(Schema.BYTES).topic(topicName) + .messageRoutingMode(MessageRoutingMode.RoundRobinPartition).create(); + + // Create a consumer directly attached to broker + @Cleanup + Consumer consumer = pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-sub").subscribe(); + + for (int i = 0; i < 10; i++) { + producer.send("test".getBytes()); + } + + for (int i = 0; i < 10; i++) { + Message msg = consumer.receive(1, TimeUnit.SECONDS); + checkNotNull(msg); + } + } + + +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java index b410fa6a20acf..912bbd6a80005 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java @@ -210,16 +210,16 @@ public void testTlsClientAuthOverBinaryProtocol() throws Exception { internalSetUpForNamespace(); - // Test 1 - Using TLS on binary protocol without sending certs - expect failure - internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); - - try { - pulsarClient.newConsumer().topic(topicName) - .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); - Assert.fail("Server should have failed the TLS handshake since client didn't ."); - } catch (Exception ex) { - // OK - } +// // Test 1 - Using TLS on binary protocol without sending certs - expect failure +// internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); +// +// try { +// pulsarClient.newConsumer().topic(topicName) +// .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); +// Assert.fail("Server should have failed the TLS handshake since client didn't ."); +// } catch (Exception ex) { +// // OK +// } // Using TLS on binary protocol - sending certs internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); diff --git a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java index ea1c68f4026c4..57e18fa97e4fa 100644 --- a/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java +++ b/pulsar-discovery-service/src/main/java/org/apache/pulsar/discovery/service/server/ServiceConfig.java @@ -122,4 +122,8 @@ public class ServiceConfig implements PulsarConfiguration { private String tlsTrustStorePassword = null; private Properties properties = new Properties(); + + public String getConfigurationStoreServers() { + return null == configurationStoreServers ? getGlobalZookeeperServers() : configurationStoreServers; + } } From abb7dee4f31ec7bbe096ebc979e987a3e58cc1d7 Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Thu, 7 May 2020 17:42:43 +0800 Subject: [PATCH 07/11] change follow comments remove unused code. add test for brokerclientauth --- .../pulsar/broker/ServiceConfiguration.java | 47 ++++ .../apache/pulsar/broker/PulsarService.java | 26 +- .../pulsar/broker/service/BrokerService.java | 20 +- .../impl/auth/AuthenticationKeyStoreTls.java | 2 +- .../pulsar/client/AdminApiTlsAuthTest.java | 226 ++++++++++++++++++ .../TlsProducerConsumerTestWithAuth.java | 25 +- .../util/keystoretls/KeyStoreSSLContext.java | 140 +++-------- .../proxy/server/ProxyConfiguration.java | 19 +- 8 files changed, 360 insertions(+), 145 deletions(-) create mode 100644 pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java index 49b83c76b0003..5e588084b282a 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java @@ -1627,6 +1627,53 @@ public class ServiceConfiguration implements PulsarConfiguration { ) private String tlsTrustStorePassword = null; + /**** --- KeyStore TLS config variables used for internal client/admin to auth with other broker--- ****/ + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Whether the Pulsar proxy use KeyStore type to authenticate with Pulsar brokers" + ) + private boolean brokerClientTlsEnabledWithKeyStore = false; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "The TLS Provider used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientSslProvider = null; + // needed when client auth is required + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore type configuration for proxy: JKS, PKCS12 " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStoreType = "JKS"; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore path for proxy, " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStore = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "TLS TrustStore password for proxy, " + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private String brokerClientTlsTrustStorePassword = null; + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Specify the tls cipher the proxy will use to negotiate during TLS Handshake" + + " (a comma-separated list of ciphers).\n\n" + + "Examples:- [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256].\n" + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private Set brokerClientTlsCiphers = Sets.newTreeSet(); + @FieldContext( + category = CATEGORY_KEYSTORE_TLS, + doc = "Specify the tls protocols the broker will use to negotiate during TLS handshake" + + " (a comma-separated list of protocol names).\n\n" + + "Examples:- [TLSv1.2, TLSv1.1, TLSv1] \n" + + " used by the Pulsar proxy to authenticate with Pulsar brokers" + ) + private Set brokerClientTlsProtocols = Sets.newTreeSet(); + /** * @deprecated See {@link #getConfigurationStoreServers} */ diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java index e3874efc0e337..4cc8879517b6e 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java @@ -960,10 +960,17 @@ public synchronized PulsarClient getClient() throws PulsarServerException { .tlsTrustCertsFilePath(this.getConfiguration().getTlsCertificateFilePath()); if (this.getConfiguration().isBrokerClientTlsEnabled()) { - builder.tlsTrustCertsFilePath( - isNotBlank(this.getConfiguration().getBrokerClientTrustCertsFilePath()) - ? this.getConfiguration().getBrokerClientTrustCertsFilePath() - : this.getConfiguration().getTlsCertificateFilePath()); + if (this.getConfiguration().isBrokerClientTlsEnabledWithKeyStore()) { + builder.useKeyStoreTls(true) + .tlsTrustStoreType(this.getConfiguration().getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(this.getConfiguration().getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(this.getConfiguration().getBrokerClientTlsTrustStorePassword()); + } else { + builder.tlsTrustCertsFilePath( + isNotBlank(this.getConfiguration().getBrokerClientTrustCertsFilePath()) + ? this.getConfiguration().getBrokerClientTrustCertsFilePath() + : this.getConfiguration().getTlsCertificateFilePath()); + } } if (isNotBlank(this.getConfiguration().getBrokerClientAuthenticationPlugin())) { @@ -989,8 +996,15 @@ public synchronized PulsarAdmin getAdminClient() throws PulsarServerException { conf.getBrokerClientAuthenticationParameters()); if (conf.isBrokerClientTlsEnabled()) { - builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); - builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); + if (this.getConfiguration().isBrokerClientTlsEnabledWithKeyStore()) { + builder.useKeyStoreTls(true) + .tlsTrustStoreType(this.getConfiguration().getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(this.getConfiguration().getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(this.getConfiguration().getBrokerClientTlsTrustStorePassword()); + } else { + builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); + builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); + } } // most of the admin request requires to make zk-call so, keep the max read-timeout based on diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java index 5d28fedfc0d97..af1cdfcb307e2 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/BrokerService.java @@ -796,8 +796,17 @@ public PulsarClient getReplicationClient(String cluster) { .serviceUrl(isNotBlank(data.getBrokerServiceUrlTls()) ? data.getBrokerServiceUrlTls() : data.getServiceUrlTls()) .enableTls(true) - .tlsTrustCertsFilePath(pulsar.getConfiguration().getBrokerClientTrustCertsFilePath()) .allowTlsInsecureConnection(pulsar.getConfiguration().isTlsAllowInsecureConnection()); + if (pulsar.getConfiguration().isBrokerClientTlsEnabledWithKeyStore()) { + clientBuilder.useKeyStoreTls(true) + .tlsTrustStoreType(pulsar.getConfiguration().getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(pulsar.getConfiguration().getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(pulsar.getConfiguration() + .getBrokerClientTlsTrustStorePassword()); + } else { + clientBuilder.tlsTrustCertsFilePath(pulsar.getConfiguration() + .getBrokerClientTrustCertsFilePath()); + } } else { clientBuilder.serviceUrl( isNotBlank(data.getBrokerServiceUrl()) ? data.getBrokerServiceUrl() : data.getServiceUrl()); @@ -833,8 +842,15 @@ public PulsarAdmin getClusterPulsarAdmin(String cluster) { conf.getBrokerClientAuthenticationParameters()); if (isTlsUrl) { - builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); + if (conf.isBrokerClientTlsEnabledWithKeyStore()) { + builder.useKeyStoreTls(true) + .tlsTrustStoreType(conf.getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(conf.getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(conf.getBrokerClientTlsTrustStorePassword()); + } else { + builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); + } } // most of the admin request requires to make zk-call so, keep the max read-timeout based on diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java index 93514fa0ae810..e8c7764f027fc 100644 --- a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java +++ b/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java @@ -89,7 +89,7 @@ public void configure(String paramsString) { params = AuthenticationUtil.configureFromJsonString(paramsString); } catch (Exception e) { // auth-param is not in json format - log.info("parameter not in Json format: ", paramsString); + log.info("parameter not in Json format: {}", paramsString); } // in ":" "," format. diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java new file mode 100644 index 0000000000000..267798426bb15 --- /dev/null +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java @@ -0,0 +1,226 @@ +/** + * 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. + */ +package org.apache.pulsar.client; + +import static org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls.mapToString; +import static org.testng.Assert.fail; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.net.ssl.SSLContext; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MediaType; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.pulsar.broker.authentication.AuthenticationProviderTls; +import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.admin.PulsarAdminException; +import org.apache.pulsar.client.admin.internal.JacksonConfigurator; +import org.apache.pulsar.client.api.ProducerConsumerBase; +import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; +import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.util.keystoretls.KeyStoreSSLContext; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Slf4j +public class AdminApiTlsAuthTest extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_PW = "111111"; + protected final String BROKER_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_PW = "111111"; + protected final String CLIENT_TRUSTSTORE_PW = "111111"; + + protected final String CLIENT_KEYSTORE_CN = "clientuser"; + protected final String KEYSTORE_TYPE = "JKS"; + + private final String clusterName = "test"; + Set tlsProtocols = Sets.newConcurrentHashSet(); + + @BeforeMethod + @Override + public void setup() throws Exception { + conf.setLoadBalancerEnabled(true); + conf.setBrokerServicePortTls(Optional.of(0)); + conf.setWebServicePortTls(Optional.of(0)); + + conf.setTlsEnabledWithKeyStore(true); + conf.setTlsKeyStoreType(KEYSTORE_TYPE); + conf.setTlsKeyStore(BROKER_KEYSTORE_FILE_PATH); + conf.setTlsKeyStorePassword(BROKER_KEYSTORE_PW); + + conf.setTlsTrustStoreType(KEYSTORE_TYPE); + conf.setTlsTrustStore(CLIENT_TRUSTSTORE_FILE_PATH); + conf.setTlsTrustStorePassword(CLIENT_TRUSTSTORE_PW); + + conf.setClusterName(clusterName); + conf.setTlsRequireTrustedClientCertOnConnect(true); + tlsProtocols.add("TLSv1.2"); + conf.setTlsProtocols(tlsProtocols); + + // config for authentication and authorization. + conf.setSuperUserRoles(Sets.newHashSet(CLIENT_KEYSTORE_CN)); + conf.setAuthenticationEnabled(true); + conf.setAuthorizationEnabled(true); + Set providers = new HashSet<>(); + providers.add(AuthenticationProviderTls.class.getName()); + conf.setAuthenticationProviders(providers); + + conf.setBrokerClientTlsEnabled(true); + conf.setBrokerClientTlsEnabledWithKeyStore(true); + + // set broker client tls auth + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_TYPE, KEYSTORE_TYPE); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + conf.setBrokerClientAuthenticationPlugin(AuthenticationKeyStoreTls.class.getName()); + conf.setBrokerClientAuthenticationParameters(mapToString(authParams)); + conf.setBrokerClientTlsTrustStore(BROKER_TRUSTSTORE_FILE_PATH); + conf.setBrokerClientTlsTrustStorePassword(BROKER_TRUSTSTORE_PW); + + super.init(); + + PulsarAdmin admin = buildAdminClient(); + admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); + admin.close(); + } + + @AfterMethod + @Override + public void cleanup() throws Exception { + super.internalCleanup(); + } + + WebTarget buildWebClient() throws Exception { + ClientConfig httpConfig = new ClientConfig(); + httpConfig.property(ClientProperties.FOLLOW_REDIRECTS, true); + httpConfig.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 8); + httpConfig.register(MultiPartFeature.class); + + ClientBuilder clientBuilder = ClientBuilder.newBuilder().withConfig(httpConfig) + .register(JacksonConfigurator.class).register(JacksonFeature.class); + + SSLContext sslCtx = KeyStoreSSLContext.createClientSslContext( + KEYSTORE_TYPE, + CLIENT_KEYSTORE_FILE_PATH, + CLIENT_KEYSTORE_PW, + KEYSTORE_TYPE, + BROKER_TRUSTSTORE_FILE_PATH, + BROKER_TRUSTSTORE_PW); + + clientBuilder.sslContext(sslCtx).hostnameVerifier(NoopHostnameVerifier.INSTANCE); + Client client = clientBuilder.build(); + + return client.target(brokerUrlTls.toString()); + } + + PulsarAdmin buildAdminClient() throws Exception { + Map authParams = new HashMap<>(); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PATH, CLIENT_KEYSTORE_FILE_PATH); + authParams.put(AuthenticationKeyStoreTls.KEYSTORE_PW, CLIENT_KEYSTORE_PW); + + return PulsarAdmin.builder() + .serviceHttpUrl(brokerUrlTls.toString()) + .useKeyStoreTls(true) + .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) + .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) + .allowTlsInsecureConnection(false) + .authentication(AuthenticationKeyStoreTls.class.getName(), authParams) + .build(); + } + + @Test + public void testSuperUserCanListTenants() throws Exception { + try (PulsarAdmin admin = buildAdminClient()) { + admin.tenants().createTenant("tenant1", + new TenantInfo(ImmutableSet.of("foobar"), + ImmutableSet.of("test"))); + Assert.assertEquals(ImmutableSet.of("tenant1"), admin.tenants().getTenants()); + } + } + + @Test + public void testSuperUserCantListNamespaces() throws Exception { + try (PulsarAdmin admin = buildAdminClient()) { + admin.tenants().createTenant("tenant1", + new TenantInfo(ImmutableSet.of("proxy"), + ImmutableSet.of("test"))); + admin.namespaces().createNamespace("tenant1/ns1"); + admin.namespaces().getNamespaces("tenant1").contains("tenant1/ns1"); + } + } + + @Test + public void testAuthorizedUserAsOriginalPrincipal() throws Exception { + try (PulsarAdmin admin = buildAdminClient()) { + admin.tenants().createTenant("tenant1", + new TenantInfo(ImmutableSet.of("proxy", "user1"), + ImmutableSet.of("test"))); + admin.namespaces().createNamespace("tenant1/ns1"); + } + WebTarget root = buildWebClient(); + Assert.assertEquals(ImmutableSet.of("tenant1/ns1"), + root.path("/admin/v2/namespaces").path("tenant1") + .request(MediaType.APPLICATION_JSON) + .header("X-Original-Principal", "user1") + .get(new GenericType>() {})); + } + + @Test + public void testPersistentList() throws Exception { + log.info("-- Starting {} test --", methodName); + + /***** Broker 2 Started *****/ + try (PulsarAdmin admin = buildAdminClient()) { + admin.tenants().createTenant("tenant1", + new TenantInfo(ImmutableSet.of("foobar"), + ImmutableSet.of("test"))); + Assert.assertEquals(ImmutableSet.of("tenant1"), admin.tenants().getTenants()); + + admin.namespaces().createNamespace("tenant1/ns1"); + + // this will calls internal admin to list nonpersist topics. + admin.topics().getList("tenant1/ns1"); + } catch (PulsarAdminException ex) { + ex.printStackTrace(); + fail("Should not have thrown an exception"); + } + } +} diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java index 912bbd6a80005..abaf10dc88525 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java @@ -63,7 +63,6 @@ public class TlsProducerConsumerTestWithAuth extends ProducerConsumerBase { protected final String KEYSTORE_TYPE = "JKS"; private final String clusterName = "use"; - Set tlsProtocols = Sets.newConcurrentHashSet(); @BeforeMethod @Override @@ -97,8 +96,6 @@ protected void internalSetUpForBroker() throws Exception { conf.setClusterName(clusterName); conf.setTlsRequireTrustedClientCertOnConnect(true); - tlsProtocols.add("TLSv1.2"); - conf.setTlsProtocols(tlsProtocols); // config for authentication and authorization. conf.setSuperUserRoles(Sets.newHashSet(CLIENT_KEYSTORE_CN)); @@ -148,7 +145,7 @@ protected void internalSetUpForNamespace() throws Exception { .useKeyStoreTls(true) .tlsTrustStorePath(BROKER_TRUSTSTORE_FILE_PATH) .tlsTrustStorePassword(BROKER_TRUSTSTORE_PW) - .allowTlsInsecureConnection(true) + .allowTlsInsecureConnection(false) .authentication(AuthenticationKeyStoreTls.class.getName(), authParams).build()); admin.clusters().createCluster(clusterName, new ClusterData(brokerUrl.toString(), brokerUrlTls.toString(), pulsar.getBrokerServiceUrl(), pulsar.getBrokerServiceUrlTls())); @@ -210,16 +207,16 @@ public void testTlsClientAuthOverBinaryProtocol() throws Exception { internalSetUpForNamespace(); -// // Test 1 - Using TLS on binary protocol without sending certs - expect failure -// internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); -// -// try { -// pulsarClient.newConsumer().topic(topicName) -// .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); -// Assert.fail("Server should have failed the TLS handshake since client didn't ."); -// } catch (Exception ex) { -// // OK -// } + // Test 1 - Using TLS on binary protocol without sending certs - expect failure + internalSetUpForClient(false, pulsar.getBrokerServiceUrlTls()); + + try { + pulsarClient.newConsumer().topic(topicName) + .subscriptionName("my-subscriber-name").subscriptionType(SubscriptionType.Exclusive).subscribe(); + Assert.fail("Server should have failed the TLS handshake since client didn't ."); + } catch (Exception ex) { + // OK + } // Using TLS on binary protocol - sending certs internalSetUpForClient(true, pulsar.getBrokerServiceUrlTls()); diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java index 39743cd6eaad7..b9ad2e7d6ed4d 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/util/keystoretls/KeyStoreSSLContext.java @@ -21,6 +21,7 @@ import static org.apache.pulsar.common.util.SecurityUtility.getProvider; import com.google.common.base.Strings; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -62,25 +63,6 @@ public enum Mode { SERVER } - /** - * Supported Key File Types. - */ - public enum KeyStoreType { - PKCS12("PKCS12"), - JKS("JKS"); - - private String str; - - KeyStoreType(String str) { - this.str = str; - } - - @Override - public String toString() { - return this.str; - } - } - @Getter private final Mode mode; @@ -102,6 +84,7 @@ public String toString() { private String kmfAlgorithm = DEFAULT_SSL_KEYMANGER_ALGORITHM; private String tmfAlgorithm = DEFAULT_SSL_TRUSTMANAGER_ALGORITHM; + // only init vars, before using it, need to call createSSLContext to create ssl context. public KeyStoreSSLContext(Mode mode, String sslProviderString, String keyStoreTypeString, @@ -165,11 +148,16 @@ public SSLContext createSSLContext() throws GeneralSecurityException, IOExceptio } // trust store - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm); - KeyStore trustStore = KeyStore.getInstance(trustStoreTypeString); - char[] passwordChars = trustStorePassword.toCharArray(); - trustStore.load(new FileInputStream(trustStorePath), passwordChars); - trustManagerFactory.init(trustStore); + TrustManagerFactory trustManagerFactory; + if (this.allowInsecureConnection) { + trustManagerFactory = InsecureTrustManagerFactory.INSTANCE; + } else { + trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm); + KeyStore trustStore = KeyStore.getInstance(trustStoreTypeString); + char[] passwordChars = trustStorePassword.toCharArray(); + trustStore.load(new FileInputStream(trustStorePath), passwordChars); + trustManagerFactory.init(trustStore); + } // init sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom()); @@ -193,84 +181,6 @@ public SSLEngine createSSLEngine() { return sslEngine; } - // for netty server - public static SSLEngine createNettySSLEngineForServer(String sslProviderString, - String keyStoreTypeString, - String keyStorePath, - String keyStorePassword, - boolean allowInsecureConnection, - String trustStoreTypeString, - String trustStorePath, - String trustStorePassword, - boolean requireTrustedClientCertOnConnect, - Set ciphers, - Set protocols) - throws GeneralSecurityException, IOException { - KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.SERVER, - sslProviderString, - keyStoreTypeString, - keyStorePath, - keyStorePassword, - allowInsecureConnection, - trustStoreTypeString, - trustStorePath, - trustStorePassword, - requireTrustedClientCertOnConnect, - ciphers, - protocols); - - SSLContext sslContext = keyStoreSSLContext.createSSLContext(); - - SSLEngine sslEngine = sslContext.createSSLEngine(); - sslEngine.setUseClientMode(false); - - sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols()); - sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites()); - - if (keyStoreSSLContext.mode == Mode.SERVER) { - sslEngine.setNeedClientAuth(keyStoreSSLContext.needClientAuth); - } else { - sslEngine.setWantClientAuth(keyStoreSSLContext.needClientAuth); - } - return sslEngine; - } - - // for netty client - public static SSLEngine createNettySSLEngineForClient(String sslProviderString, - String keyStoreTypeString, - String keyStorePath, - String keyStorePassword, - boolean allowInsecureConnection, - String trustStoreTypeString, - String trustStorePath, - String trustStorePassword, - Set ciphers, - Set protocols) - throws GeneralSecurityException, IOException { - KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, - sslProviderString, - keyStoreTypeString, - keyStorePath, - keyStorePassword, - allowInsecureConnection, - trustStoreTypeString, - trustStorePath, - trustStorePassword, - false, - ciphers, - protocols); - - SSLContext sslContext = keyStoreSSLContext.createSSLContext(); - - SSLEngine sslEngine = sslContext.createSSLEngine(); - sslEngine.setUseClientMode(true); - - sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols()); - sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites()); - - return sslEngine; - } - public static KeyStoreSSLContext createClientKeyStoreSslContext(String sslProviderString, String keyStoreTypeString, String keyStorePath, @@ -367,8 +277,6 @@ public static SSLContext createClientSslContext(String sslProviderString, Set ciphers, Set protocol) throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { - SslContextFactory ssl = new SslContextFactory(); - KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, sslProviderString, keyStoreTypeString, @@ -379,6 +287,30 @@ public static SSLContext createClientSslContext(String sslProviderString, trustStorePath, trustStorePassword, false, + ciphers, + protocol); + + return keyStoreSSLContext.createSSLContext(); + } + + // for web client + public static SSLContext createClientSslContext(String keyStoreTypeString, + String keyStorePath, + String keyStorePassword, + String trustStoreTypeString, + String trustStorePath, + String trustStorePassword) + throws GeneralSecurityException, SSLException, FileNotFoundException, IOException { + KeyStoreSSLContext keyStoreSSLContext = new KeyStoreSSLContext(Mode.CLIENT, + null, + keyStoreTypeString, + keyStorePath, + keyStorePassword, + false, + trustStoreTypeString, + trustStorePath, + trustStorePassword, + false, null, null); diff --git a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java index 8aaecb0cc4ab4..7eac236c6dac2 100644 --- a/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java +++ b/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java @@ -394,24 +394,7 @@ public class ProxyConfiguration implements PulsarConfiguration { doc = "The TLS Provider used by the Pulsar proxy to authenticate with Pulsar brokers" ) private String brokerClientSslProvider = null; - @FieldContext( - category = CATEGORY_KEYSTORE_TLS, - doc = "TLS KeyStore type configuration for proxy: JKS, PKCS12," - + " used by the Pulsar proxy to authenticate with Pulsar brokers" - ) - private String brokerClientTlsKeyStoreType = "JKS"; - @FieldContext( - category = CATEGORY_KEYSTORE_TLS, - doc = "TLS KeyStore file path configuration for proxy," - + " used by the Pulsar proxy to authenticate with Pulsar brokers" - ) - private String brokerClientTlsKeyStore = null; - @FieldContext( - category = CATEGORY_KEYSTORE_TLS, - doc = "TLS KeyStore password configuration for proxy," - + " used by the Pulsar proxy to authenticate with Pulsar brokers" - ) - private String brokerClientTlsKeyStorePassword = null; + // needed when client auth is required @FieldContext( category = CATEGORY_KEYSTORE_TLS, From 3a5ef0bfda1535b33b6eb61fa5669d343dfa6f46 Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Fri, 8 May 2020 00:43:54 +0800 Subject: [PATCH 08/11] add some docs in config file --- conf/broker.conf | 56 +++++++++++ conf/standalone.conf | 96 +++++++++++++++++++ distribution/server/pom.xml | 6 ++ .../pulsar/broker/ServiceConfiguration.java | 28 +++--- .../org/apache/pulsar/PulsarStandalone.java | 25 ++++- .../apache/pulsar/broker/PulsarService.java | 14 +-- .../pulsar/client/AdminApiTlsAuthTest.java | 9 +- site2/docs/reference-configuration.md | 12 +++ 8 files changed, 217 insertions(+), 29 deletions(-) diff --git a/conf/broker.conf b/conf/broker.conf index 2c5d6b9429e8c..4a9c8eca2c9aa 100644 --- a/conf/broker.conf +++ b/conf/broker.conf @@ -406,6 +406,62 @@ tlsCiphers= # authentication. tlsRequireTrustedClientCertOnConnect=false +### --- KeyStore TLS config variables --- ### +# Enable TLS with KeyStore type configuration in broker. +tlsEnabledWithKeyStore=false + +# TLS Provider for KeyStore type +tlsProvider= + +# TLS KeyStore type configuration in broker: JKS, PKCS12 +tlsKeyStoreType=JKS + +# TLS KeyStore path in broker +tlsKeyStore= + +# TLS KeyStore password for broker +tlsKeyStorePassword= + +# TLS TrustStore type configuration in broker: JKS, PKCS12 +tlsTrustStoreType=JKS + +# TLS TrustStore path in broker +tlsTrustStore= + +# TLS TrustStore password in broker +tlsTrustStorePassword= + +# Whether internal client use KeyStore type to authenticate with Pulsar brokers +brokerClientTlsEnabledWithKeyStore=false + +# The TLS Provider used by internal client to authenticate with other Pulsar brokers +brokerClientSslProvider= + +# TLS TrustStore type configuration for internal client: JKS, PKCS12 +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStoreType=JKS + +# TLS TrustStore path for internal client +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStore= + +# TLS TrustStore password for internal client, +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStorePassword= + +# Specify the tls cipher the internal client will use to negotiate during TLS Handshake +# (a comma-separated list of ciphers) +# e.g. [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]. +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsCiphers= + +# Specify the tls protocols the broker will use to negotiate during TLS handshake +# (a comma-separated list of protocol names). +# e.g. [TLSv1.2, TLSv1.1, TLSv1] +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsProtocols= + + ### --- Authentication --- ### # Enable authentication diff --git a/conf/standalone.conf b/conf/standalone.conf index 0fd80a358d5c0..8a5773fba758e 100644 --- a/conf/standalone.conf +++ b/conf/standalone.conf @@ -225,6 +225,102 @@ maxConsumersPerSubscription=0 # Use 0 or negative number to disable the check maxNumPartitionsPerPartitionedTopic=0 +### --- TLS --- ### +# Deprecated - Use webServicePortTls and brokerServicePortTls instead +tlsEnabled=false + +# Tls cert refresh duration in seconds (set 0 to check on every new connection) +tlsCertRefreshCheckDurationSec=300 + +# Path for the TLS certificate file +tlsCertificateFilePath= + +# Path for the TLS private key file +tlsKeyFilePath= + +# Path for the trusted TLS certificate file. +# This cert is used to verify that any certs presented by connecting clients +# are signed by a certificate authority. If this verification +# fails, then the certs are untrusted and the connections are dropped. +tlsTrustCertsFilePath= + +# Accept untrusted TLS certificate from client. +# If true, a client with a cert which cannot be verified with the +# 'tlsTrustCertsFilePath' cert will allowed to connect to the server, +# though the cert will not be used for client authentication. +tlsAllowInsecureConnection=false + +# Specify the tls protocols the broker will use to negotiate during TLS handshake +# (a comma-separated list of protocol names). +# Examples:- [TLSv1.2, TLSv1.1, TLSv1] +tlsProtocols= + +# Specify the tls cipher the broker will use to negotiate during TLS Handshake +# (a comma-separated list of ciphers). +# Examples:- [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256] +tlsCiphers= + +# Trusted client certificates are required for to connect TLS +# Reject the Connection if the Client Certificate is not trusted. +# In effect, this requires that all connecting clients perform TLS client +# authentication. +tlsRequireTrustedClientCertOnConnect=false + +### --- KeyStore TLS config variables --- ### +# Enable TLS with KeyStore type configuration in broker. +tlsEnabledWithKeyStore=false + +# TLS Provider for KeyStore type +tlsProvider= + +# TLS KeyStore type configuration in broker: JKS, PKCS12 +tlsKeyStoreType=JKS + +# TLS KeyStore path in broker +tlsKeyStore= + +# TLS KeyStore password for broker +tlsKeyStorePassword= + +# TLS TrustStore type configuration in broker: JKS, PKCS12 +tlsTrustStoreType=JKS + +# TLS TrustStore path in broker +tlsTrustStore= + +# TLS TrustStore password for broker +tlsTrustStorePassword= + +# Whether internal client use KeyStore type to authenticate with Pulsar brokers +brokerClientTlsEnabledWithKeyStore=false + +# The TLS Provider used by internal client to authenticate with other Pulsar brokers +brokerClientSslProvider= + +# TLS TrustStore type configuration for internal client: JKS, PKCS12 +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStoreType=JKS + +# TLS TrustStore path for internal client +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStore= + +# TLS TrustStore password for internal client, +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsTrustStorePassword= + +# Specify the tls cipher the internal client will use to negotiate during TLS Handshake +# (a comma-separated list of ciphers) +# e.g. [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]. +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsCiphers= + +# Specify the tls protocols the broker will use to negotiate during TLS handshake +# (a comma-separated list of protocol names). +# e.g. [TLSv1.2, TLSv1.1, TLSv1] +# used by the internal client to authenticate with Pulsar brokers +brokerClientTlsProtocols= + ### --- Authentication --- ### # Role names that are treated as "proxy roles". If the broker sees a request with #role as proxyRoles - it will demand to see a valid original principal. diff --git a/distribution/server/pom.xml b/distribution/server/pom.xml index 9942b7f2224f5..99701e3cdcccc 100644 --- a/distribution/server/pom.xml +++ b/distribution/server/pom.xml @@ -64,6 +64,12 @@ ${project.version}
+ + org.apache.pulsar + pulsar-client-auth-keystoretls + ${project.version} + + org.apache.pulsar pulsar-client-tools diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java index 5e588084b282a..768eeca1375e0 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/ServiceConfiguration.java @@ -1587,7 +1587,7 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS Provider" + doc = "TLS Provider for KeyStore type" ) private String tlsProvider = null; @@ -1605,7 +1605,7 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS KeyStore password in broker" + doc = "TLS KeyStore password for broker" ) private String tlsKeyStorePassword = null; @@ -1623,46 +1623,46 @@ public class ServiceConfiguration implements PulsarConfiguration { @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS TrustStore password in broker" + doc = "TLS TrustStore password for broker" ) private String tlsTrustStorePassword = null; /**** --- KeyStore TLS config variables used for internal client/admin to auth with other broker--- ****/ @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "Whether the Pulsar proxy use KeyStore type to authenticate with Pulsar brokers" + doc = "Whether internal client use KeyStore type to authenticate with other Pulsar brokers" ) private boolean brokerClientTlsEnabledWithKeyStore = false; @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "The TLS Provider used by the Pulsar proxy to authenticate with Pulsar brokers" + doc = "The TLS Provider used by internal client to authenticate with other Pulsar brokers" ) private String brokerClientSslProvider = null; // needed when client auth is required @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS TrustStore type configuration for proxy: JKS, PKCS12 " - + " used by the Pulsar proxy to authenticate with Pulsar brokers" + doc = "TLS TrustStore type configuration for internal client: JKS, PKCS12 " + + " used by the internal client to authenticate with Pulsar brokers" ) private String brokerClientTlsTrustStoreType = "JKS"; @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS TrustStore path for proxy, " - + " used by the Pulsar proxy to authenticate with Pulsar brokers" + doc = "TLS TrustStore path for internal client, " + + " used by the internal client to authenticate with Pulsar brokers" ) private String brokerClientTlsTrustStore = null; @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "TLS TrustStore password for proxy, " - + " used by the Pulsar proxy to authenticate with Pulsar brokers" + doc = "TLS TrustStore password for internal client, " + + " used by the internal client to authenticate with Pulsar brokers" ) private String brokerClientTlsTrustStorePassword = null; @FieldContext( category = CATEGORY_KEYSTORE_TLS, - doc = "Specify the tls cipher the proxy will use to negotiate during TLS Handshake" + doc = "Specify the tls cipher the internal client will use to negotiate during TLS Handshake" + " (a comma-separated list of ciphers).\n\n" + "Examples:- [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256].\n" - + " used by the Pulsar proxy to authenticate with Pulsar brokers" + + " used by the internal client to authenticate with Pulsar brokers" ) private Set brokerClientTlsCiphers = Sets.newTreeSet(); @FieldContext( @@ -1670,7 +1670,7 @@ public class ServiceConfiguration implements PulsarConfiguration { doc = "Specify the tls protocols the broker will use to negotiate during TLS handshake" + " (a comma-separated list of protocol names).\n\n" + "Examples:- [TLSv1.2, TLSv1.1, TLSv1] \n" - + " used by the Pulsar proxy to authenticate with Pulsar brokers" + + " used by the internal client to authenticate with Pulsar brokers" ) private Set brokerClientTlsProtocols = Sets.newTreeSet(); diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java b/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java index 7c7625770b14f..7eee3c591877e 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java @@ -33,6 +33,7 @@ import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.ServiceConfigurationUtils; import org.apache.pulsar.client.admin.PulsarAdmin; +import org.apache.pulsar.client.admin.PulsarAdminBuilder; import org.apache.pulsar.client.admin.PulsarAdminException; import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.ClusterData; @@ -332,11 +333,29 @@ public void start() throws Exception { createSampleNameSpace(clusterData, cluster); } else { URL webServiceUrlTls = new URL( - String.format("http://%s:%d", config.getAdvertisedAddress(), config.getWebServicePortTls().get())); + String.format("https://%s:%d", config.getAdvertisedAddress(), config.getWebServicePortTls().get())); String brokerServiceUrlTls = String.format("pulsar+ssl://%s:%d", config.getAdvertisedAddress(), config.getBrokerServicePortTls().get()); - admin = PulsarAdmin.builder().serviceHttpUrl(webServiceUrlTls.toString()).authentication( - config.getBrokerClientAuthenticationPlugin(), config.getBrokerClientAuthenticationParameters()).build(); + PulsarAdminBuilder builder = PulsarAdmin.builder() + .serviceHttpUrl(webServiceUrlTls.toString()) + .authentication( + config.getBrokerClientAuthenticationPlugin(), + config.getBrokerClientAuthenticationParameters()); + + // set trust store if needed. + if (config.isBrokerClientTlsEnabled()) { + if (config.isBrokerClientTlsEnabledWithKeyStore()) { + builder.useKeyStoreTls(true) + .tlsTrustStoreType(config.getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(config.getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(config.getBrokerClientTlsTrustStorePassword()); + } else { + builder.tlsTrustCertsFilePath(config.getBrokerClientTrustCertsFilePath()); + builder.allowTlsInsecureConnection(config.isTlsAllowInsecureConnection()); + } + } + + admin = builder.build(); ClusterData clusterData = new ClusterData(null, webServiceUrlTls.toString(), null, brokerServiceUrlTls); createSampleNameSpace(clusterData, cluster); } diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java index 4cc8879517b6e..6ce3930b9b447 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java @@ -524,9 +524,9 @@ public Boolean get() { final String bootstrapMessage = "bootstrap service " + (config.getWebServicePort().isPresent() ? "port = " + config.getWebServicePort().get() : "") - + (config.getWebServicePortTls().isPresent() ? "tls-port = " + config.getWebServicePortTls() : "") - + (config.getBrokerServicePort().isPresent() ? "broker url= " + brokerServiceUrl : "") - + (config.getBrokerServicePortTls().isPresent() ? "broker url= " + brokerServiceUrlTls : ""); + + (config.getWebServicePortTls().isPresent() ? ", tls-port = " + config.getWebServicePortTls() : "") + + (config.getBrokerServicePort().isPresent() ? ", broker url= " + brokerServiceUrl : "") + + (config.getBrokerServicePortTls().isPresent() ? ", broker tls url= " + brokerServiceUrlTls : ""); LOG.info("messaging service is ready"); LOG.info("messaging service is ready, {}, cluster={}, configs={}", bootstrapMessage, @@ -996,11 +996,11 @@ public synchronized PulsarAdmin getAdminClient() throws PulsarServerException { conf.getBrokerClientAuthenticationParameters()); if (conf.isBrokerClientTlsEnabled()) { - if (this.getConfiguration().isBrokerClientTlsEnabledWithKeyStore()) { + if (conf.isBrokerClientTlsEnabledWithKeyStore()) { builder.useKeyStoreTls(true) - .tlsTrustStoreType(this.getConfiguration().getBrokerClientTlsTrustStoreType()) - .tlsTrustStorePath(this.getConfiguration().getBrokerClientTlsTrustStore()) - .tlsTrustStorePassword(this.getConfiguration().getBrokerClientTlsTrustStorePassword()); + .tlsTrustStoreType(conf.getBrokerClientTlsTrustStoreType()) + .tlsTrustStorePath(conf.getBrokerClientTlsTrustStore()) + .tlsTrustStorePassword(conf.getBrokerClientTlsTrustStorePassword()); } else { builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java index 267798426bb15..62898e16aa689 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java +++ b/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java @@ -116,10 +116,6 @@ public void setup() throws Exception { conf.setBrokerClientTlsTrustStorePassword(BROKER_TRUSTSTORE_PW); super.init(); - - PulsarAdmin admin = buildAdminClient(); - admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); - admin.close(); } @AfterMethod @@ -169,6 +165,7 @@ PulsarAdmin buildAdminClient() throws Exception { @Test public void testSuperUserCanListTenants() throws Exception { try (PulsarAdmin admin = buildAdminClient()) { + admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); admin.tenants().createTenant("tenant1", new TenantInfo(ImmutableSet.of("foobar"), ImmutableSet.of("test"))); @@ -179,6 +176,7 @@ public void testSuperUserCanListTenants() throws Exception { @Test public void testSuperUserCantListNamespaces() throws Exception { try (PulsarAdmin admin = buildAdminClient()) { + admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); admin.tenants().createTenant("tenant1", new TenantInfo(ImmutableSet.of("proxy"), ImmutableSet.of("test"))); @@ -190,6 +188,7 @@ public void testSuperUserCantListNamespaces() throws Exception { @Test public void testAuthorizedUserAsOriginalPrincipal() throws Exception { try (PulsarAdmin admin = buildAdminClient()) { + admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); admin.tenants().createTenant("tenant1", new TenantInfo(ImmutableSet.of("proxy", "user1"), ImmutableSet.of("test"))); @@ -207,8 +206,8 @@ public void testAuthorizedUserAsOriginalPrincipal() throws Exception { public void testPersistentList() throws Exception { log.info("-- Starting {} test --", methodName); - /***** Broker 2 Started *****/ try (PulsarAdmin admin = buildAdminClient()) { + admin.clusters().createCluster("test", new ClusterData(brokerUrl.toString())); admin.tenants().createTenant("tenant1", new TenantInfo(ImmutableSet.of("foobar"), ImmutableSet.of("test"))); diff --git a/site2/docs/reference-configuration.md b/site2/docs/reference-configuration.md index 2ce29d5a54333..b1e893fd7c3c1 100644 --- a/site2/docs/reference-configuration.md +++ b/site2/docs/reference-configuration.md @@ -151,6 +151,18 @@ Pulsar brokers are responsible for handling incoming messages from producers, di |tlsAllowInsecureConnection| Accept untrusted TLS certificate from client |false| |tlsProtocols|Specify the tls protocols the broker will use to negotiate during TLS Handshake. Multiple values can be specified, separated by commas. Example:- ```TLSv1.2```, ```TLSv1.1```, ```TLSv1``` || |tlsCiphers|Specify the tls cipher the broker will use to negotiate during TLS Handshake. Multiple values can be specified, separated by commas. Example:- ```TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256```|| +|tlsEnabledWithKeyStore| Enable TLS with KeyStore type configuration in broker |false| +|tlsProvider| TLS Provider for KeyStore type || +|tlsKeyStoreType| LS KeyStore type configuration in broker: JKS, PKCS12 |JKS| +|tlsKeyStore| TLS KeyStore path in broker || +|tlsKeyStorePassword| TLS KeyStore password for broker || +|brokerClientTlsEnabledWithKeyStore| Whether internal client use KeyStore type to authenticate with Pulsar brokers |false| +|brokerClientSslProvider| The TLS Provider used by internal client to authenticate with other Pulsar brokers || +|brokerClientTlsTrustStoreType| TLS TrustStore type configuration for internal client: JKS, PKCS12, used by the internal client to authenticate with Pulsar brokers |JKS| +|brokerClientTlsTrustStore| TLS TrustStore path for internal client, used by the internal client to authenticate with Pulsar brokers || +|brokerClientTlsTrustStorePassword| TLS TrustStore password for internal client, used by the internal client to authenticate with Pulsar brokers || +|brokerClientTlsCiphers| Specify the tls cipher the internal client will use to negotiate during TLS Handshake. (a comma-separated list of ciphers) e.g. [TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]|| +|brokerClientTlsProtocols|Specify the tls protocols the broker will use to negotiate during TLS handshake. (a comma-separated list of protocol names). e.g. [TLSv1.2, TLSv1.1, TLSv1] || |ttlDurationDefaultInSeconds| The default ttl for namespaces if ttl is not configured at namespace policies. |0| |tokenSecretKey| Configure the secret key to be used to validate auth tokens. The key can be specified like: `tokenSecretKey=data:base64,xxxxxxxxx` or `tokenSecretKey=file:///my/secret.key`|| |tokenPublicKey| Configure the public key to be used to validate auth tokens. The key can be specified like: `tokenPublicKey=data:base64,xxxxxxxxx` or `tokenPublicKey=file:///my/secret.key`|| From dd8eecad657d6aa91e9642a7ed3243ecffac94aa Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Fri, 8 May 2020 00:47:18 +0800 Subject: [PATCH 09/11] fix config for insecurecon --- .../src/main/java/org/apache/pulsar/PulsarStandalone.java | 2 +- .../src/main/java/org/apache/pulsar/broker/PulsarService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java b/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java index 7eee3c591877e..be1b2762dd2f9 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/PulsarStandalone.java @@ -351,8 +351,8 @@ public void start() throws Exception { .tlsTrustStorePassword(config.getBrokerClientTlsTrustStorePassword()); } else { builder.tlsTrustCertsFilePath(config.getBrokerClientTrustCertsFilePath()); - builder.allowTlsInsecureConnection(config.isTlsAllowInsecureConnection()); } + builder.allowTlsInsecureConnection(config.isTlsAllowInsecureConnection()); } admin = builder.build(); diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java index 6ce3930b9b447..30d1dde60312c 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/PulsarService.java @@ -1003,8 +1003,8 @@ public synchronized PulsarAdmin getAdminClient() throws PulsarServerException { .tlsTrustStorePassword(conf.getBrokerClientTlsTrustStorePassword()); } else { builder.tlsTrustCertsFilePath(conf.getBrokerClientTrustCertsFilePath()); - builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); } + builder.allowTlsInsecureConnection(conf.isTlsAllowInsecureConnection()); } // most of the admin request requires to make zk-call so, keep the max read-timeout based on From a7955379127e830282e83149b3db32422b2fe19f Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Fri, 8 May 2020 10:28:18 +0800 Subject: [PATCH 10/11] add keystore config support for bin/pulsar-client --- conf/client.conf | 11 +++++++++ .../pulsar/admin/cli/PulsarAdminTool.java | 24 +++++++++++++++---- .../pulsar/client/cli/PulsarClientTool.java | 18 ++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/conf/client.conf b/conf/client.conf index 887785a78b368..597478e2baaea 100644 --- a/conf/client.conf +++ b/conf/client.conf @@ -56,3 +56,14 @@ tlsEnableHostnameVerification=false # fails, then the cert is untrusted and the connection is dropped. tlsTrustCertsFilePath= +# Enable TLS with KeyStore type configuration in broker. +useKeyStoreTls=false; + +# TLS KeyStore type configuration: JKS, PKCS12 +tlsTrustStoreType=JKS + +# TLS TrustStore path +tlsTrustStorePath= + +# TLS TrustStore password +tlsTrustStorePassword= diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java index 148fd6b5f5baa..ccc28547d9200 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java @@ -53,16 +53,22 @@ public class PulsarAdminTool { @Parameter(names = { "--tls-allow-insecure" }, description = "Allow TLS insecure connection") Boolean tlsAllowInsecureConnection; - + @Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS trust cert file path") String tlsTrustCertsFilePath; - + @Parameter(names = { "--tls-enable-hostname-verification" }, description = "Enable TLS common name verification") Boolean tlsEnableHostnameVerification; @Parameter(names = { "-h", "--help", }, help = true, description = "Show this help.") boolean help; + // for tls with keystore type config + boolean useKeyStoreTls = false; + String tlsTrustStoreType = "JKS"; + String tlsTrustStorePath = null; + String tlsTrustStorePassword = null; + PulsarAdminTool(Properties properties) throws Exception { // fallback to previous-version serviceUrl property to maintain backward-compatibility serviceUrl = StringUtils.isNotBlank(properties.getProperty("webServiceUrl")) @@ -80,9 +86,19 @@ public class PulsarAdminTool { ? this.tlsTrustCertsFilePath : properties.getProperty("tlsTrustCertsFilePath"); + this.useKeyStoreTls = Boolean + .parseBoolean(properties.getProperty("useKeyStoreTls", "false")); + this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", "JKS"); + this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath"); + this.tlsTrustStorePassword = properties.getProperty("tlsTrustStorePassword"); + adminBuilder = PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection) .enableTlsHostnameVerification(tlsEnableHostnameVerification) - .tlsTrustCertsFilePath(tlsTrustCertsFilePath); + .tlsTrustCertsFilePath(tlsTrustCertsFilePath) + .useKeyStoreTls(useKeyStoreTls) + .tlsTrustStoreType(tlsTrustStoreType) + .tlsTrustStorePath(tlsTrustStorePath) + .tlsTrustStorePassword(tlsTrustStorePassword); jcommander = new JCommander(); jcommander.setProgramName("pulsar-admin"); @@ -108,7 +124,7 @@ public class PulsarAdminTool { commandMap.put("resource-quotas", CmdResourceQuotas.class); // pulsar-proxy cli commandMap.put("proxy-stats", CmdProxyStats.class); - + commandMap.put("functions", CmdFunctions.class); commandMap.put("functions-worker", CmdFunctionWorker.class); commandMap.put("sources", CmdSources.class); diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java index 2c38b345de367..b86bc79f5c1dc 100644 --- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java +++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java @@ -60,6 +60,12 @@ public class PulsarClientTool { boolean tlsEnableHostnameVerification = false; String tlsTrustCertsFilePath = null; + // for tls with keystore type config + boolean useKeyStoreTls = false; + String tlsTrustStoreType = "JKS"; + String tlsTrustStorePath = null; + String tlsTrustStorePassword = null; + JCommander commandParser; CmdProduce produceCommand; CmdConsume consumeCommand; @@ -79,6 +85,12 @@ public PulsarClientTool(Properties properties) { .parseBoolean(properties.getProperty("tlsEnableHostnameVerification", "false")); this.tlsTrustCertsFilePath = properties.getProperty("tlsTrustCertsFilePath"); + this.useKeyStoreTls = Boolean + .parseBoolean(properties.getProperty("useKeyStoreTls", "false")); + this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", "JKS"); + this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath"); + this.tlsTrustStorePassword = properties.getProperty("tlsTrustStorePassword"); + produceCommand = new CmdProduce(); consumeCommand = new CmdConsume(); @@ -99,6 +111,12 @@ private void updateConfig() throws UnsupportedAuthenticationException { clientBuilder.allowTlsInsecureConnection(this.tlsAllowInsecureConnection); clientBuilder.tlsTrustCertsFilePath(this.tlsTrustCertsFilePath); clientBuilder.serviceUrl(serviceURL); + + clientBuilder.useKeyStoreTls(useKeyStoreTls) + .tlsTrustStoreType(tlsTrustStoreType) + .tlsTrustStorePath(tlsTrustStorePath) + .tlsTrustStorePassword(tlsTrustStorePassword); + this.produceCommand.updateConfig(clientBuilder, authentication, this.serviceURL); this.consumeCommand.updateConfig(clientBuilder, authentication, this.serviceURL); } From d6584acd0fc92189fdebac7cdaaab364c1be35f4 Mon Sep 17 00:00:00 2001 From: Jia Zhai Date: Fri, 8 May 2020 14:31:41 +0800 Subject: [PATCH 11/11] mv AuthenticationKeyStoreTls into pulsar-client module --- distribution/server/pom.xml | 6 -- pom.xml | 2 - .../impl/AdminApiKeyStoreTlsAuthTest.java | 16 +-- ...yStoreTlsProducerConsumerTestWithAuth.java | 16 +-- ...oreTlsProducerConsumerTestWithoutAuth.java | 16 +-- .../pulsar/client/impl}/KeyStoreTlsTest.java | 14 ++- .../keystoretls}/broker.keystore.jks | Bin .../keystoretls}/broker.truststore.jks | Bin .../keystoretls}/brokerKeyStorePW.txt | 0 .../keystoretls}/brokerTrustStorePW.txt | 0 .../keystoretls}/client.keystore.jks | Bin .../keystoretls}/client.truststore.jks | Bin .../keystoretls}/clientKeyStorePW.txt | 0 .../keystoretls}/clientTrustStorePW.txt | 0 pulsar-client-auth-keystoretls/pom.xml | 95 ------------------ .../auth/AuthenticationDataKeyStoreTls.java | 0 .../impl/auth/AuthenticationKeyStoreTls.java | 0 .../server/ProxyKeyStoreTlsTestWithAuth.java | 18 ++-- .../ProxyKeyStoreTlsTestWithoutAuth.java | 18 ++-- .../keystoretls/broker.keystore.jks | Bin 0 -> 2767 bytes .../keystoretls/broker.truststore.jks | Bin 0 -> 731 bytes .../keystoretls/brokerKeyStorePW.txt | 1 + .../keystoretls/brokerTrustStorePW.txt | 1 + .../keystoretls/client.keystore.jks | Bin 0 -> 2767 bytes .../keystoretls/client.truststore.jks | Bin 0 -> 731 bytes .../keystoretls/clientKeyStorePW.txt | 1 + .../keystoretls/clientTrustStorePW.txt | 1 + 27 files changed, 63 insertions(+), 142 deletions(-) rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java => pulsar-broker/src/test/java/org/apache/pulsar/client/impl/AdminApiKeyStoreTlsAuthTest.java (93%) rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java => pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithAuth.java (93%) rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java => pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithoutAuth.java (93%) rename {pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client => pulsar-broker/src/test/java/org/apache/pulsar/client/impl}/KeyStoreTlsTest.java (81%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/broker.keystore.jks (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/broker.truststore.jks (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/brokerKeyStorePW.txt (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/brokerTrustStorePW.txt (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/client.keystore.jks (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/client.truststore.jks (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/clientKeyStorePW.txt (100%) rename {pulsar-client-auth-keystoretls/src/test/resources => pulsar-broker/src/test/resources/authentication/keystoretls}/clientTrustStorePW.txt (100%) delete mode 100644 pulsar-client-auth-keystoretls/pom.xml rename {pulsar-client-auth-keystoretls => pulsar-client}/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java (100%) rename {pulsar-client-auth-keystoretls => pulsar-client}/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java (100%) rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java => pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithAuth.java (92%) rename pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java => pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithoutAuth.java (91%) create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/broker.keystore.jks create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/broker.truststore.jks create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/client.keystore.jks create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/client.truststore.jks create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt create mode 100644 pulsar-proxy/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt diff --git a/distribution/server/pom.xml b/distribution/server/pom.xml index 99701e3cdcccc..9942b7f2224f5 100644 --- a/distribution/server/pom.xml +++ b/distribution/server/pom.xml @@ -64,12 +64,6 @@ ${project.version} - - org.apache.pulsar - pulsar-client-auth-keystoretls - ${project.version} - - org.apache.pulsar pulsar-client-tools diff --git a/pom.xml b/pom.xml index 949155f9937e1..ddeda8fc081da 100644 --- a/pom.xml +++ b/pom.xml @@ -108,8 +108,6 @@ flexible messaging model and an intuitive client API. pulsar-broker-auth-sasl pulsar-client-auth-sasl - pulsar-client-auth-keystoretls - pulsar-transaction diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/AdminApiKeyStoreTlsAuthTest.java similarity index 93% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java rename to pulsar-broker/src/test/java/org/apache/pulsar/client/impl/AdminApiKeyStoreTlsAuthTest.java index 62898e16aa689..ab2833dfbd00f 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/AdminApiTlsAuthTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/AdminApiKeyStoreTlsAuthTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.client.impl; import static org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls.mapToString; import static org.testng.Assert.fail; @@ -56,14 +56,18 @@ import org.testng.annotations.Test; @Slf4j -public class AdminApiTlsAuthTest extends ProducerConsumerBase { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; +public class AdminApiKeyStoreTlsAuthTest extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithAuth.java similarity index 93% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java rename to pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithAuth.java index abaf10dc88525..14177e092321d 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithAuth.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithAuth.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.client.impl; import static org.mockito.Mockito.spy; @@ -48,14 +48,18 @@ // TLS authentication and authorization based on KeyStore type config. @Slf4j -public class TlsProducerConsumerTestWithAuth extends ProducerConsumerBase { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; +public class KeyStoreTlsProducerConsumerTestWithAuth extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithoutAuth.java similarity index 93% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java rename to pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithoutAuth.java index 614c428eff6e9..c95f3dfa5db78 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/TlsProducerConsumerTestWithoutAuth.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsProducerConsumerTestWithoutAuth.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.client.impl; import static org.mockito.Mockito.spy; @@ -48,14 +48,18 @@ // TLS test without authentication and authorization based on KeyStore type config. @Slf4j -public class TlsProducerConsumerTestWithoutAuth extends ProducerConsumerBase { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; +public class KeyStoreTlsProducerConsumerTestWithoutAuth extends ProducerConsumerBase { + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsTest.java similarity index 81% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java rename to pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsTest.java index 60bc8c4128e02..0f9993dd81f5c 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/KeyStoreTlsTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/KeyStoreTlsTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.client.impl; import static org.apache.pulsar.common.util.SecurityUtility.getProvider; @@ -28,13 +28,17 @@ public class KeyStoreTlsTest { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; protected final String KEYSTORE_TYPE = "JKS"; diff --git a/pulsar-client-auth-keystoretls/src/test/resources/broker.keystore.jks b/pulsar-broker/src/test/resources/authentication/keystoretls/broker.keystore.jks similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/broker.keystore.jks rename to pulsar-broker/src/test/resources/authentication/keystoretls/broker.keystore.jks diff --git a/pulsar-client-auth-keystoretls/src/test/resources/broker.truststore.jks b/pulsar-broker/src/test/resources/authentication/keystoretls/broker.truststore.jks similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/broker.truststore.jks rename to pulsar-broker/src/test/resources/authentication/keystoretls/broker.truststore.jks diff --git a/pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt b/pulsar-broker/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/brokerKeyStorePW.txt rename to pulsar-broker/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt diff --git a/pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt b/pulsar-broker/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/brokerTrustStorePW.txt rename to pulsar-broker/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt diff --git a/pulsar-client-auth-keystoretls/src/test/resources/client.keystore.jks b/pulsar-broker/src/test/resources/authentication/keystoretls/client.keystore.jks similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/client.keystore.jks rename to pulsar-broker/src/test/resources/authentication/keystoretls/client.keystore.jks diff --git a/pulsar-client-auth-keystoretls/src/test/resources/client.truststore.jks b/pulsar-broker/src/test/resources/authentication/keystoretls/client.truststore.jks similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/client.truststore.jks rename to pulsar-broker/src/test/resources/authentication/keystoretls/client.truststore.jks diff --git a/pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt b/pulsar-broker/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/clientKeyStorePW.txt rename to pulsar-broker/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt diff --git a/pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt b/pulsar-broker/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt similarity index 100% rename from pulsar-client-auth-keystoretls/src/test/resources/clientTrustStorePW.txt rename to pulsar-broker/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt diff --git a/pulsar-client-auth-keystoretls/pom.xml b/pulsar-client-auth-keystoretls/pom.xml deleted file mode 100644 index d159062e42df2..0000000000000 --- a/pulsar-client-auth-keystoretls/pom.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - 4.0.0 - - - org.apache.pulsar - pulsar - 2.6.0-SNAPSHOT - .. - - - pulsar-client-auth-keystoretls - jar - TLS authentication plugin with keystore type for java client - - - - - ${project.groupId} - pulsar-client-original - ${project.parent.version} - true - - - - com.google.guava - guava - - - - org.apache.commons - commons-lang3 - - - - org.projectlombok - lombok - - - - javax.ws.rs - javax.ws.rs-api - - - - org.apache.pulsar - testmocks - ${project.version} - test - - - - ${project.groupId} - pulsar-broker - ${project.version} - test-jar - test - - - - ${project.groupId} - pulsar-broker - ${project.version} - test - - - - ${project.groupId} - pulsar-proxy - ${project.version} - test - - - - diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java similarity index 100% rename from pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java rename to pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationDataKeyStoreTls.java diff --git a/pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java similarity index 100% rename from pulsar-client-auth-keystoretls/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java rename to pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationKeyStoreTls.java diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithAuth.java similarity index 92% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java rename to pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithAuth.java index 3b85a52bf740c..b08201956f71c 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithAuth.java +++ b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithAuth.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.proxy.server; import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.Mockito.doReturn; @@ -43,8 +43,6 @@ import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; import org.apache.pulsar.common.policies.data.TenantInfo; -import org.apache.pulsar.proxy.server.ProxyConfiguration; -import org.apache.pulsar.proxy.server.ProxyService; import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -52,14 +50,18 @@ import org.testng.annotations.Test; @Slf4j -public class ProxyTlsTestWithAuth extends MockedPulsarServiceBaseTest { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; +public class ProxyKeyStoreTlsTestWithAuth extends MockedPulsarServiceBaseTest { + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; diff --git a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithoutAuth.java similarity index 91% rename from pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java rename to pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithoutAuth.java index 1606412c8ed35..b920ef48390e2 100644 --- a/pulsar-client-auth-keystoretls/src/test/java/org/apache/pulsar/client/ProxyTlsTestWithoutAuth.java +++ b/pulsar-proxy/src/test/java/org/apache/pulsar/proxy/server/ProxyKeyStoreTlsTestWithoutAuth.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.pulsar.client; +package org.apache.pulsar.proxy.server; import static com.google.common.base.Preconditions.checkNotNull; import static org.mockito.Mockito.doReturn; @@ -39,8 +39,6 @@ import org.apache.pulsar.client.impl.auth.AuthenticationKeyStoreTls; import org.apache.pulsar.common.configuration.PulsarConfigurationLoader; import org.apache.pulsar.common.policies.data.TenantInfo; -import org.apache.pulsar.proxy.server.ProxyConfiguration; -import org.apache.pulsar.proxy.server.ProxyService; import org.mockito.Mockito; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -48,14 +46,18 @@ import org.testng.annotations.Test; @Slf4j -public class ProxyTlsTestWithoutAuth extends MockedPulsarServiceBaseTest { - protected final String BROKER_KEYSTORE_FILE_PATH = "./src/test/resources/broker.keystore.jks"; - protected final String BROKER_TRUSTSTORE_FILE_PATH = "./src/test/resources/broker.truststore.jks"; +public class ProxyKeyStoreTlsTestWithoutAuth extends MockedPulsarServiceBaseTest { + protected final String BROKER_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.keystore.jks"; + protected final String BROKER_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/broker.truststore.jks"; protected final String BROKER_KEYSTORE_PW = "111111"; protected final String BROKER_TRUSTSTORE_PW = "111111"; - protected final String CLIENT_KEYSTORE_FILE_PATH = "./src/test/resources/client.keystore.jks"; - protected final String CLIENT_TRUSTSTORE_FILE_PATH = "./src/test/resources/client.truststore.jks"; + protected final String CLIENT_KEYSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.keystore.jks"; + protected final String CLIENT_TRUSTSTORE_FILE_PATH = + "./src/test/resources/authentication/keystoretls/client.truststore.jks"; protected final String CLIENT_KEYSTORE_PW = "111111"; protected final String CLIENT_TRUSTSTORE_PW = "111111"; diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/broker.keystore.jks b/pulsar-proxy/src/test/resources/authentication/keystoretls/broker.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..b4fec69ac2361e28da306de053e544c900fb18dc GIT binary patch literal 2767 zcmeH|c{J2}AIE>c!7wHdVo*sLaxHOw#-8!Sl_I(fVz{`JU72Ld5;G%_eZ2_TT}zhC zxUyF$V;M|#w@{YJk|L6=J39Bdx2Jo~^Z#?s{p0(`_jSJ8`F!8!_1RnATLu6C_UC|K zcDPO;_y7Rt^=KV24{+cWR3Hu3iY$l>ONYoH5rzQL9epD>IDXECc;E=x+xoXZ@_-NgxOZwkX@k!o~uhv;0e3e@a-s8fiem0YBQn1%jZU6jJ6}{W%^y|>z zn%{=hIt^zo_m=Kekz>Sw%R}K|A7oWl<3#>D{SyX=11fE&qruV!W#g<)7J7 zC>rc~*78`N8NXQ7;-Csiv@!KfpUi~g@bo*$ApO>IuJ+2vurp2@n8@#4eQ3tPUg8Zh z^lN%cE;JY$2!LN#h*fk}g+L&gG8_~CMkCSNBS^kODOg+2Coe-wG<>=9m(}>J9ir1} z_s|_h$LXRzUQINGbb0MD=( zYLpIqvPLK#)p2REu8zNOKxdHAnRjcfIglHs!wAIBsf0ZAf-sWeR!k4=E>9CKp(rsb)uRi?_2;mSzg+=hH5G{@_4}wHN4HVX68BB z_`l?I%Ga@^%&Rb%@t;}o1p&kr!exgmt_1Ht((7l(q6w_<#6bUobIZX&c2E|^53*9l zP6lsy#v4odQp_dO9-pf{!P$#>QEig>l)W_M%CTZ(C64!xIZTl_NSF29m z3T}3@(ObMmQHXl!oTGI9tz?B_4?UaDPFHDNbIi<)Q)(aVrEb{nT5u(V_idRcEq#=G z1%%+HhVJJ+&=5&cs93X#O~4%g%@j9tmS$ujH&Z+rYA@qZwkye(a-su)O52qu$q4+a z+4e#YSUHhl{lT(@i!SKv+$;3@-ksiYPxqyP>o4r{YF4KS;`m4UZr|NkPRi^te{{jJ~NkBuMDe?E**>*_+py>ScNSaBD2M_%;L|oN&(NpL_9-t>Fa=V{BNsZl+^#* zD*FKPRODm*_!kXvUi9?vB>4YHxCFi>oPQ(SeL)ioR;4>HsNV7MIM@b-iYLe>7`HQ!Z z@9?7Q`%n8gof|%!sr9nEsoX~Op=EtQN>$_5sRi;UOCzKfC)ovOWf|8nXk|R-#mPq^ zQVoQ>B$!6hLIQPt2uw_8@ibN_FxosgR(h(SQcR9g?3?IW5Q^LqSGn|l`%)k%so7Kb zzNG}0aHk|}a@u+}2c8t|Y)1^bc)}4h(%kDakYyu<1N-k7I8uT#WQ45gYq-~r`W9yS zFE4J}Xz6O`>4tj0&635Q%c<5jqN63onxQU%LAbYbkvxat^s+S)p#}{%ytw|r(1a2o z0jn(1YVsOd8R#$5#doBe8$Q1hN`1aqKVI5U#}IayXy@m~_@OJ6ga(axxmO|f4-Zp$1%&aqeAP5aULhB2WVeN|AL zFJ-)Tf@`5M@|wt@n)2KqHR$Vo?rV$L9e$gJN-k#}Bp&3Dd^OiGvFXwZN6~fFyV~ip z;#rsy&+IFw<6vD-OzW1UUi)51e%r|7sqB>olLVTiF7CiNLMbH{>#933P*CxJy6Bp@ z?V3tC5+VFzMKvY>X@DHN(`X+%l()B0i1)lJ_(Q-7&kg)}-O4F$u)JCa9(N|zLN3;J r`3UWJKf1f;+CM%2pPv7J(DPsCSENGW!FJm!fAA-4f18*?ZNn=n&yo`HfmuaSX)iIIh&v5}#H zL6ii)ks(OHzyvCQuAzxh3E4`f0myAd&vDNhf4^vvg_Pxt+NZAr-l+`dtw?`)`zqlh-ZS$1w6&GG^iI`M;Fn4thHd_Rgw(x$*s@}ghG59Limdcdw)cHT(8I{X8U%z0p zS97na_{2nq_K)k9F6Er`!Ro6Nlefr;L)5{xyY)yEM-gxV}k0$;8aaz=#|| zz(5Cv5F^9n&DVPRUs_Hq&aT!yYFy*sTC#AatxW6N6U#sK&-v4k`swu5PQ#RkH|I)l z$w^HK*i@VJV}66hVnI!V%Zp&|eY18p97oRvZ-mqDq^R4FT;Vqj# zuo}xf&)Q>nDX_YE-c*URV&_EN=1gsz8gRY7&4^w3iob3~)+!IF?!wcHBQAwq-K|k| z=yU$E8;~e@`979R+uTO!vHAEwS`}k8QQGQYiopRWjuO literal 0 HcmV?d00001 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt b/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt b/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-proxy/src/test/resources/authentication/keystoretls/brokerTrustStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/client.keystore.jks b/pulsar-proxy/src/test/resources/authentication/keystoretls/client.keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..499c8bec41b32febb3846428d27294e2b77ef487 GIT binary patch literal 2767 zcmeH|c{J4PAIHDn!Avu*EE8HRC1h`A3|YnvmFOnMHn=6*7)zOq8I#7nvseq)mPod- zMGGz?vbAVL2t$@g(Jewi4_9U%&1-zyI$!_m9tc&htLc^Ld{0`Ml5Tv-W!J zH2?sR{{+;>i$W$-0RRlS{Q>OP3Gy6S=EQ`*GhWm*zMA z=Ds{UCXkh_o1o)P4vjmeUVT2kwC5GiH8u3k$yygU4clxpkB@wI!BMMG&(qBC-um&1 zhaJ?t&zTWbqXRTs!c_63d-En0^QqgF?Nava*f-dNYtn2okWcY4kDV_sE{3PhnJj6rm zKd6wUUurH=i14=}D0e$bD^<+&8?Y4`(qDCJj`$9Yx3Z(ZGRuRJbtKXDloXX#IE zw~GkbQLp*%)PY%2J46Hoz^^MLEV{5lAdpr)kdXAsI3+ZQq085gFh4-$UNlpPe%*F? zAu0PKi@4D7=q%cId^l$_LTT%H`>LP3-{rB43N38pDeXg|a`U(==kT$$W|DgZRreQr ze=}zf;qrBsBVM%ds^`S?l_mol(E>apGBQ@Yc*S~a zd`L&3tH9+^gT|S=Z^>$T75^^|0F9k{FvC zc+*T{UN{VU%l5Q z=W!iAIQXK**oLGQF1oCpZ7IziC-Ijk=AyU}nV%QEd>9uR6hkU9QpIP9(U>03tR)qL z`W~l*JNczBhPbpV_QCyJ<)^J$-1T;Am`XHo+3<~gl*h=y%hTzj0r=^_hR2o_*MMd!{1m?)pjuYq<-Fa!v5qGZ8Wk*(`b5c%()*5-_^-}+#MQ{SL@)3_hW`) zCrI^Z%XU`+bng*5ChL3bi_*d)Yafr*P*0yGIX`(hjC(Ftud~%-uKU!l2VY&|d0TDC zj5&-uU0k=?*h3&QNwJ@zR5KX7_~+^ZT2P!044vZe2H7y*RzyH_8w;8~DU@WGm8SG##EE%GR6xZE{(<)&ckXexOBZFY5+gJ>bAG za~1rmn^~gkhgj*c)&sQ{jFq!7OCma?xgKPLGI%*XGA zcEV8SSZLTg95ud#$U{Wz#s};EGxPtM`G13%ZvuCi#Yc1=;KKCxfr5kC$Mb@Z{{c;5 BppgIo literal 0 HcmV?d00001 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/client.truststore.jks b/pulsar-proxy/src/test/resources/authentication/keystoretls/client.truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..8eaa06ba5812f2440651d87080cdbec252b596e7 GIT binary patch literal 731 zcmezO_TO6u1_mY|W(3o0$%#ez`6WPZ;epRJKN(mf^h^ybfhy)0G%?LEXku(&;$)bS zQrgbSI&H22FB_*;n@8JsUPeZ4Rt5uJLv903Hs(+kHesgZJOc%BULyko6C(>lVesM>v+U6gVk4!hI-SJi=<;YZ=G5%T0VQ<5}BF|w{ZFM!Gdz@Utck8 zl-WJ?<$2B9Dc2r;P^tFVznbI7x{hX-)Ne)|P8lU9{c8#pcWIt0aD9`0l8Kp-fe|@` zfPoGSAx4JDo3HiszqFiKoL#MZ)VRjMwPfK;Tbb6kCzgNepYx|7_0#FAorWn7Z_btA zl9QSeu&Flb$NUD1#g3lhMJI2th<&tK)fL#j-Im|{(x&5IEBPd8n|rIcm>e&)I&y~3H4AtX=O%Huw|!pLP7j?f(a+g3 zXDgSA+f41)hmI|%Dv5jjwZUA4|5;_zWL=HYOMb1 rQ)9~Sv8OI_4B!0R^wNv|{|gnRGCu5TJ*a(QhWm!M^WR@jJN^~`qDeO@ literal 0 HcmV?d00001 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt b/pulsar-proxy/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-proxy/src/test/resources/authentication/keystoretls/clientKeyStorePW.txt @@ -0,0 +1 @@ +111111 diff --git a/pulsar-proxy/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt b/pulsar-proxy/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt new file mode 100644 index 0000000000000..90d2950097fa1 --- /dev/null +++ b/pulsar-proxy/src/test/resources/authentication/keystoretls/clientTrustStorePW.txt @@ -0,0 +1 @@ +111111