This is an automated email from the ASF dual-hosted git repository.
szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git
The following commit(s) were added to refs/heads/master by this push:
new da5d868 RATIS-1542. Support TlsConf in netty streaming. (#619)
da5d868 is described below
commit da5d86892b20039c0d514686d834cc4298e6f4cd
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Wed Mar 16 21:58:43 2022 +0800
RATIS-1542. Support TlsConf in netty streaming. (#619)
---
.../java/org/apache/ratis/client/RaftClient.java | 4 +-
.../apache/ratis/client/impl/ClientImplUtils.java | 10 +-
.../apache/ratis/client/impl/RaftClientImpl.java | 5 +-
.../main/java/org/apache/ratis/conf/ConfUtils.java | 13 ++
.../apache/ratis/security/SecurityTestUtils.java | 65 ++++++
.../apache/ratis/grpc/MiniRaftClusterWithGrpc.java | 8 +-
.../org/apache/ratis/netty/NettyConfigKeys.java | 85 ++++---
.../apache/ratis/netty/NettyDataStreamFactory.java | 20 +-
.../java/org/apache/ratis/netty/NettyUtils.java | 13 +-
.../ratis/netty/client/NettyClientStreamRpc.java | 23 +-
...usterWithRpcTypeGrpcAndDataStreamTypeNetty.java | 25 ++-
...amSslWithRpcTypeGrpcAndDataStreamTypeNetty.java | 72 ++++++
.../apache/ratis/netty/TestTlsConfWithNetty.java | 250 +++++++++++++++++++++
ratis-test/src/test/resources/ssl/ca.crt | 46 ++++
ratis-test/src/test/resources/ssl/ca.key | 71 ++++++
ratis-test/src/test/resources/ssl/client.crt | 44 ++++
ratis-test/src/test/resources/ssl/client.csr | 43 ++++
ratis-test/src/test/resources/ssl/client.key | 68 ++++++
ratis-test/src/test/resources/ssl/client.pem | 69 ++++++
ratis-test/src/test/resources/ssl/generate.sh | 50 +++++
ratis-test/src/test/resources/ssl/server.crt | 44 ++++
ratis-test/src/test/resources/ssl/server.csr | 43 ++++
ratis-test/src/test/resources/ssl/server.key | 68 ++++++
ratis-test/src/test/resources/ssl/server.pem | 69 ++++++
24 files changed, 1130 insertions(+), 78 deletions(-)
diff --git a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
index 6529732..d4c2f16 100644
--- a/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
+++ b/ratis-client/src/main/java/org/apache/ratis/client/RaftClient.java
@@ -118,8 +118,8 @@ public interface RaftClient extends Closeable {
}
}
return ClientImplUtils.newRaftClient(clientId, group, leaderId,
primaryDataStreamServer,
- Objects.requireNonNull(clientRpc, "The 'clientRpc' field is not
initialized."),
- properties, retryPolicy);
+ Objects.requireNonNull(clientRpc, "The 'clientRpc' field is not
initialized."), retryPolicy,
+ properties, parameters);
}
/** Set {@link RaftClient} ID. */
diff --git
a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
index eeddb22..392dcd7 100644
---
a/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
+++
b/ratis-client/src/main/java/org/apache/ratis/client/impl/ClientImplUtils.java
@@ -21,6 +21,7 @@ import org.apache.ratis.client.DataStreamClient;
import org.apache.ratis.client.DataStreamClientRpc;
import org.apache.ratis.client.RaftClient;
import org.apache.ratis.client.RaftClientRpc;
+import org.apache.ratis.conf.Parameters;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.protocol.RaftGroup;
import org.apache.ratis.protocol.RaftGroupId;
@@ -31,11 +32,12 @@ import org.apache.ratis.protocol.RaftPeerId;
/** Client utilities for internal use. */
public interface ClientImplUtils {
+ @SuppressWarnings("checkstyle:ParameterNumber")
static RaftClient newRaftClient(ClientId clientId, RaftGroup group,
- RaftPeerId leaderId, RaftPeer primaryDataStreamServer, RaftClientRpc
clientRpc, RaftProperties properties,
- RetryPolicy retryPolicy) {
- return new RaftClientImpl(clientId, group, leaderId,
primaryDataStreamServer, clientRpc, properties,
- retryPolicy);
+ RaftPeerId leaderId, RaftPeer primaryDataStreamServer, RaftClientRpc
clientRpc, RetryPolicy retryPolicy,
+ RaftProperties properties, Parameters parameters) {
+ return new RaftClientImpl(clientId, group, leaderId,
primaryDataStreamServer, clientRpc, retryPolicy,
+ properties, parameters);
}
static DataStreamClient newDataStreamClient(ClientId clientId, RaftGroupId
groupId, RaftPeer primaryDataStreamServer,
diff --git
a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
index 22254be..75871bf 100644
---
a/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
+++
b/ratis-client/src/main/java/org/apache/ratis/client/impl/RaftClientImpl.java
@@ -24,6 +24,7 @@ import org.apache.ratis.client.api.DataStreamApi;
import org.apache.ratis.client.api.LeaderElectionManagementApi;
import org.apache.ratis.client.api.SnapshotManagementApi;
import org.apache.ratis.client.retry.ClientRetryEvent;
+import org.apache.ratis.conf.Parameters;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.proto.RaftProtos.SlidingWindowEntry;
import org.apache.ratis.protocol.ClientId;
@@ -146,8 +147,9 @@ public final class RaftClientImpl implements RaftClient {
private final ConcurrentMap<RaftPeerId, LeaderElectionManagementApi>
leaderElectionManagement = new ConcurrentHashMap<>();
+ @SuppressWarnings("checkstyle:ParameterNumber")
RaftClientImpl(ClientId clientId, RaftGroup group, RaftPeerId leaderId,
RaftPeer primaryDataStreamServer,
- RaftClientRpc clientRpc, RaftProperties properties, RetryPolicy
retryPolicy) {
+ RaftClientRpc clientRpc, RetryPolicy retryPolicy, RaftProperties
properties, Parameters parameters) {
this.clientId = clientId;
this.peers.set(group.getPeers());
this.groupId = group.getGroupId();
@@ -173,6 +175,7 @@ public final class RaftClientImpl implements RaftClient {
.setRaftGroupId(groupId)
.setDataStreamServer(primaryDataStreamServer)
.setProperties(properties)
+ .setParameters(parameters)
.build());
this.adminApi = JavaUtils.memoize(() -> new AdminImpl(this));
}
diff --git a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
index 1d7d9c5..e5b04c6 100644
--- a/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/conf/ConfUtils.java
@@ -17,6 +17,7 @@
*/
package org.apache.ratis.conf;
+import org.apache.ratis.security.TlsConf;
import org.apache.ratis.thirdparty.com.google.common.base.Objects;
import org.apache.ratis.util.NetUtils;
import org.apache.ratis.util.SizeInBytes;
@@ -35,6 +36,7 @@ import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
+import java.util.function.Function;
public interface ConfUtils {
Logger LOG = LoggerFactory.getLogger(ConfUtils.class);
@@ -198,6 +200,12 @@ public interface ConfUtils {
return value;
}
+ static TlsConf getTlsConf(
+ Function<String, TlsConf> tlsConfGetter,
+ String key, Consumer<String> logger) {
+ return get((k, d) -> tlsConfGetter.apply(k), key, null, logger);
+ }
+
@SafeVarargs
static <T> T get(BiFunction<String, T, T> getter,
String key, T defaultValue, Consumer<String> logger, BiConsumer<String,
T>... assertions) {
@@ -271,6 +279,11 @@ public interface ConfUtils {
set(timeDurationSetter, key, value, assertions);
}
+ static void setTlsConf(
+ BiConsumer<String, TlsConf> tlsConfSetter, String key, TlsConf value) {
+ set(tlsConfSetter, key, value);
+ }
+
@SafeVarargs
static <T> void set(
BiConsumer<String, T> setter, String key, T value,
diff --git
a/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java
b/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java
new file mode 100644
index 0000000..d35a38b
--- /dev/null
+++
b/ratis-common/src/test/java/org/apache/ratis/security/SecurityTestUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.ratis.security;
+
+import org.apache.ratis.security.TlsConf.Builder;
+import org.apache.ratis.security.TlsConf.CertificatesConf;
+import org.apache.ratis.security.TlsConf.PrivateKeyConf;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Optional;
+
+public interface SecurityTestUtils {
+ Logger LOG = LoggerFactory.getLogger(SecurityTestUtils.class);
+
+ ClassLoader CLASS_LOADER = SecurityTestUtils.class.getClassLoader();
+
+ static File getResource(String name) {
+ final File file = Optional.ofNullable(CLASS_LOADER.getResource(name))
+ .map(URL::getFile)
+ .map(File::new)
+ .orElse(null);
+ LOG.info("Getting resource {}: {}", name, file);
+ return file;
+ }
+
+ static TlsConf newServerTlsConfig(boolean mutualAuthn) {
+ LOG.info("newServerTlsConfig: mutualAuthn? {}", mutualAuthn);
+ return new Builder()
+ .setName("server")
+ .setPrivateKey(new PrivateKeyConf(getResource("ssl/server.pem")))
+ .setKeyCertificates(new
CertificatesConf(getResource("ssl/server.crt")))
+ .setTrustCertificates(new
CertificatesConf(getResource("ssl/client.crt")))
+ .setMutualTls(mutualAuthn)
+ .build();
+ }
+
+ static TlsConf newClientTlsConfig(boolean mutualAuthn) {
+ LOG.info("newClientTlsConfig: mutualAuthn? {}", mutualAuthn);
+ return new Builder()
+ .setName("client")
+ .setPrivateKey(new PrivateKeyConf(getResource("ssl/client.pem")))
+ .setKeyCertificates(new
CertificatesConf(getResource("ssl/client.crt")))
+ .setTrustCertificates(new CertificatesConf(getResource("ssl/ca.crt")))
+ .setMutualTls(mutualAuthn)
+ .build();
+ }
+}
\ No newline at end of file
diff --git
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
index 1528349..097a762 100644
---
a/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
+++
b/ratis-grpc/src/test/java/org/apache/ratis/grpc/MiniRaftClusterWithGrpc.java
@@ -41,7 +41,7 @@ public class MiniRaftClusterWithGrpc extends
MiniRaftCluster.RpcBase {
@Override
public MiniRaftClusterWithGrpc newCluster(String[] ids, RaftProperties
prop) {
RaftConfigKeys.Rpc.setType(prop, SupportedRpcType.GRPC);
- return new MiniRaftClusterWithGrpc(ids, prop);
+ return new MiniRaftClusterWithGrpc(ids, prop, null);
}
};
@@ -55,8 +55,8 @@ public class MiniRaftClusterWithGrpc extends
MiniRaftCluster.RpcBase {
public static final DelayLocalExecutionInjection sendServerRequestInjection =
new DelayLocalExecutionInjection(GrpcService.GRPC_SEND_SERVER_REQUEST);
- protected MiniRaftClusterWithGrpc(String[] ids, RaftProperties properties) {
- super(ids, properties, null);
+ protected MiniRaftClusterWithGrpc(String[] ids, RaftProperties properties,
Parameters parameters) {
+ super(ids, properties, parameters);
}
@Override
@@ -66,7 +66,7 @@ public class MiniRaftClusterWithGrpc extends
MiniRaftCluster.RpcBase {
GrpcConfigKeys.Client.setPort(properties,
NetUtils.createSocketAddr(address).getPort()));
Optional.ofNullable(getAddress(id, group,
RaftPeer::getAdminAddress)).ifPresent(address ->
GrpcConfigKeys.Admin.setPort(properties,
NetUtils.createSocketAddr(address).getPort()));
- return null;
+ return parameters;
}
@Override
diff --git
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
index e6fe6a2..fcd7eac 100644
--- a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
+++ b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyConfigKeys.java
@@ -17,7 +17,10 @@
*/
package org.apache.ratis.netty;
+import org.apache.ratis.conf.ConfUtils;
+import org.apache.ratis.conf.Parameters;
import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.security.TlsConf;
import org.apache.ratis.thirdparty.io.netty.util.NettyRuntime;
import org.apache.ratis.util.TimeDuration;
import org.slf4j.Logger;
@@ -52,7 +55,7 @@ public interface NettyConfigKeys {
}
interface DataStream {
- Logger LOG = LoggerFactory.getLogger(Server.class);
+ Logger LOG = LoggerFactory.getLogger(DataStream.class);
static Consumer<String> getDefaultLog() {
return LOG::info;
}
@@ -71,44 +74,62 @@ public interface NettyConfigKeys {
setInt(properties::setInt, PORT_KEY, port);
}
- String CLIENT_WORKER_GROUP_SIZE_KEY = PREFIX + ".client.worker-group.size";
- int CLIENT_WORKER_GROUP_SIZE_DEFAULT = Math.max(1,
NettyRuntime.availableProcessors() * 2);
-
- static int clientWorkerGroupSize(RaftProperties properties) {
- return getInt(properties::getInt, CLIENT_WORKER_GROUP_SIZE_KEY,
- CLIENT_WORKER_GROUP_SIZE_DEFAULT, getDefaultLog(), requireMin(1),
requireMax(65536));
- }
-
- static void setClientWorkerGroupSize(RaftProperties properties, int
clientWorkerGroupSize) {
- setInt(properties::setInt, CLIENT_WORKER_GROUP_SIZE_KEY,
clientWorkerGroupSize);
- }
-
- String CLIENT_WORKER_GROUP_SHARE_KEY = PREFIX +
".client.worker-group.share";
- boolean CLIENT_WORKER_GROUP_SHARE_DEFAULT = false;
+ interface Client {
+ String PREFIX = DataStream.PREFIX + ".client";
- static boolean clientWorkerGroupShare(RaftProperties properties) {
- return getBoolean(properties::getBoolean, CLIENT_WORKER_GROUP_SHARE_KEY,
- CLIENT_WORKER_GROUP_SHARE_DEFAULT, getDefaultLog());
- }
-
- static void setClientWorkerGroupShare(RaftProperties properties, boolean
clientWorkerGroupShare) {
- setBoolean(properties::setBoolean, CLIENT_WORKER_GROUP_SHARE_KEY,
clientWorkerGroupShare);
- }
+ String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
+ Class<TlsConf> TLS_CONF_CLASS = TlsConf.class;
+ static TlsConf tlsConf(Parameters parameters) {
+ return getTlsConf(key -> parameters.get(key, TLS_CONF_CLASS),
TLS_CONF_PARAMETER, getDefaultLog());
+ }
+ static void setTlsConf(Parameters parameters, TlsConf conf) {
+ LOG.info("setTlsConf " + conf);
+ ConfUtils.setTlsConf((key, value) -> parameters.put(key, value,
TLS_CONF_CLASS), TLS_CONF_PARAMETER, conf);
+ }
- String CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY = PREFIX +
".client.reply.queue.grace-period";
- TimeDuration CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT =
TimeDuration.ONE_SECOND;
+ String WORKER_GROUP_SIZE_KEY = PREFIX + ".worker-group.size";
+ int WORKER_GROUP_SIZE_DEFAULT = Math.max(1,
NettyRuntime.availableProcessors() * 2);
+ static int workerGroupSize(RaftProperties properties) {
+ return getInt(properties::getInt, WORKER_GROUP_SIZE_KEY,
+ WORKER_GROUP_SIZE_DEFAULT, getDefaultLog(), requireMin(1),
requireMax(65536));
+ }
+ static void setWorkerGroupSize(RaftProperties properties, int
clientWorkerGroupSize) {
+ setInt(properties::setInt, WORKER_GROUP_SIZE_KEY,
clientWorkerGroupSize);
+ }
- static TimeDuration clientReplyQueueGracePeriod(RaftProperties properties)
{
- return
getTimeDuration(properties.getTimeDuration(CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT.getUnit()),
- CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY,
CLIENT_REPLY_QUEUE_GRACE_PERIOD_DEFAULT, getDefaultLog());
- }
+ String WORKER_GROUP_SHARE_KEY = PREFIX + ".worker-group.share";
+ boolean WORKER_GROUP_SHARE_DEFAULT = false;
+ static boolean workerGroupShare(RaftProperties properties) {
+ return getBoolean(properties::getBoolean, WORKER_GROUP_SHARE_KEY,
+ WORKER_GROUP_SHARE_DEFAULT, getDefaultLog());
+ }
+ static void setWorkerGroupShare(RaftProperties properties, boolean
clientWorkerGroupShare) {
+ setBoolean(properties::setBoolean, WORKER_GROUP_SHARE_KEY,
clientWorkerGroupShare);
+ }
- static void setClientReplyQueueGracePeriod(RaftProperties properties,
TimeDuration timeoutDuration) {
- setTimeDuration(properties::setTimeDuration,
CLIENT_REPLY_QUEUE_GRACE_PERIOD_KEY, timeoutDuration);
+ String REPLY_QUEUE_GRACE_PERIOD_KEY = PREFIX +
".reply.queue.grace-period";
+ TimeDuration REPLY_QUEUE_GRACE_PERIOD_DEFAULT = TimeDuration.ONE_SECOND;
+ static TimeDuration replyQueueGracePeriod(RaftProperties properties) {
+ return
getTimeDuration(properties.getTimeDuration(REPLY_QUEUE_GRACE_PERIOD_DEFAULT.getUnit()),
+ REPLY_QUEUE_GRACE_PERIOD_KEY, REPLY_QUEUE_GRACE_PERIOD_DEFAULT,
getDefaultLog());
+ }
+ static void setReplyQueueGracePeriod(RaftProperties properties,
TimeDuration timeoutDuration) {
+ setTimeDuration(properties::setTimeDuration,
REPLY_QUEUE_GRACE_PERIOD_KEY, timeoutDuration);
+ }
}
interface Server {
- String PREFIX = NettyConfigKeys.PREFIX + ".server";
+ String PREFIX = DataStream.PREFIX + ".server";
+
+ String TLS_CONF_PARAMETER = PREFIX + ".tls.conf";
+ Class<TlsConf> TLS_CONF_CLASS = TlsConf.class;
+ static TlsConf tlsConf(Parameters parameters) {
+ return getTlsConf(key -> parameters.get(key, TLS_CONF_CLASS),
TLS_CONF_PARAMETER, getDefaultLog());
+ }
+ static void setTlsConf(Parameters parameters, TlsConf conf) {
+ LOG.info("setTlsConf " + conf);
+ ConfUtils.setTlsConf((key, value) -> parameters.put(key, value,
TLS_CONF_CLASS), TLS_CONF_PARAMETER, conf);
+ }
String USE_EPOLL_KEY = PREFIX + ".use-epoll";
boolean USE_EPOLL_DEFAULT = false;
diff --git
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
index 31bec8d..11aa051 100644
---
a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
+++
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyDataStreamFactory.java
@@ -17,29 +17,25 @@
*/
package org.apache.ratis.netty;
-import org.apache.ratis.client.DataStreamClientRpc;
import org.apache.ratis.client.DataStreamClientFactory;
+import org.apache.ratis.client.DataStreamClientRpc;
import org.apache.ratis.conf.Parameters;
import org.apache.ratis.conf.RaftProperties;
import org.apache.ratis.datastream.SupportedDataStreamType;
import org.apache.ratis.netty.client.NettyClientStreamRpc;
import org.apache.ratis.netty.server.NettyServerStreamRpc;
import org.apache.ratis.protocol.RaftPeer;
-import org.apache.ratis.security.TlsConf;
-import org.apache.ratis.server.DataStreamServerRpc;
import org.apache.ratis.server.DataStreamServerFactory;
+import org.apache.ratis.server.DataStreamServerRpc;
import org.apache.ratis.server.RaftServer;
+import java.util.Optional;
+
public class NettyDataStreamFactory implements DataStreamServerFactory,
DataStreamClientFactory {
- private final TlsConf tlsConf;
+ private final Parameters parameters;
public NettyDataStreamFactory(Parameters parameters) {
- //TODO: RATIS-1542: get TlsConf from parameters and add tls tests
- this((TlsConf) null);
- }
-
- private NettyDataStreamFactory(TlsConf tlsConf) {
- this.tlsConf = tlsConf;
+ this.parameters =
Optional.ofNullable(parameters).orElseGet(Parameters::new);
}
@Override
@@ -49,11 +45,11 @@ public class NettyDataStreamFactory implements
DataStreamServerFactory, DataStre
@Override
public DataStreamClientRpc newDataStreamClientRpc(RaftPeer server,
RaftProperties properties) {
- return new NettyClientStreamRpc(server, tlsConf, properties);
+ return new NettyClientStreamRpc(server,
NettyConfigKeys.DataStream.Client.tlsConf(parameters), properties);
}
@Override
public DataStreamServerRpc newDataStreamServerRpc(RaftServer server) {
- return new NettyServerStreamRpc(server, tlsConf);
+ return new NettyServerStreamRpc(server,
NettyConfigKeys.DataStream.Server.tlsConf(parameters));
}
}
diff --git a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
index 560605e..146e199 100644
--- a/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
+++ b/ratis-netty/src/main/java/org/apache/ratis/netty/NettyUtils.java
@@ -96,7 +96,7 @@ public interface NettyUtils {
}
static SslContext buildSslContextForServer(TlsConf tlsConf) {
- return buildSslContext(tlsConf, true,
NettyUtils::initSslContextBuilderForServer);
+ return buildSslContext("server", tlsConf,
NettyUtils::initSslContextBuilderForServer);
}
static SslContextBuilder initSslContextBuilderForClient(TlsConf tlsConf) {
@@ -109,18 +109,21 @@ public interface NettyUtils {
}
static SslContext buildSslContextForClient(TlsConf tlsConf) {
- return buildSslContext(tlsConf, false,
NettyUtils::initSslContextBuilderForClient);
+ return buildSslContext("client", tlsConf,
NettyUtils::initSslContextBuilderForClient);
}
- static SslContext buildSslContext(TlsConf tlsConf, boolean isServer,
Function<TlsConf, SslContextBuilder> builder) {
+ static SslContext buildSslContext(String name, TlsConf tlsConf,
Function<TlsConf, SslContextBuilder> builder) {
if (tlsConf == null) {
return null;
}
+ final SslContext sslContext;
try {
- return builder.apply(tlsConf).build();
+ sslContext = builder.apply(tlsConf).build();
} catch (Exception e) {
- final String message = "Failed to build a " + (isServer ? "server" :
"client") + " SslContext from " + tlsConf;
+ final String message = "Failed to buildSslContext for " + name + " from
" + tlsConf;
throw new IllegalArgumentException(message, e);
}
+ LOG.debug("buildSslContext for {} from {} returns {}", name, tlsConf,
sslContext.getClass().getName());
+ return sslContext;
}
}
\ No newline at end of file
diff --git
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
index b2714c7..dce9b63 100644
---
a/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
+++
b/ratis-netty/src/main/java/org/apache/ratis/netty/client/NettyClientStreamRpc.java
@@ -57,6 +57,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@@ -78,14 +79,14 @@ public class NettyClientStreamRpc implements
DataStreamClientRpc {
static EventLoopGroup newWorkerGroup(RaftProperties properties) {
return NettyUtils.newEventLoopGroup(
JavaUtils.getClassSimpleName(NettyClientStreamRpc.class) +
"-workerGroup",
- NettyConfigKeys.DataStream.clientWorkerGroupSize(properties), false);
+ NettyConfigKeys.DataStream.Client.workerGroupSize(properties),
false);
}
private final EventLoopGroup workerGroup;
private final boolean ignoreShutdown;
WorkerGroupGetter(RaftProperties properties) {
- if (NettyConfigKeys.DataStream.clientWorkerGroupShare(properties)) {
+ if (NettyConfigKeys.DataStream.Client.workerGroupShare(properties)) {
workerGroup = SHARED_WORKER_GROUP.updateAndGet(g -> g != null? g:
newWorkerGroup(properties));
ignoreShutdown = true;
} else {
@@ -142,14 +143,14 @@ public class NettyClientStreamRpc implements
DataStreamClientRpc {
static class Connection {
static final TimeDuration RECONNECT = TimeDuration.valueOf(100,
TimeUnit.MILLISECONDS);
- private final String address;
+ private final InetSocketAddress address;
private final WorkerGroupGetter workerGroup;
private final Supplier<ChannelInitializer<SocketChannel>>
channelInitializerSupplier;
/** The {@link ChannelFuture} is null when this connection is closed. */
private final AtomicReference<ChannelFuture> ref;
- Connection(String address, WorkerGroupGetter workerGroup,
+ Connection(InetSocketAddress address, WorkerGroupGetter workerGroup,
Supplier<ChannelInitializer<SocketChannel>>
channelInitializerSupplier) {
this.address = address;
this.workerGroup = workerGroup;
@@ -179,7 +180,7 @@ public class NettyClientStreamRpc implements
DataStreamClientRpc {
.channel(NioSocketChannel.class)
.handler(channelInitializerSupplier.get())
.option(ChannelOption.SO_KEEPALIVE, true)
- .connect(NetUtils.createSocketAddr(address))
+ .connect(address)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
@@ -236,12 +237,13 @@ public class NettyClientStreamRpc implements
DataStreamClientRpc {
public NettyClientStreamRpc(RaftPeer server, TlsConf tlsConf, RaftProperties
properties) {
this.name = JavaUtils.getClassSimpleName(getClass()) + "->" + server;
- this.replyQueueGracePeriod =
NettyConfigKeys.DataStream.clientReplyQueueGracePeriod(properties);
+ this.replyQueueGracePeriod =
NettyConfigKeys.DataStream.Client.replyQueueGracePeriod(properties);
+ final InetSocketAddress address =
NetUtils.createSocketAddr(server.getDataStreamAddress());
final SslContext sslContext = NettyUtils.buildSslContextForClient(tlsConf);
- this.connection = new Connection(server.getDataStreamAddress(),
+ this.connection = new Connection(address,
new WorkerGroupGetter(properties),
- () -> newChannelInitializer(sslContext, getClientHandler()));
+ () -> newChannelInitializer(address, sslContext, getClientHandler()));
}
private ChannelInboundHandler getClientHandler(){
@@ -306,13 +308,14 @@ public class NettyClientStreamRpc implements
DataStreamClientRpc {
};
}
- static ChannelInitializer<SocketChannel> newChannelInitializer(SslContext
sslContext, ChannelInboundHandler handler) {
+ static ChannelInitializer<SocketChannel> newChannelInitializer(
+ InetSocketAddress address, SslContext sslContext, ChannelInboundHandler
handler) {
return new ChannelInitializer<SocketChannel>(){
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslContext != null) {
- p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+ p.addLast("ssl", sslContext.newHandler(ch.alloc(),
address.getHostName(), address.getPort()));
}
p.addLast(newEncoder());
p.addLast(newEncoderDataStreamRequestFilePositionCount());
diff --git
a/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
b/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
index f5e97c1..1a210d8 100644
---
a/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
+++
b/ratis-test/src/test/java/org/apache/ratis/datastream/MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.java
@@ -17,6 +17,7 @@
*/
package org.apache.ratis.datastream;
+import org.apache.ratis.security.TlsConf;
import org.apache.ratis.server.impl.MiniRaftCluster;
import org.apache.ratis.RaftConfigKeys;
import org.apache.ratis.conf.Parameters;
@@ -31,25 +32,33 @@ import org.apache.ratis.rpc.SupportedRpcType;
* A {@link MiniRaftCluster} with {{@link SupportedRpcType#GRPC}} and {@link
SupportedDataStreamType#NETTY}.
*/
public class MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty extends
MiniRaftClusterWithGrpc {
- public static final
Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> FACTORY
- = new Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>() {
+ static class Factory extends
MiniRaftCluster.Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> {
+ private final Parameters parameters;
+
+ Factory(Parameters parameters) {
+ this.parameters = parameters;
+ }
+
@Override
public MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty
newCluster(String[] ids, RaftProperties prop) {
RaftConfigKeys.Rpc.setType(prop, SupportedRpcType.GRPC);
RaftConfigKeys.DataStream.setType(prop, SupportedDataStreamType.NETTY);
- return new MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(ids,
prop);
+ return new MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(ids,
prop, parameters);
}
- };
+ }
+
+ public static final Factory FACTORY = new Factory(null);
- public interface FactoryGet extends
Factory.Get<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty> {
+ public interface FactoryGet extends
MiniRaftCluster.Factory.Get<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
{
@Override
- default Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
getFactory() {
+ default
MiniRaftCluster.Factory<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
getFactory() {
return FACTORY;
}
}
- private MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(String[] ids,
RaftProperties properties) {
- super(ids, properties);
+ private MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty(String[] ids,
RaftProperties properties,
+ Parameters parameters) {
+ super(ids, properties, parameters);
}
@Override
diff --git
a/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
new file mode 100644
index 0000000..4a69aa4
--- /dev/null
+++
b/ratis-test/src/test/java/org/apache/ratis/datastream/TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ratis.datastream;
+
+import org.apache.log4j.Level;
+import org.apache.ratis.conf.Parameters;
+import org.apache.ratis.netty.NettyConfigKeys;
+import org.apache.ratis.netty.NettyUtils;
+import org.apache.ratis.security.SecurityTestUtils;
+import org.apache.ratis.security.TlsConf;
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.Log4jUtils;
+import org.junit.Ignore;
+
+import java.util.function.Supplier;
+
+public class TestDataStreamSslWithRpcTypeGrpcAndDataStreamTypeNetty
+ extends
DataStreamAsyncClusterTests<MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty>
{
+ {
+ Log4jUtils.setLogLevel(NettyUtils.LOG, Level.DEBUG);
+ }
+
+ Parameters newParameters() {
+ final Parameters parameters = new Parameters();
+ final TlsConf serverTlsConfig = SecurityTestUtils.newServerTlsConfig(true);
+ NettyConfigKeys.DataStream.Server.setTlsConf(parameters, serverTlsConfig);
+ final TlsConf clientTlsConfig = SecurityTestUtils.newClientTlsConfig(true);
+ NettyConfigKeys.DataStream.Client.setTlsConf(parameters, clientTlsConfig);
+ return parameters;
+ }
+
+ private final Supplier<Parameters> parameters =
JavaUtils.memoize(this::newParameters);
+
+ public Parameters getParameters() {
+ return parameters.get();
+ }
+
+ @Override
+ public MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.Factory
getFactory() {
+ return new
MiniRaftClusterWithRpcTypeGrpcAndDataStreamTypeNetty.Factory(getParameters());
+ }
+
+ @Ignore
+ @Override
+ public void testStreamWrites() {
+ }
+
+ @Ignore
+ @Override
+ public void testMultipleStreamsMultipleServers() {
+ }
+
+ @Ignore
+ @Override
+ public void testMultipleStreamsMultipleServersStepDownLeader() {
+ }
+}
diff --git
a/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
new file mode 100644
index 0000000..d33639c
--- /dev/null
+++ b/ratis-test/src/test/java/org/apache/ratis/netty/TestTlsConfWithNetty.java
@@ -0,0 +1,250 @@
+/*
+ * 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.ratis.netty;
+
+import org.apache.ratis.security.SecurityTestUtils;
+import org.apache.ratis.security.TlsConf;
+import org.apache.ratis.thirdparty.io.netty.bootstrap.Bootstrap;
+import org.apache.ratis.thirdparty.io.netty.bootstrap.ServerBootstrap;
+import org.apache.ratis.thirdparty.io.netty.buffer.ByteBuf;
+import org.apache.ratis.thirdparty.io.netty.buffer.Unpooled;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelFuture;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelHandlerContext;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelInboundHandler;
+import
org.apache.ratis.thirdparty.io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelInitializer;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelOption;
+import org.apache.ratis.thirdparty.io.netty.channel.ChannelPipeline;
+import org.apache.ratis.thirdparty.io.netty.channel.EventLoopGroup;
+import org.apache.ratis.thirdparty.io.netty.channel.nio.NioEventLoopGroup;
+import org.apache.ratis.thirdparty.io.netty.channel.socket.SocketChannel;
+import
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioServerSocketChannel;
+import
org.apache.ratis.thirdparty.io.netty.channel.socket.nio.NioSocketChannel;
+import org.apache.ratis.thirdparty.io.netty.handler.logging.LogLevel;
+import org.apache.ratis.thirdparty.io.netty.handler.logging.LoggingHandler;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.Closeable;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Testing {@link TlsConf} and the security related utility methods in {@link
NettyUtils}.
+ */
+public class TestTlsConfWithNetty {
+ private final static Logger LOG =
LoggerFactory.getLogger(TestTlsConfWithNetty.class);
+
+ static String buffer2String(ByteBuf buf){
+ try {
+ return buf.toString(StandardCharsets.UTF_8);
+ } finally {
+ buf.release();
+ }
+ }
+
+ static ByteBuf unpooledBuffer(String s) {
+ final ByteBuf buf = Unpooled.buffer();
+ buf.writeBytes(s.getBytes(StandardCharsets.UTF_8));
+ return buf;
+ }
+
+ static int randomPort() {
+ final int port = 50000 + ThreadLocalRandom.current().nextInt(10000);
+ LOG.info("randomPort: {}", port);
+ return port;
+ }
+
+ @Test
+ public void testNoSsl() throws Exception {
+ runTest(randomPort(), null, null);
+ }
+
+ @Test
+ public void testSsl() throws Exception {
+ final TlsConf serverTlsConfig = SecurityTestUtils.newServerTlsConfig(true);
+ final TlsConf clientTlsConfig = SecurityTestUtils.newClientTlsConfig(true);
+ runTest(randomPort(), serverTlsConfig, clientTlsConfig);
+ }
+
+ static void runTest(int port, TlsConf serverSslConf, TlsConf clientSslConf)
throws Exception {
+ final SslContext serverSslContext = serverSslConf == null? null
+ : NettyUtils.buildSslContextForServer(serverSslConf);
+ final SslContext clientSslContext = clientSslConf == null? null
+ : NettyUtils.buildSslContextForClient(clientSslConf);
+
+ final String message = "Hey, how are you?";
+ final String[] words = message.split(" ");
+ try (NettyTestServer server = new NettyTestServer(port, serverSslContext);
+ NettyTestClient client = new NettyTestClient("localhost", port,
clientSslContext)) {
+ final List<CompletableFuture<String>> replyFutures = new ArrayList<>();
+ for(String word : words) {
+ final ByteBuf buf = unpooledBuffer(word + " ");
+ final CompletableFuture<String> f = client.writeAndFlush(buf);
+ replyFutures.add(f);
+ }
+ for(int i = 0; i < replyFutures.size(); i++) {
+ final CompletableFuture<String> future = replyFutures.get(i);
+ final String reply = future.get(3, TimeUnit.SECONDS);
+ LOG.info(reply);
+ Assert.assertEquals(NettyTestServer.toReply(words[i]), reply);
+ }
+ }
+ }
+
+ static class NettyTestServer implements Closeable {
+ private static final Logger LOG =
LoggerFactory.getLogger(NettyTestServer.class);
+
+ private final EventLoopGroup bossGroup = new NioEventLoopGroup(3);
+ private final EventLoopGroup workerGroup = new NioEventLoopGroup(3);
+ private final ChannelFuture channelFuture;
+
+ public NettyTestServer(int port, SslContext sslContext) {
+ this.channelFuture = new ServerBootstrap()
+ .group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .handler(new LoggingHandler(getClass(), LogLevel.INFO))
+ .childHandler(newChannelInitializer(sslContext))
+ .bind(port)
+ .syncUninterruptibly();
+ }
+
+ private ChannelInitializer<SocketChannel> newChannelInitializer(SslContext
sslContext){
+ return new ChannelInitializer<SocketChannel>(){
+ @Override
+ public void initChannel(SocketChannel ch) {
+ final ChannelPipeline p = ch.pipeline();
+ if (sslContext != null) {
+ p.addLast("ssl", sslContext.newHandler(ch.alloc()));
+ }
+ p.addLast(newServerHandler());
+ }
+ };
+ }
+
+ private ChannelInboundHandler newServerHandler(){
+ return new ChannelInboundHandlerAdapter() {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object obj) {
+ if (obj instanceof ByteBuf) {
+ final String s = buffer2String((ByteBuf) obj);
+ LOG.info("channelRead: " + s);
+ for(String word : s.split(" ")) {
+ ctx.writeAndFlush(unpooledBuffer(toReply(word) + " "));
+ }
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable
throwable) {
+ LOG.error(NettyTestServer.this.getClass().getSimpleName() + ":
exceptionCaught", throwable);
+ ctx.close();
+ }
+ };
+ }
+
+ static String toReply(String request) {
+ return "[" + request + "]";
+ }
+
+
+ @Override
+ public void close() {
+ channelFuture.channel().close();
+ bossGroup.shutdownGracefully();
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+ static class NettyTestClient implements Closeable {
+ private static final Logger LOG =
LoggerFactory.getLogger(NettyTestClient.class);
+
+ private final EventLoopGroup workerGroup = new NioEventLoopGroup(3);
+ private final ChannelFuture channelFuture;
+
+
+ private final Queue<CompletableFuture<String>> queue = new LinkedList<>();
+
+ public NettyTestClient(String host, int port, SslContext sslContext) {
+ this.channelFuture = new Bootstrap()
+ .group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .handler(new LoggingHandler(getClass(), LogLevel.INFO))
+ .handler(newChannelInitializer(sslContext, host, port))
+ .option(ChannelOption.SO_KEEPALIVE, true)
+ .option(ChannelOption.TCP_NODELAY, true)
+ .connect(host, port)
+ .syncUninterruptibly();
+ }
+
+ public CompletableFuture<String> writeAndFlush(ByteBuf buf) {
+ final CompletableFuture<String> reply = new CompletableFuture<>();
+ queue.offer(reply);
+ channelFuture.channel().writeAndFlush(buf);
+ return reply;
+ }
+
+ private ChannelInitializer<SocketChannel> newChannelInitializer(SslContext
sslContext, String host, int port) {
+ return new ChannelInitializer<SocketChannel>(){
+ @Override
+ public void initChannel(SocketChannel ch) {
+ ChannelPipeline p = ch.pipeline();
+ if (sslContext != null) {
+ p.addLast("ssl", sslContext.newHandler(ch.alloc(), host, port));
+ }
+ p.addLast(getClientHandler());
+ }
+ };
+ }
+
+ private ChannelInboundHandler getClientHandler(){
+ return new ChannelInboundHandlerAdapter(){
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object obj) {
+ final String s = buffer2String((ByteBuf) obj);
+ LOG.info("received: " + s);
+ for(String word : s.split(" ")) {
+ queue.remove().complete(word);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable
cause) {
+ LOG.error(NettyTestClient.this.getClass().getSimpleName() + ":
exceptionCaught", cause);
+ ctx.close();
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+ channelFuture.channel().close();
+ workerGroup.shutdownGracefully();
+ }
+ }
+}
diff --git a/ratis-test/src/test/resources/ssl/ca.crt
b/ratis-test/src/test/resources/ssl/ca.crt
new file mode 100644
index 0000000..4e0519c
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/ca.crt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIFCTCCAvGgAwIBAgIUIVxtEYUqKP75yGl4BFak/NlORQEwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYw
+MjEzMDgzM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAsDabxsyPdRnF5J/xIo3K6QQA8wx2FKz04YzJQO6A03A2
+ICIHzWnDIcDxkCgmMS0lujq3Ai/2/uaeEiUsItajqboQ45b7C5LmPrc5qM1NTnHj
+AfxD6BZ2+8fbJfkRvLKrjvErck3zKIsMEdrHXbGYo68l34YwKLdlfkFThJox7mPY
+EqCDXYuRgI4eWZ5FFkAWZd2sOo6bdnsg+2+7Y9hawhqfn9RldNj7wyQJ5Pv/kWr2
+SzskoJENJMkxrgJfYfCn8E7jQzCP/XZmTEpW2dXJb3KMxrO5BN933pA687k7WniC
+Ft8E3zeKLV2LM6gijModOk4VCPLbw1+j8D3wt68lFfMWvWqS1YEizJvFDWPVC0RT
+JHk3k80Vcz91wFoAvcrpkLDmFrbdHHj2lRl3dpY+P0ymk9DGkDnK4DsrpZ2QyOzm
+0fT0BmMKvSqnNjxvGf0psrdjUZMiNuvE8+7YBwTyw/FUMEBtuY3S6MFnsrsK6vmO
+g1lRGRjuF0dVq86AYE1QPZQmB1ZiN8kIymN2bHpKPF7huLDKZ6YUzlXey8Ak4Osu
+AmoULN7O9lGB4kFXRy2Ll+KYWnP/T0YJS0e3UyQRtj6sNRIX+ckkWMRkEpvZR/+O
+ozg0Pu8pXQK2hTKiZxcvYUyXl10Db+0jkKjCXZqTFCRsibo9lOlcxY4ZCGd7fR8C
+AwEAAaNTMFEwHQYDVR0OBBYEFKs6E+inQbqexOtm8AB1YFsAtbcWMB8GA1UdIwQY
+MBaAFKs6E+inQbqexOtm8AB1YFsAtbcWMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBAF+igdCKtvKYNgP9yuvM31cpLPdAtvnG9uTv9YIFPrmjhMJ4
+80dZ5i0rboQsjrS45pQ5/Q+6vLCP+SI+tbtcQBuAIlNVdFztHpVrxbwLqom/nZGo
+kGGo1BgrSZbVtx40Rwl4oOC5HBqkoV1nkVe1SyDsDfe6yn8gZJz9KhjA+T5MHI8c
+COlTVsAKdP27yNlagXmI2VkZV3VtISAUCU9HlrIIIQyC14/Zcgd1n84hKAaQKT5U
+9VFOCvc/3sc69vG8CpyQNRmX4UDreUaAtU1EnZFUGUuHCQvB+a1baXp69dcjrG/6
+jFNL/5MTMf+UQ6xRGeKp0fB/i91Z2mfLsrAUl3rkTjm9MV1SJJUwTZ0qlh+5NmBS
+15+eJoHe3JRfOPTNabmnmtpGyrBVY+RPiUfOmQSq2HAejIUGjA+hUcDSCslhBtML
+/wxhqgggLlvDZ1d/zq8nnXRl4tpHYlaQICuBFZu2aElN5XcLi6Gt53J1yPJqd91l
+IgF5FzAmg74wpbzT8S3UN2Snhn/Ys8t68c3etOsGr8uiljn874WhQF0kvdDmfgx5
+BGAQyhXPfNnBRnD+tMiabwyBOUfOAN8+YGEh8LrJe+QWWPUHmoiEJd2LMdJFpCYD
+HEJvQjxZPlKDVCyG9kKkS1B3Fa+kaDVhR9aGWj9IWq33i+iyBIQe5VkJ+p+7
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/ca.key
b/ratis-test/src/test/resources/ssl/ca.key
new file mode 100644
index 0000000..3481897
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/ca.key
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,F9550AC98392E96B
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/client.crt
b/ratis-test/src/test/resources/ssl/client.crt
new file mode 100644
index 0000000..7081283
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.crt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYwMjEzMDgzM1owFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA066Z8xTn9sX2
+BqMv+jVD302CeOFyJPH5disAx+f5K73rhSSaq0itTXExR6w2vhI+Zj6hLIq+jjpg
+xWK7Ki9hvpkvnQdKT7CqllDHL//9PvEe75MZjWWAqeqfYc2rJ5cik4p/4nRp1ldP
+brTi8ziAEBmgRWZ7TDrEN07Myi1bII7EzuGD0c7JXGePQOCfQyu+gozFDyftQX+p
+wEtJ9P7VF53aqfy2dw0+Bt5F2cQmIIaeS/wQlM9brlvmuFEelo333kbtAi8CXv1K
+hoPGYAGXDHc/ldjatOHymhJ6WewvMalFgpFcq+r9hQvED0eEtvagD/baNLWLkSL9
+w9aBbdULzA08ERY1onr95+ZjXaEc8v2yKJwPLLy/AVdssEmtJFfQYiwh+HT31TYU
+QWe47w1E5RUTBMPonzmcyjsAAq8JEMA7my4g2Zaj4YmuRddD0vFJB627WGW089qr
+zo7qzQRvj6I/VNzyswaNuOG2rNKSwAuUQL0PgijlQvNnyHylUR0tlzU9yehQdYW+
+mmMGnv2eF7p+cFGQDjhELS8ua0+uVEFysLynzn1rZWKXz7RdIcCbugio5ORtXupp
+TOSSDq+ctYKMMR5kW65sBINFI3Vw+KhqYQXKmLKcyOKRP4IS7bFSMZwlHjasULn3
+fwVlzq/jN0Ahg4hRIgJCp+fAK0XhKKcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA
+QoM8w5JkeWqJsNr4s6s2tSXefvzHUWmqYGKd/GsBMkf0aL7Q2QxWPHM3jbP1E2EW
+gNLpqIyZ6w/mP6nC/ljcNoQH6bvGUdA/0mB+uOptERQwaoncvmff9X39GCqc65+b
+Cstm9gw+B3+RWnOLMcM87/khqkB5zN0nNyqqgPVZIjluJ2CsmbZUTnNV5ME+95XI
+OfhT5nm4+BQyPrmR5xIi2LtA5hqAGUF/lv+WbwqFRPhkOX0CVed+1r58sxHqP2mu
+vHpLNYmLLIcM3yCsOgs4QuGulHma5ysxUPQQlyf0e7iNoRkPZDcpb8JYNxOk/Qv8
+SN9s3KEewD6OQT/gpcaSh3vkfd0ZlPDs3ShSq2ywz1V6u0WW2rBTLOBYpqMkO0ku
+SOWdNbWck8vz+7qgDE2T7hW1o9XlG1yjFQeKei7Af+LnkjOJDkqZAnOyGVubY5Bd
+tuE+RpIInZCkQIEV5Du7eT7ncCUpLQGzl5Zpb6yFz03pXqL9HtZCwu1LF9vIbBFi
+4Q9smxUrim1WShis3vY0VsFzxFDi/RVrPTUy9oxIAwYW548IW/13xQZcIPgrYn3m
+q5mdPdS+NcywJt7NEN1CE8SV8D00eeCB8gkj/5DDZ+yj7A23uZ2+AFRi1GdiR5PH
+aGk8S4VRmummXTnMsW5FYzsdBr2xfGFyZ8V2PUqF5UA=
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/client.csr
b/ratis-test/src/test/resources/ssl/client.csr
new file mode 100644
index 0000000..e4ed6cd
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.csr
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA066Z8xTn9sX2BqMv+jVD302CeOFyJPH5disAx+f5
+K73rhSSaq0itTXExR6w2vhI+Zj6hLIq+jjpgxWK7Ki9hvpkvnQdKT7CqllDHL//9
+PvEe75MZjWWAqeqfYc2rJ5cik4p/4nRp1ldPbrTi8ziAEBmgRWZ7TDrEN07Myi1b
+II7EzuGD0c7JXGePQOCfQyu+gozFDyftQX+pwEtJ9P7VF53aqfy2dw0+Bt5F2cQm
+IIaeS/wQlM9brlvmuFEelo333kbtAi8CXv1KhoPGYAGXDHc/ldjatOHymhJ6Wewv
+MalFgpFcq+r9hQvED0eEtvagD/baNLWLkSL9w9aBbdULzA08ERY1onr95+ZjXaEc
+8v2yKJwPLLy/AVdssEmtJFfQYiwh+HT31TYUQWe47w1E5RUTBMPonzmcyjsAAq8J
+EMA7my4g2Zaj4YmuRddD0vFJB627WGW089qrzo7qzQRvj6I/VNzyswaNuOG2rNKS
+wAuUQL0PgijlQvNnyHylUR0tlzU9yehQdYW+mmMGnv2eF7p+cFGQDjhELS8ua0+u
+VEFysLynzn1rZWKXz7RdIcCbugio5ORtXuppTOSSDq+ctYKMMR5kW65sBINFI3Vw
++KhqYQXKmLKcyOKRP4IS7bFSMZwlHjasULn3fwVlzq/jN0Ahg4hRIgJCp+fAK0Xh
+KKcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCDCV5pC0dh7rL6QaxWaYHeMy5a
+Khw25nNCs18D4Ttz2o5t7mb06xpzbl1T+6QBFXtMe4qEPrAq6xMPu7Pf4uiJDR4e
+UO4yvGbAz4MFyy01qbl6StVTpkERSINS/ykxi04/GOd9U91+b3rBiMAAmQa8673Q
+k1k0aqNTmmvOdJjr3OZHnTiIRWYzKzVUc9uq9ntodGUKAS99ewi+CIG3SHeQCbql
+3HQ3iAVkSszMgjPc6JtpZ6MwGl00CLkCpjty7jME9n3ZtoA0H2hLUoja+oiqTory
+i33ACkTLz4WoN3I8DaQOQBOHqJ1X7kz/vqzCM0u7UrC6i1l0/DhV8knGPcWXduDB
+0joj+3UQNwxA6Qoj/xgr7OQ13Qz4mjraGtqqXJuQNaz0qzLcUxwXB4JgEZ+sZjPy
+CjvLMZWPPWLh3lNr1hLMPU8+QIAq/OrcCpPqD1tmXiAPLi9vKd21D1C6xripGRIB
+XEdh6L1Y0WJdwr7jNC4Jjk/HHxCf8AYU0zr9P+9UCsE29V2VF11LYTMW+Jrn4Pog
+mkJC0wKEfrwH20rHsHm8r06i+TbXtNQd0a+sB5auHdUz+qR566/nSWBP0LXdY2Y6
+SzLVkBGDbRfDp27R1zh2TqK4A+mqX+f7hocWtn9FcWRQsoVhS8EYh3Q+A4CAaRU2
+PrXS6ii1ODpa6QhelA==
+-----END CERTIFICATE REQUEST-----
diff --git a/ratis-test/src/test/resources/ssl/client.key
b/ratis-test/src/test/resources/ssl/client.key
new file mode 100644
index 0000000..8a5a158
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.key
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/client.pem
b/ratis-test/src/test/resources/ssl/client.pem
new file mode 100644
index 0000000..a833dde
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/client.pem
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDTrpnzFOf2xfYG
+oy/6NUPfTYJ44XIk8fl2KwDH5/krveuFJJqrSK1NcTFHrDa+Ej5mPqEsir6OOmDF
+YrsqL2G+mS+dB0pPsKqWUMcv//0+8R7vkxmNZYCp6p9hzasnlyKTin/idGnWV09u
+tOLzOIAQGaBFZntMOsQ3TszKLVsgjsTO4YPRzslcZ49A4J9DK76CjMUPJ+1Bf6nA
+S0n0/tUXndqp/LZ3DT4G3kXZxCYghp5L/BCUz1uuW+a4UR6WjffeRu0CLwJe/UqG
+g8ZgAZcMdz+V2Nq04fKaEnpZ7C8xqUWCkVyr6v2FC8QPR4S29qAP9to0tYuRIv3D
+1oFt1QvMDTwRFjWiev3n5mNdoRzy/bIonA8svL8BV2ywSa0kV9BiLCH4dPfVNhRB
+Z7jvDUTlFRMEw+ifOZzKOwACrwkQwDubLiDZlqPhia5F10PS8UkHrbtYZbTz2qvO
+jurNBG+Poj9U3PKzBo244bas0pLAC5RAvQ+CKOVC82fIfKVRHS2XNT3J6FB1hb6a
+Ywae/Z4Xun5wUZAOOEQtLy5rT65UQXKwvKfOfWtlYpfPtF0hwJu6CKjk5G1e6mlM
+5JIOr5y1gowxHmRbrmwEg0UjdXD4qGphBcqYspzI4pE/ghLtsVIxnCUeNqxQufd/
+BWXOr+M3QCGDiFEiAkKn58ArReEopwIDAQABAoICAQCS/TchuG6qySPh0AdMsxgC
+Q4xJ7CKazPvwtkPAjLBVU3fr6FMOhVTx5ZvWrRsGORzjWrIIf/96O8deyQQ2ctho
+xW5ul9VeKMsLvePSzOtJfZ2Btp+Ts9v4IVezTmF/CE+WLCJJWZT0pYQe17SQN7eV
+dnX4OwV7FYOS9ED4Uy5oGTtIfQhP9EuWwsKSxmdQ+Vs8xAtCTvNM/B0fzif428uU
+rqRcTYNWf/eGRWozoSToiRH6qXnKG4MKO/6mFo4q9UIdMxr43TPEShYp+e8SU4hT
+u6qHnyGfYRZZLmOPdZX/4g7Gb7HuHysT6+ukwIDDm9XhwAQdfcMAcU/NYXJWlg2t
+zAI4zOjjcromFhZpwQ3FIfAyXK1MVAPUyumzw5ehHr1DU3Fa2BUb/gec0FIciF/A
+1Mlfun4SKR0mRjqzVl+0mhwAnNju1t5S4052+sYZBKC5VhfyHHoRz2bCISnVccTr
+U8BJbmGVkTWnkcc0t259JYt1V1of2h4s5ss99p2rX+0TUoieRpE6Ud+Jtg6Ks1QN
+vHRPh7ueMAEzCL6fsexTMzsb7rQPhDATQsQuxbpHfOqgcDXw0OBJBfeABx38bTLc
+SELQPN5J5n9UTDFU6ArEeG8ZxjYGwSM7JMYFpdXAGioRUUl1rPSBeahOn8hrlsd9
+Nzu/B0YanBDoUDvOEXVRoQKCAQEA9JM3OKuAUmIplOTLin1ksbUCgZITjstSYsSM
+8s8JGSd0FXog8jZOjVD5/Ha7hpnuTWB+rTKxUcHuZrsKtZw6wC5CfX940R3IIf0h
+sjJ2IwEi7sMs04R7wFuBrSe/hG9cBAqoo81Sz0rNUPqbjF6061Xc5pkRBXiTg1Ie
+UwoD5qkQEz9GuPQxDfs/nI6B8bxGzYZDoLkd6SkL/IEpTAe2Gz8BYdK9LZ9TtmSc
+YMzNN70tuQSM1fm4hF1jw8Bzl7bQqpB8CA2elXXjqpxe2QdCvnd7OojXdYD9ZsJW
+bVTt/MVLp+8lssBK53Flv8tR3diG0eSqxierV37ZwtEjfp7NuwKCAQEA3ZIHqTEG
+e+CpO6pFAEgrigpmiv+OfsEHJXLW3y9yCtkSW2TdSRt22HIfWBfkwbXTUIoQKK3k
+gS8s59zeIvIkDD6cCEPiyV1zmsNJfYmt7lXyKd30atsKWsE/2ptM45oPnJw9nPfm
+iMnykQi3V98YzoPEvG5vlZvDmABlf/g4xchOhWgEgYQuIPyZ0MvFGZHt/qqoKA67
+UewP1p0ZaxzhKUtLNnhIAvTplNi2kTCIyalv1KhdsEEOTE1EMx+qjnZvewjSrbcH
+mH5E9seWxIMzs7M0sxGeXbO/BIslDGXCaVxMP+PvnRbDEhSk9f+XLJ2aDHxPknPM
+S7LWQsrnCBEsBQKCAQBlV6sGHGpruKkqPTPqCtNyn2KOdg0MmktlCbSt2AQZHDQJ
+wjVsp/wcqnUrm7dQqvAHt/hFpD+9dPJoNxDU+AuQHpsGjY9udzy6eL1BrY8uKgZj
+kRQ+ASOWLVSqf1HoeoxSUGhYl3e81W4GJBHYNFYbPdTu6HYZIkFdpOVT206KgXtb
+vlh7wxOgi39sS9CkbjOYbg4PxyURvCRvWMXX70oSOCVGd6BUCzUQ2tfBdJuEgSs1
+AyGoeHVww5HXRyOete34/y2l2J7hp5vS0kCrBUsuySuSsak102Es91IAs7R5jPcw
+evPQAcYH6QEUXBdsKZCT4ICy4zUoaDOreiWPDhaJAoIBAQDQaYS1O70alrb4vW9M
+FZaGsMPFqR6S0eRz8DJ3GrzNwWNH/nc7Rg8jBy2+gjMVln8fOc57RR4z+Xjm29VC
+z7rinZ3NFHXk7PdJsrmU0QV+su8nPHSkckaqGS6D5SKM0CQxLrGxzWjWcAavL6Ct
+kddWoZFToRlqg2qWtoDCv2w/deuOBsp+PSPauV/Bi3ppU9+mdUXY8Ap4w3AecgUv
+wKl2WzFzGe66gT6ZWMNZjGqOLcdYI8qEeJ0HXoJxCC848+RmuWTaK9iEMvmRmymQ
+zMy8EivW6BKrluBtWKjPoYQd8Mh0LGX9wCRbBHoqPnEncVMuvVtnqW6YIa7ygsKP
+zv81AoIBAQDWAbmia09ITgVubEBkfeqpSirWLRo39EBCXYD7deN2wj3vrfywtUnY
+PCx5P5hP2FFCIIbuegKP3VeCOBZ5o4k41M8jZCHUaUF74T328BuR+jfOUaxj3thk
+8bptvU5q19on4F0cKcph7tHfVjJmC6IAmVi0PBO5CBLMD2nizFjNLOEzMQKMnGif
+cUE9y5mKnUgPXsAS4gtzQzP5gvFHlweEO47w94CT6Ni+NG9oyBVSgsaBm2gH2mw1
++s3ukyfgbZDLPN/HI7BduQAbOf+yuE0ocVKe1CNZB1QS2hCkENTibb6cqu21ba7S
+2aZkEK99uNWcsTLOgMbe555x+EObmSsb
+-----END PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/generate.sh
b/ratis-test/src/test/resources/ssl/generate.sh
new file mode 100755
index 0000000..809333c
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/generate.sh
@@ -0,0 +1,50 @@
+# 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.
+
+# Changes these CN's to match your hosts in your environment if needed.
+SERVER_CN=localhost
+CLIENT_CN=localhost # Used when doing mutual TLS
+
+echo Generate CA key:
+openssl genrsa -passout pass:1111 -des3 -out ca.key 4096
+echo Generate CA certificate:
+# Generates ca.crt which is the trustCertCollectionFile
+openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt
-subj "/CN=${SERVER_CN}"
+echo Generate server key:
+openssl genrsa -passout pass:1111 -des3 -out server.key 4096
+echo Generate server signing request:
+openssl req -passin pass:1111 -new -key server.key -out server.csr -subj
"/CN=${SERVER_CN}"
+echo Self-signed server certificate:
+# Generates server.crt which is the certChainFile for the server
+openssl x509 -req -passin pass:1111 -days 3650 -in server.csr -CA ca.crt
-CAkey ca.key -set_serial 01 -out server.crt
+echo Remove passphrase from server key:
+openssl rsa -passin pass:1111 -in server.key -out server.key
+echo Generate client key
+openssl genrsa -passout pass:1111 -des3 -out client.key 4096
+echo Generate client signing request:
+openssl req -passin pass:1111 -new -key client.key -out client.csr -subj
"/CN=${CLIENT_CN}"
+echo Self-signed client certificate:
+# Generates client.crt which is the clientCertChainFile for the client (need
for mutual TLS only)
+openssl x509 -passin pass:1111 -req -days 3650 -in client.csr -CA ca.crt
-CAkey ca.key -set_serial 01 -out client.crt
+echo Remove passphrase from client key:
+openssl rsa -passin pass:1111 -in client.key -out client.key
+echo Converting the private keys to X.509:
+# Generates client.pem which is the clientPrivateKeyFile for the Client
(needed for mutual TLS only)
+openssl pkcs8 -topk8 -nocrypt -in client.key -out client.pem
+# Generates server.pem which is the privateKeyFile for the Server
+openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem
+
+
diff --git a/ratis-test/src/test/resources/ssl/server.crt
b/ratis-test/src/test/resources/ssl/server.crt
new file mode 100644
index 0000000..aa43f66
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.crt
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTIxMDYwNDEzMDgzM1oXDTMxMDYwMjEzMDgzM1owFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuceyZa7AKmiq
+Yof4KcEb84MOu+zPmujOpBRLH74Bc0z05hC0pCLMMVLkEGAkrVBoBHLfeuhoRdr+
+hKVdnPaNUv5uW2qpyuFU4DG43wEXKYeQTleuMDefzx37xgUJNyqFJAnZsu+Hst9r
+aO6TfbJ4g1swo2VK3F+ga0j38IDsimj8Qt1V3lZoEPFV4GfZt86D+OIWV0zQdko1
+/kk+zO/wbja2uxgtu8O9oINbyrCI0HldVEWXv8+ye9NiIVnt3FLWYiJrZJ7iF4hs
+MHRafZySu64OXexOBI0IIa6i20KRSzPImThhbtOXR5Pp6lMcdiBiWCn9DRhDn/Ds
+0hSyOeKDO+GB8Pw7b/ihmpB8b+IeB0u1Z8CTRkFabOUPH5Q0ZxekszphB65pROrL
+bcRDhIJc43lxdp/RltnXWt+yjqyfOO9zQ02STGyfe4ckc/xdEVZ4IlGEej0qNA+J
+jRN0yBDy0824jDVXy4EHAeXH68VReU4XM+xRQJef/6QSKzo9S6R5atzyZJBoBLTO
+liobwH6MITb52zDDWmi8qkTtyaa2Y1W3v6+R0JU/zDG3WclI97aBn6jEq8Yxm243
+3ZOdnMHdJrYVxSwoDYR/zmR7bekSkhX/azbd4I4WqX51JADSJIngocST813f47oX
+CU5hk9o+Oz4PQUI5u/j4tE3fwoRizmsCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA
+SsN61v0xDIQsXsTSNpiO5x8osdFKopegx3xzgi48pk2GwcjJ6Oy7hg1Jvfsbju1q
+hdYyT4pPFEpEUrKlT31RzgvRk/No7Ck8T6zA4eZj4fnH7n30AhClchmdwSiY8C5Q
+MU0oTvZC7QtAHbOvyDaY5/L1NTJE5ttY/p2zRmSEvOmvxpCjKxvoaYH2KDxsI9qh
+8GtTK2p/M5x4EQSFImTthEsAe40Uq1ss3eTsZc17K0S1YRong99TELOAwSCvRLJG
+7tqu1f1zTjGXlCNdNa4quehpgWTEk5RoAhOfhnu6Swxke192pyi/Ea60zFgqmDEx
+PXzFR+zpIom5CHeLWtKcgKwfNkJ4EHYkUjmJKsulxxE2fG+FU0s/iNUyd1IWtH1y
+1B9yRu5zeB8gSZc2Iypp4pyFYYyw/lVEKJJRZPvRYUyOK2JLdC8QTiWyvG3Aw3lL
+/vd/X5QgxWrOIRlrUIfeoJUd4eBhPdgKWwT4q2g0c6fqHAZKXaXA62enVU9vigRz
+BVFYWO/VGfMa15L6Jfslc0TqW2JODcDhFYRJEelKEtv6q8p1Z31yaQ1TwZRO5D5I
+IxyquzR/jD5EVH41wYSRmO+p4ci7vEr8chGtQSri2jIKkmuhjgPT1taUit33Y1xd
+E1+GDptymFLTQmzlXpjknMj7Wecwl6hpnCNXJV6Wh9s=
+-----END CERTIFICATE-----
diff --git a/ratis-test/src/test/resources/ssl/server.csr
b/ratis-test/src/test/resources/ssl/server.csr
new file mode 100644
index 0000000..e7daafb
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.csr
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAuceyZa7AKmiqYof4KcEb84MOu+zPmujOpBRLH74B
+c0z05hC0pCLMMVLkEGAkrVBoBHLfeuhoRdr+hKVdnPaNUv5uW2qpyuFU4DG43wEX
+KYeQTleuMDefzx37xgUJNyqFJAnZsu+Hst9raO6TfbJ4g1swo2VK3F+ga0j38IDs
+imj8Qt1V3lZoEPFV4GfZt86D+OIWV0zQdko1/kk+zO/wbja2uxgtu8O9oINbyrCI
+0HldVEWXv8+ye9NiIVnt3FLWYiJrZJ7iF4hsMHRafZySu64OXexOBI0IIa6i20KR
+SzPImThhbtOXR5Pp6lMcdiBiWCn9DRhDn/Ds0hSyOeKDO+GB8Pw7b/ihmpB8b+Ie
+B0u1Z8CTRkFabOUPH5Q0ZxekszphB65pROrLbcRDhIJc43lxdp/RltnXWt+yjqyf
+OO9zQ02STGyfe4ckc/xdEVZ4IlGEej0qNA+JjRN0yBDy0824jDVXy4EHAeXH68VR
+eU4XM+xRQJef/6QSKzo9S6R5atzyZJBoBLTOliobwH6MITb52zDDWmi8qkTtyaa2
+Y1W3v6+R0JU/zDG3WclI97aBn6jEq8Yxm2433ZOdnMHdJrYVxSwoDYR/zmR7bekS
+khX/azbd4I4WqX51JADSJIngocST813f47oXCU5hk9o+Oz4PQUI5u/j4tE3fwoRi
+zmsCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCY1/hPvxArJ6XR6DHVnjNBk8Jc
+sMm8QQQT4yPY9liuLiQrvg+rJYkbHbmDtkOF/a6FP8v+0Zea9VOY7lGZAEhGDVOy
+maA7WABV00JFNfozMr9mJiYFeAunJGA7leA3C70uKtyzhUCoZanY+3F3m2tSPkJs
+1fvUjJWxQ/IO/pJCfPS+nWpWP9dDUsKhCYfbrt2W1cKUVlWHhuM2Eup9uHJh5h+d
+5hRA5M2/mc2DyVonGTSh/AWCINJgbmpx/b3/OYlAGKfWZQUgnbDoipxao1iu1b1N
+H20/E7FN1NHU2Pe1F1MnVAvlycicTUmo/SokhA07wgoNarbnLtFheQPjY3HI8m8R
+8ljRZTSmmrEV12wZ4oyNBmp7KCiZwDFmXzSh+EQGo81KC3fiBhDJbtnxG3ZoEcLF
+5rizSLnRCELUFvPTf5Ufj8kJvEshpPTogJfswRCUrc2lLqqujzEhSFBQcqJTyYsk
+1wwtynKwR+WsaYZTAGn6W1oBI6cMbfb54v0W6/naI+ohzS3eaCR8IO1p/YYr/q//
+nJKEIkFsKIgRLUjFBZ3lZ0CKKwp8vTAYkjcjFUWj1Dt8WqBArsZ8MN9dzwDBrOPJ
+RVBfnmrHFbcWmbxUNKPDczxEiTJfGC68V83ejVEzaixZfrEBCmjTzOS8TUCz+esZ
+RhvhWXXlPZSJXZO02A==
+-----END CERTIFICATE REQUEST-----
diff --git a/ratis-test/src/test/resources/ssl/server.key
b/ratis-test/src/test/resources/ssl/server.key
new file mode 100644
index 0000000..6fed777
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.key
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/ratis-test/src/test/resources/ssl/server.pem
b/ratis-test/src/test/resources/ssl/server.pem
new file mode 100644
index 0000000..7a20975
--- /dev/null
+++ b/ratis-test/src/test/resources/ssl/server.pem
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+-----BEGIN PRIVATE KEY-----
+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
+lrugObUwuMHdvaNJN7HsTS9rwAQefA==
+-----END PRIVATE KEY-----