You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by te...@apache.org on 2022/06/14 10:41:55 UTC

[pulsar] branch master updated: [improve][metadata] Add tls config for etcd client (#15270)

This is an automated email from the ASF dual-hosted git repository.

technoboy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new e0c4099893a [improve][metadata] Add tls config for etcd client (#15270)
e0c4099893a is described below

commit e0c4099893a77e211137e8156193f5c0c0f8b522
Author: Zixuan Liu <no...@gmail.com>
AuthorDate: Tue Jun 14 18:41:48 2022 +0800

    [improve][metadata] Add tls config for etcd client (#15270)
---
 conf/broker.conf                                   |  3 +
 conf/standalone.conf                               |  4 ++
 .../pulsar/metadata/impl/EtcdMetadataStore.java    | 67 ++++++++++++++++++++-
 .../metadata/impl/EtcdMetadataStoreTest.java       | 70 ++++++++++++++++++++++
 .../src/test/resources/etcd_config_example.yml     | 36 +++++++++++
 pulsar-metadata/src/test/resources/ssl/.gitignore  |  1 +
 pulsar-metadata/src/test/resources/ssl/README.md   | 28 +++++++++
 .../src/test/resources/ssl/cert/ca-config.json     |  1 +
 .../src/test/resources/ssl/cert/ca-key.pem         | 27 +++++++++
 pulsar-metadata/src/test/resources/ssl/cert/ca.csr | 15 +++++
 pulsar-metadata/src/test/resources/ssl/cert/ca.pem | 18 ++++++
 .../src/test/resources/ssl/cert/client-key-pk8.pem | 28 +++++++++
 .../src/test/resources/ssl/cert/client-key.pem     | 27 +++++++++
 .../src/test/resources/ssl/cert/client.csr         | 16 +++++
 .../src/test/resources/ssl/cert/client.pem         | 20 +++++++
 .../src/test/resources/ssl/cert/etcd0-key.pem      | 27 +++++++++
 .../src/test/resources/ssl/cert/etcd0.csr          | 16 +++++
 .../src/test/resources/ssl/cert/etcd0.pem          | 20 +++++++
 .../src/test/resources/ssl/cert/etcd1-key.pem      | 27 +++++++++
 .../src/test/resources/ssl/cert/etcd1.csr          | 16 +++++
 .../src/test/resources/ssl/cert/etcd1.pem          | 20 +++++++
 .../src/test/resources/ssl/cert/etcd2-key.pem      | 27 +++++++++
 .../src/test/resources/ssl/cert/etcd2.csr          | 16 +++++
 .../src/test/resources/ssl/cert/etcd2.pem          | 20 +++++++
 .../src/test/resources/ssl/cert/server-key.pem     | 27 +++++++++
 .../src/test/resources/ssl/cert/server.csr         | 16 +++++
 .../src/test/resources/ssl/cert/server.pem         | 20 +++++++
 .../ssl/generate-self-signed-certificates.sh       | 70 ++++++++++++++++++++++
 28 files changed, 680 insertions(+), 3 deletions(-)

diff --git a/conf/broker.conf b/conf/broker.conf
index 24e32e0b7f4..2cde2073678 100644
--- a/conf/broker.conf
+++ b/conf/broker.conf
@@ -26,6 +26,9 @@
 # * zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181/my-chroot-path (to add a ZK chroot path)
 metadataStoreUrl=
 
+# Configuration file path for metadata store. It's supported by RocksdbMetadataStore and EtcdMetadataStore for now
+metadataStoreConfigPath=
+
 # The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl
 configurationMetadataStoreUrl=
 
diff --git a/conf/standalone.conf b/conf/standalone.conf
index cae48d57dc2..c367916e9eb 100644
--- a/conf/standalone.conf
+++ b/conf/standalone.conf
@@ -26,6 +26,10 @@
 # * zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181/my-chroot-path (to add a ZK chroot path)
 metadataStoreUrl=
 
+# Configuration file path for metadata store. It's supported by RocksdbMetadataStore and EtcdMetadataStore for now
+metadataStoreConfigPath=
+
+
 # The metadata store URL for the configuration data. If empty, we fall back to use metadataStoreUrl
 configurationMetadataStoreUrl=
 
diff --git a/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/impl/EtcdMetadataStore.java b/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/impl/EtcdMetadataStore.java
index e1c26bead09..0073db0c8d7 100644
--- a/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/impl/EtcdMetadataStore.java
+++ b/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/impl/EtcdMetadataStore.java
@@ -18,8 +18,11 @@
  */
 package org.apache.pulsar.metadata.impl;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.etcd.jetcd.ByteSequence;
 import io.etcd.jetcd.Client;
+import io.etcd.jetcd.ClientBuilder;
 import io.etcd.jetcd.KV;
 import io.etcd.jetcd.KeyValue;
 import io.etcd.jetcd.Txn;
@@ -40,8 +43,16 @@ import io.etcd.jetcd.watch.WatchEvent;
 import io.etcd.jetcd.watch.WatchResponse;
 import io.grpc.Status;
 import io.grpc.StatusRuntimeException;
+import io.grpc.netty.GrpcSslContexts;
 import io.grpc.stub.StreamObserver;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslProvider;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -54,7 +65,12 @@ import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.pulsar.metadata.api.GetResult;
 import org.apache.pulsar.metadata.api.MetadataStoreConfig;
 import org.apache.pulsar.metadata.api.MetadataStoreException;
@@ -87,10 +103,8 @@ public class EtcdMetadataStore extends AbstractBatchedMetadataStore {
         super(conf);
 
         this.leaseTTLSeconds = conf.getSessionTimeoutMillis() / 1000;
-        String etcdUrl = metadataURL.replaceFirst(ETCD_SCHEME_IDENTIFIER, "");
-
         try {
-            this.client = Client.builder().endpoints(etcdUrl).build();
+            this.client = newEtcdClient(metadataURL, conf);
             this.kv = client.getKVClient();
             this.client.getWatchClient().watch(ByteSequence.from("\0", StandardCharsets.UTF_8),
                     WatchOption.newBuilder()
@@ -110,6 +124,38 @@ public class EtcdMetadataStore extends AbstractBatchedMetadataStore {
         }
     }
 
+    private Client newEtcdClient(String metadataURL, MetadataStoreConfig conf) throws IOException {
+        String etcdUrl = metadataURL.replaceFirst(ETCD_SCHEME_IDENTIFIER, "");
+        ClientBuilder clientBuilder = Client.builder().endpoints(etcdUrl);
+
+        if (StringUtils.isNotEmpty(conf.getConfigFilePath())) {
+            try (InputStream inputStream = Files.newInputStream(Paths.get(conf.getConfigFilePath()))) {
+                EtcdConfig etcdConfig = new ObjectMapper(new YAMLFactory()).readValue(inputStream, EtcdConfig.class);
+                if (etcdConfig.isUseTls()) {
+                    File trustCertsFile = readFile(etcdConfig.getTlsTrustCertsFilePath());
+                    File keyFile = readFile(etcdConfig.getTlsKeyFilePath());
+                    File certFile = readFile(etcdConfig.getTlsCertificateFilePath());
+                    SslContext context = GrpcSslContexts.forClient()
+                            .trustManager(trustCertsFile)
+                            .sslProvider(etcdConfig.getTlsProvider())
+                            .keyManager(certFile, keyFile)
+                            .build();
+                    clientBuilder.sslContext(context);
+                }
+
+                if (StringUtils.isNotEmpty(etcdConfig.getAuthority())) {
+                    clientBuilder.authority(etcdConfig.getAuthority());
+                }
+            }
+        }
+
+        return clientBuilder.build();
+    }
+
+    private File readFile(String path) {
+        return StringUtils.isEmpty(path) ? null : new File(path);
+    }
+
     @Override
     public void close() throws Exception {
         super.close();
@@ -433,3 +479,18 @@ public class EtcdMetadataStore extends AbstractBatchedMetadataStore {
         }
     }
 }
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Builder
+class EtcdConfig {
+    private boolean useTls;
+
+    private SslProvider tlsProvider;
+    private String tlsTrustCertsFilePath;
+    private String tlsKeyFilePath;
+    private String tlsCertificateFilePath;
+
+    private String authority;
+}
diff --git a/pulsar-metadata/src/test/java/org/apache/pulsar/metadata/impl/EtcdMetadataStoreTest.java b/pulsar-metadata/src/test/java/org/apache/pulsar/metadata/impl/EtcdMetadataStoreTest.java
new file mode 100644
index 00000000000..180d835830a
--- /dev/null
+++ b/pulsar-metadata/src/test/java/org/apache/pulsar/metadata/impl/EtcdMetadataStoreTest.java
@@ -0,0 +1,70 @@
+/**
+ * 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.metadata.impl;
+
+import static org.testng.Assert.assertTrue;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.google.common.io.Resources;
+import io.etcd.jetcd.launcher.EtcdCluster;
+import io.etcd.jetcd.launcher.EtcdClusterFactory;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import lombok.Cleanup;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pulsar.metadata.api.MetadataStore;
+import org.apache.pulsar.metadata.api.MetadataStoreConfig;
+import org.apache.pulsar.metadata.api.MetadataStoreFactory;
+import org.testng.annotations.Test;
+
+@Slf4j
+public class EtcdMetadataStoreTest {
+
+    @Test
+    public void testTlsInstance() throws Exception {
+        @Cleanup
+        EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster("test-tls", 1, true);
+        etcdCluster.start();
+
+        EtcdConfig etcdConfig = EtcdConfig.builder().useTls(true)
+                .tlsProvider(null)
+                .authority("etcd0")
+                .tlsTrustCertsFilePath(Resources.getResource("ssl/cert/ca.pem").getPath())
+                .tlsKeyFilePath(Resources.getResource("ssl/cert/client-key-pk8.pem").getPath())
+                .tlsCertificateFilePath(Resources.getResource("ssl/cert/client.pem").getPath())
+                .build();
+        Path etcdConfigPath = Files.createTempFile("etcd_config", ".yml");
+        new ObjectMapper(new YAMLFactory()).writeValue(etcdConfigPath.toFile(), etcdConfig);
+
+        String metadataURL =
+                "etcd:" + etcdCluster.getClientEndpoints().stream().map(URI::toString).collect(Collectors.joining(","));
+
+        @Cleanup
+        MetadataStore store = MetadataStoreFactory.create(metadataURL,
+                MetadataStoreConfig.builder().configFilePath(etcdConfigPath.toString()).build());
+
+        store.put("/test", "value".getBytes(StandardCharsets.UTF_8), Optional.empty()).join();
+
+        assertTrue(store.exists("/test").join());
+    }
+}
\ No newline at end of file
diff --git a/pulsar-metadata/src/test/resources/etcd_config_example.yml b/pulsar-metadata/src/test/resources/etcd_config_example.yml
new file mode 100644
index 00000000000..4ac10028c72
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/etcd_config_example.yml
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+# Configure whether to use TLS connection
+useTls: false
+
+# Name for TLS provider: one of OPENSSL, JDK
+tlsProvider:
+
+# Path for the trusted TLS certificate file
+tlsTrustCertsFilePath:
+
+# Path for the TLS private key file
+tlsKeyFilePath:
+
+# Path for the TLS certificate file
+tlsCertificateFilePath:
+
+# Override SAN(Subject Alternative Name) of certificate
+authority:
diff --git a/pulsar-metadata/src/test/resources/ssl/.gitignore b/pulsar-metadata/src/test/resources/ssl/.gitignore
new file mode 100644
index 00000000000..0139cd0a6fa
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/.gitignore
@@ -0,0 +1 @@
+cfssl
diff --git a/pulsar-metadata/src/test/resources/ssl/README.md b/pulsar-metadata/src/test/resources/ssl/README.md
new file mode 100644
index 00000000000..af9ad88dbae
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/README.md
@@ -0,0 +1,28 @@
+<!--
+
+    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.
+
+-->
+
+This directory is used for Etcd metadata testing.
+
+**Note: DO NOT EDIT THIS DIRECTORY STRUCTURE**
+
+The `cert` directory holds the Etcd TLS certificate.
+
+The `generate-self-signed-certificates.sh` is used to generate self-signed certificates based on [cfssl](https://github.com/cloudflare/cfssl).
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/ca-config.json b/pulsar-metadata/src/test/resources/ssl/cert/ca-config.json
new file mode 100644
index 00000000000..dafdebe741d
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/ca-config.json
@@ -0,0 +1 @@
+{"signing":{"default":{"expiry":"876000h","usages":["signing","key encipherment","server auth","client auth"]}}}
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/ca-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/ca-key.pem
new file mode 100644
index 00000000000..9a3ceba7351
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/ca-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAyUXfR1a3FAsYajnkYYASVgvOBY9jiFrWeNfi9HZsDF6xs5+3
+vempZ5FYw9smzJBxRwV7r6g8TJbXW4V03Drjuk3Y9VSZfk5uyaWT3tzr0uYV7hYR
+9kM3RybimN1cbtUELK7LKvdg3Y+gHrvFPTQ3I0grXI4geCGfDh07mPYFmH8SqtAY
+22qJb8sFBaIq76dstuKmX55abEJWXQG5B2CXxuYYaseydzAJNk8y+zEfJVZs4pWg
+NcC4CozjQcl2NsokX8PIE/OaCxIwabBw1ONFHHcClFwW7inXnip1fYE+Se6348U6
+sOVwCt2wTGYnBJOqLZw1vuZZYXUUwdgp0Y+6pQIDAQABAoIBAQDIQBBptcjo1DEy
+Xu7uYrHbklskBQmOPHJuQLy5ZKkTlZNPl3UEKlo1hNK/6mar2Hobo0Tv+34i5WDW
+2ezuwFian6FgVQ9yLy7iyS5Z6YtnhSwEeUjdrvpsfC+uz26LhrxkiHPzB7UEO0Wf
+zknrHuxM2hzqNS3w1+Kb9FG7WesvUFpxFzl2bdktdxI2THGr5mK2S4mSGFRkNazz
+tAwUpe+9WGra5OmlukYGHtm9g1WNcutyYAWOdfSlFOva9D9wRWiE2t1NpWjWwQv0
+P11RZQbPbAqmAhHwciSAWu+m8M/8EUDmlebF5pbPTDyGm5d35+byPWNiKxMn4/Rj
+zEebwR3xAoGBAO0WMeqLFLfGShCxu9Ke4Qb2c8MBfGa6LQJmDZ8BsUAPCvyG3NAt
+308KvNudj/+rrr9xCkmNSKD31DOUu6y05/Sa7Opq2qj/L7Hxk8GxT3/3Q/3jTmDn
+8KOQ94oUQylUSLz7gQnGiwORfBRDa1yYAljtuvXGkzuv9sJSjYp++4TXAoGBANlU
+SRVmXxYS6WgcsBX6ZlKK1yKyoaLMobDWBGNcGWTdn+7yUcs0viBLnwf2Sgd1uhx0
+N1XI2pOxTMbLKLoNUiw19jAfh9Jb7GieeyANQHjp4z8q1u8JpY+hd8SxJt/wAWyH
+yELVymT5xREyE/kBexGLHn3E6fwG3e4lejmmsJDjAoGANDHas2QDH6JL0/kM/IV1
+zO6c9aVyGbuLBSgqf4MD3rJxHL8kBcDTlLcsdLzvYr4NLqBYP3EXODjXQrPPzWnW
+Vwd0ePclRGdJvMcOXGoIsodhNJfCPZtSYvAbTw6+6m5fxRspgVlwaUyyIFPnYu8k
+NSfBLTKWX2GR1Z4PAxqJjOkCgYBrO9Rn08BdMu2qKmWUIp50Yf9vJnbszk2F5MP7
+hskc5OHyU+yZ+KdLZiTNF/BFKAix24ImONnpABXjDQFGdCY0nFeiPRyWZjcLFA+q
+q/9A6h7v3yJ4hekEKubezY5h9pmtRH5CxBx+oGNlbbE/L0TbTiq4Yf4WFglRZXqe
+kaoTgwKBgE6LoQYR7I2TFs2mVMJn4FG9Mlk66xBWxkA/+JsQP78QoQbfhrFBFbx8
+bPZPxjtJ47fYUga25fpxRzm1sVKm69G7tnI73xCJKenRCiZE6DktnKLyOJIzfuIX
++iXKugrQaf0XQPjKXYPnczBBd572MoXoKVbWQbT+/vicv4AahGeh
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/ca.csr b/pulsar-metadata/src/test/resources/ssl/cert/ca.csr
new file mode 100644
index 00000000000..a64072407a0
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/ca.csr
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICUjCCAToCAQAwDTELMAkGA1UEAxMCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDJRd9HVrcUCxhqOeRhgBJWC84Fj2OIWtZ41+L0dmwMXrGzn7e9
+6alnkVjD2ybMkHFHBXuvqDxMltdbhXTcOuO6Tdj1VJl+Tm7JpZPe3OvS5hXuFhH2
+QzdHJuKY3Vxu1QQsrssq92Ddj6Aeu8U9NDcjSCtcjiB4IZ8OHTuY9gWYfxKq0Bjb
+aolvywUFoirvp2y24qZfnlpsQlZdAbkHYJfG5hhqx7J3MAk2TzL7MR8lVmzilaA1
+wLgKjONByXY2yiRfw8gT85oLEjBpsHDU40UcdwKUXBbuKdeeKnV9gT5J7rfjxTqw
+5XAK3bBMZicEk6otnDW+5llhdRTB2CnRj7qlAgMBAAGgADANBgkqhkiG9w0BAQsF
+AAOCAQEALczD308ElfQ2ovsec/FuqPr3zvtOKyJn6F6H2tYl4890astfZzHGBglu
+xICEWn8yAfwPW3jg6QsTjYupJpjVJMgAxsQhKOneUmTaIuDdRzIHX4ss5m7ndXpD
+B/hk1HXtu/+gAeKcSTNjEpKJh91ErsAp9y3tPjk/9BvOqI/OTRj1jeZV0e5yHaNk
+6nM0o7x9LVgREX6PDXXaFmTGYWmsiANU8l6/XLV0vvyreAKcONvruXrRQ+Cg2z4W
+yPbbFsfocwwwrIiC2zQ6+f+d5HQt5IINIKqAYibG0vNul8G0NMYC6JRFYIqEKOU5
+jQWZx7dY8ofkEpXmSx/IaMh1pQ3g4A==
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/ca.pem b/pulsar-metadata/src/test/resources/ssl/cert/ca.pem
new file mode 100644
index 00000000000..34a8593879e
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6jCCAdKgAwIBAgIUXHRN1WrCXk813Zz/mWdg3YAu9OIwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwHhcNMjIwNDI5MDQyMjAwWhcNMjcwNDI4MDQyMjAw
+WjANMQswCQYDVQQDEwJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AMlF30dWtxQLGGo55GGAElYLzgWPY4ha1njX4vR2bAxesbOft73pqWeRWMPbJsyQ
+cUcFe6+oPEyW11uFdNw647pN2PVUmX5Obsmlk97c69LmFe4WEfZDN0cm4pjdXG7V
+BCyuyyr3YN2PoB67xT00NyNIK1yOIHghnw4dO5j2BZh/EqrQGNtqiW/LBQWiKu+n
+bLbipl+eWmxCVl0BuQdgl8bmGGrHsncwCTZPMvsxHyVWbOKVoDXAuAqM40HJdjbK
+JF/DyBPzmgsSMGmwcNTjRRx3ApRcFu4p154qdX2BPknut+PFOrDlcArdsExmJwST
+qi2cNb7mWWF1FMHYKdGPuqUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
+EwEB/wQFMAMBAf8wHQYDVR0OBBYEFB0oynraPRsiMaCbp4VFErPtqn79MA0GCSqG
+SIb3DQEBCwUAA4IBAQAsOdb6+QjABRa7LMIERRy45V1l3lVy04WzLUWEGOeakakE
+I58NDopYY2NDfGPi/gPkErAo8Jo8ruRKPccguHlTYGTwjUBjKGBSySMpalw5DhN0
+iygHdKh9qzqL1ChqcQsQPlmhKgsAYkZyQzD2Gh6c/GpNMe6/NUmjlg0KcnKY3+Vx
+kzy3qWXxiNfSywJlbk6UVWOy0cCxHV+fE0gzy+DgNrcWm6euSPAD4Qz8kXvQKevB
+syTpCxqsKNKG+aVZDoW2BTyrROnnk/lqZQ7k4sKZTcYgTrpNSgylbg7rxVqOIyBk
+G+jAW18vmOlsZpXE306Pqsng6csUJ3IXoHqaBbrm
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/client-key-pk8.pem b/pulsar-metadata/src/test/resources/ssl/cert/client-key-pk8.pem
new file mode 100644
index 00000000000..a7fec9bc004
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/client-key-pk8.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/RbW6dJqk2+BN
+SZKAs1M0RQt6PrhIHHhxBBPXFM3XOqKwacrQihuZQSGLgaxfzXbRejwQ9VBg5Ia/
+tOokwDY3tK9Kp4J0bsqgNXZKTRYtnJ4JYh9566jsD47Qpj6xuQZmo3b1QKLEh+Fh
+9buWTEvvdUY8AQTASwvOGAWyzrZgaKuagQQNkZ49TM8gtsy9uJPWS2PZQpW/qk56
+5jRck8Xb8COQ0QV2wPiqwmEtA7/jEeQViQ49HLdfBTi90Hh4Q8XBv8TPW+sQpN5R
+5fvZEz+iwrGm20kIrtVQ+hMCkbywTlA5rSGW7onnakDyqybt10dBzn609/wg5Dee
+LjMZ3dEvAgMBAAECggEAR1r5NcsEWhZQ8mRNDEhZ9PkBFCTL2NMON5M+15FCTVXp
+lYiSCgRL0XuTyRiiNsdO2U0RlX3+83atsl0KsJUoZNW6Q06Eg21FmEj5jTR+3ps7
+9eIuPeylgxM2wy4R23lcIvQ+j7YCQvEyKrpitepWtcl5Xy8+F4Knr8YUciVdsk8T
+a2DxkjXRXG6RW71Hqdd4bpXszM2E22trjKgouy/PV4LOhSCzyDdwAq4fKLBmS7l5
+2j57Ag0/hDZ7L4nW6jtRG1NBonBk7FhPrd4UCGqxaYwfTYvDWDKpQkJgbkWSYCwc
+pUVAqreYi5+udKhL0dXsKrZGE9I20tQlZkaoBBgreQKBgQDAmu73weinuMp9nC6M
+gSXQxuWL02ybEQFsZioP8oGSP+wZDCrSn1HD52iSSZqEoNLoYm0u8u3D9FbWoiw/
+n8BPqTLjLXUozhDdVuvg6YkKxHG2So7ve04UOXBQqCd/pOuNXRUUYUQ+FMq8vmjD
+dRqVo1gH/qUExa4SeTzoOWUXawKBgQD+Onb9+ExPweZjy+T0eAO1FMFP//KsNwSI
+vlf22jUbCqiu6Pem1m/31Rju04AGydWhdHgd+7PG8/arVyLcgNQtTotXtD7zTCVe
+jkr42R/zbG46c0/z8brUXpYOHOjdgFbY62vwWjykulFsruf36hit9/D+tKVNOgep
+tduFS8DSTQKBgC+oJmj3efHGL5RVCM+LRSgbjsDCV6Um2AtSXCYGAzmEx46LDC2B
+bmHi6GUKAUm/4W/OquVrBpnt427IQdqcVKFhZE4B+XNXSaT61PKZ1mbrpJdOa3+m
+KvOmIrxSXzOeQwp/da/NQW17B48cLh/u4d0Uxbt0rrA3mZLInOF5EiJxAoGBAPGS
+GHOntsuq0gNOQZbTW6J7wF0GNk/ST6qoQ+m62u+BJ1xc3sZXyTlT8kcuDd9ldmve
+wiyred64/1E8kVG50OPkWJ/UFGUXnALHbxIbLzMde3hrDjQdJIyb/DYY3mVriBrD
+SWOwOyPEL474fE+k0CKvEP7WJKTHWXS364ozu1uZAoGBAMBXRwDQZgs+Xn3lTohu
+1NLlHhNIuUzcb+4ROn2StoxZS3mEv2W0KWRTzNJGRr4+kbLMcY7K1jOk0qm6fwcG
+HAJnKkxu9E3hFQVUT14CIKPwi3TQCNjmZqj+LaEwe+75w4rXTajwl3WY58KngA5/
+XVn2W3KsXAKOAteL/07xtDEi
+-----END PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/client-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/client-key.pem
new file mode 100644
index 00000000000..95b52fde4a1
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/client-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAv0W1unSapNvgTUmSgLNTNEULej64SBx4cQQT1xTN1zqisGnK
+0IobmUEhi4GsX8120Xo8EPVQYOSGv7TqJMA2N7SvSqeCdG7KoDV2Sk0WLZyeCWIf
+eeuo7A+O0KY+sbkGZqN29UCixIfhYfW7lkxL73VGPAEEwEsLzhgFss62YGirmoEE
+DZGePUzPILbMvbiT1ktj2UKVv6pOeuY0XJPF2/AjkNEFdsD4qsJhLQO/4xHkFYkO
+PRy3XwU4vdB4eEPFwb/Ez1vrEKTeUeX72RM/osKxpttJCK7VUPoTApG8sE5QOa0h
+lu6J52pA8qsm7ddHQc5+tPf8IOQ3ni4zGd3RLwIDAQABAoIBAEda+TXLBFoWUPJk
+TQxIWfT5ARQky9jTDjeTPteRQk1V6ZWIkgoES9F7k8kYojbHTtlNEZV9/vN2rbJd
+CrCVKGTVukNOhINtRZhI+Y00ft6bO/XiLj3spYMTNsMuEdt5XCL0Po+2AkLxMiq6
+YrXqVrXJeV8vPheCp6/GFHIlXbJPE2tg8ZI10VxukVu9R6nXeG6V7MzNhNtra4yo
+KLsvz1eCzoUgs8g3cAKuHyiwZku5edo+ewINP4Q2ey+J1uo7URtTQaJwZOxYT63e
+FAhqsWmMH02Lw1gyqUJCYG5FkmAsHKVFQKq3mIufrnSoS9HV7Cq2RhPSNtLUJWZG
+qAQYK3kCgYEAwJru98Hop7jKfZwujIEl0Mbli9NsmxEBbGYqD/KBkj/sGQwq0p9R
+w+dokkmahKDS6GJtLvLtw/RW1qIsP5/AT6ky4y11KM4Q3Vbr4OmJCsRxtkqO73tO
+FDlwUKgnf6TrjV0VFGFEPhTKvL5ow3UalaNYB/6lBMWuEnk86DllF2sCgYEA/jp2
+/fhMT8HmY8vk9HgDtRTBT//yrDcEiL5X9to1Gwqoruj3ptZv99UY7tOABsnVoXR4
+HfuzxvP2q1ci3IDULU6LV7Q+80wlXo5K+Nkf82xuOnNP8/G61F6WDhzo3YBW2Otr
+8Fo8pLpRbK7n9+oYrffw/rSlTToHqbXbhUvA0k0CgYAvqCZo93nxxi+UVQjPi0Uo
+G47AwlelJtgLUlwmBgM5hMeOiwwtgW5h4uhlCgFJv+FvzqrlawaZ7eNuyEHanFSh
+YWROAflzV0mk+tTymdZm66SXTmt/pirzpiK8Ul8znkMKf3WvzUFtewePHC4f7uHd
+FMW7dK6wN5mSyJzheRIicQKBgQDxkhhzp7bLqtIDTkGW01uie8BdBjZP0k+qqEPp
+utrvgSdcXN7GV8k5U/JHLg3fZXZr3sIsq3neuP9RPJFRudDj5Fif1BRlF5wCx28S
+Gy8zHXt4aw40HSSMm/w2GN5la4gaw0ljsDsjxC+O+HxPpNAirxD+1iSkx1l0t+uK
+M7tbmQKBgQDAV0cA0GYLPl595U6IbtTS5R4TSLlM3G/uETp9kraMWUt5hL9ltClk
+U8zSRka+PpGyzHGOytYzpNKpun8HBhwCZypMbvRN4RUFVE9eAiCj8It00AjY5mao
+/i2hMHvu+cOK102o8Jd1mOfCp4AOf11Z9ltyrFwCjgLXi/9O8bQxIg==
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/client.csr b/pulsar-metadata/src/test/resources/ssl/cert/client.csr
new file mode 100644
index 00000000000..4e9d091a710
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/client.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICdDCCAVwCAQAwETEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAv0W1unSapNvgTUmSgLNTNEULej64SBx4cQQT1xTN1zqi
+sGnK0IobmUEhi4GsX8120Xo8EPVQYOSGv7TqJMA2N7SvSqeCdG7KoDV2Sk0WLZye
+CWIfeeuo7A+O0KY+sbkGZqN29UCixIfhYfW7lkxL73VGPAEEwEsLzhgFss62YGir
+moEEDZGePUzPILbMvbiT1ktj2UKVv6pOeuY0XJPF2/AjkNEFdsD4qsJhLQO/4xHk
+FYkOPRy3XwU4vdB4eEPFwb/Ez1vrEKTeUeX72RM/osKxpttJCK7VUPoTApG8sE5Q
+Oa0hlu6J52pA8qsm7ddHQc5+tPf8IOQ3ni4zGd3RLwIDAQABoB4wHAYJKoZIhvcN
+AQkOMQ8wDTALBgNVHREEBDACggAwDQYJKoZIhvcNAQELBQADggEBABDSTkd0lwd1
+iU8ClUOlWFcJtkw3OXM4aRTXtbX5EGlwDr1HrQApBKqEn+Mp5rP74Du+cChTsS1S
+7flI9EO4OijwwsuMOe65fqtboUWhjy3pHrY7BEz8JzJBNH6TVPdyEyYCANyTg3z/
+0X+mGvQ7yz25BlaaqNiKI8BVs6ATPYi17fKsxdbV3ZAnhi0jv1ruR1dSBNI4/Y3F
+rdnSsJlJabHjqKbF5t1AxaZ3poR5pSY7i2gM464h8WzC9YUaRyNsDjFoUpV0CpuW
+x9kOyIBfkO1rQozSkDxCXjxDp6KyG5CTyxUsh7pITVbHd6KxaftCkCg95W8B2BzT
+GtfST4d92xU=
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/client.pem b/pulsar-metadata/src/test/resources/ssl/cert/client.pem
new file mode 100644
index 00000000000..6e3d1492726
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/client.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiSgAwIBAgIUdYkf8VBmJYG6HvKxh84BHYaTIH8wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwIBcNMjIwNDI5MDQyMjAwWhgPMjEyMjA0MDUwNDIy
+MDBaMBExDzANBgNVBAMTBmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAL9Ftbp0mqTb4E1JkoCzUzRFC3o+uEgceHEEE9cUzdc6orBpytCKG5lB
+IYuBrF/NdtF6PBD1UGDkhr+06iTANje0r0qngnRuyqA1dkpNFi2cngliH3nrqOwP
+jtCmPrG5BmajdvVAosSH4WH1u5ZMS+91RjwBBMBLC84YBbLOtmBoq5qBBA2Rnj1M
+zyC2zL24k9ZLY9lClb+qTnrmNFyTxdvwI5DRBXbA+KrCYS0Dv+MR5BWJDj0ct18F
+OL3QeHhDxcG/xM9b6xCk3lHl+9kTP6LCsabbSQiu1VD6EwKRvLBOUDmtIZbuiedq
+QPKrJu3XR0HOfrT3/CDkN54uMxnd0S8CAwEAAaOBjTCBijAOBgNVHQ8BAf8EBAMC
+BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw
+HQYDVR0OBBYEFB/UlCHqPmkA3xcYzddO9kD1w8o1MB8GA1UdIwQYMBaAFB0oynra
+PRsiMaCbp4VFErPtqn79MAsGA1UdEQQEMAKCADANBgkqhkiG9w0BAQsFAAOCAQEA
+AneFIIHBZ21J24+lln995ofX+92Yeu528IVy1WJtTGIpHVN6Fc+jZbsAZqzdhDDd
+q9pKawKlDR2bW6mg7ItF2coYprMoLtHeFAwSUIg5WcMFUgGHxITFDQlscD2mR54Y
+I1otVWegrL2PDyKs2uk5B4Jwm+O/0fbyG+D3FIje9y++gh6Oqi/uwn8YUgnLl/4e
+Yf8POmMrUcOmJn4tXX4y6HtacNR3n0leby8T1dBShNAESuBdmEo8bmgAADh/brfP
+kzGHBdO+AyHEs87TRMZ4ofhspM6m1ZNyazy861xhoYjUcjIoD4vaVsEGIRT9/2HM
+4l8oq0OyarhZRLIpJ11SJQ==
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd0-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd0-key.pem
new file mode 100644
index 00000000000..1082f8f95ca
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd0-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAwM7rFfUDX0Kwoidp7h9haJdC3rYpti9OJTtRZznEWUjIW4eN
+lhYUZlfFDX9kbRZTIPlZSHbY1KtaWTvPn6DRLiflr9t8ArUScDt8a6imShekeYwz
+Q/OPnkH7sCccPw3C5iX3VbOxDYxFoQx52hwhPPg84f6ItXX5pPZvYpT8B6tQ3DF4
+TsfZzZ0DTqI98yWWc6mZPMrBXXh+FjawzlobvAJY0MSLeokwOnZ55iInRMAVYkYB
+X0hs37ylcf4wFN2aqBshue2XfB58/tXzSwZwIu+C6aZd6T2o9hT/G3ly9m2FoXJx
+lvFOoRjxtSGGdo3MyiUbZAoVviAdM17kEyx9wQIDAQABAoIBAHYTggadWOZSyidT
+6dyJXTcXQz1u45aN29aTQCPuTZ+8Ie9j0UKz3ORcJfJBLPhdbqVH7vSgy+NZ2OfC
+6EDC1LfmeRQ8XvKtf1A6LdUkoEnJttE1GgQoObDaic9z9CltPQmDALh8ZhZ86/tX
+z5vSFEzpXSetoH04dZ0fylDeGoZ6w3WsxWzNFM4uFHn5ZeyS6WfQMlJaY1hrEMQn
+JaJmw9PXs4kqYS6VTT+40g8EJcGG5yy9WP7xN/BSYebeiFZbfNA+HEweFVfFHtaC
+qqfqpyuQAyA2PHarXm14Fh0gESrbhVbQy1cd91CT8LaiszTurYj+eIVDuhxmO/xE
+doOHJM0CgYEAyNOrzTrKI6BN16CCmDR+q3UluL+LXQ1no9yuVMp3Xd5xzPze8vgF
+8TL6DUMck57Rj2+vMjX5K+GEw5D2f4qZnipRsRDcz4qrbIrxeX/PXQgZ/BBA3gts
+JcfkdCmEnEFGl7cPplgJ+erMeIwjTkMODHjApRAeSir83D1t6Xg0MgsCgYEA9cdL
+Xk88ynV4OyXKe5OrJaIc3RyNRL82571EjsCfRmy54FSgsxu4czsCyc1D3S5oX4Ni
+K1w/J8aGmqE/uSHyX2XN5s0QL1UvywM7kP0UfxJk6rPoa3F2RIt7+Ino26oHrhJA
+Id9QWyhC4nPIzQNmUtvriS2KTEDVhfJzDnfNGuMCgYBgPpEnWfVl/X1QyWvM5duO
+8CWT1LhFcBcrQKeLmzx5kBGZ29Y9OwAeCAAuOuuHE6X6x9t+hL9VeE7YKrD3jsTw
+N/fOk/Y+wIZHDo+LWuVWAHvzgRaVU4QQ1yt1QDB9cgsSyniFj8u4Kf60BvUknJ4u
+++/XK1JY2SE1q3rxWNoq4QKBgQDIANTZOxvaGTuszjT22RUV+9UHLdvbzMrOTBvF
+GwTB12pvHg7jkbRIuQWs2IfYLpKq3wx9AoU2Rfx1SrDDY9eLQBoNLmR0UBsjVk8b
+zobmXT6YK6z/+gXnA6bverJuRvAW/C0KI2KNu7ap9tS52IFk/ieuqst2/++CH+j3
+JhBXRwKBgFZ2E0nbgrk8CgEyI1dPGLIaN2E9QZxZNUfg01C0SgjBT/PKQS+Hx4Fk
+j0ABMqcfSwMFomezfxUfTzqaawlCMcpuQtE/1KtEfRMCwuvKdYcARDClH2p78e1P
+tZ9xiswa6PMXjLXc+jFy/kzgDS2JBaaZMP3C1NGbcDNpTxMQGKSe
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd0.csr b/pulsar-metadata/src/test/resources/ssl/cert/etcd0.csr
new file mode 100644
index 00000000000..75c12943408
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd0.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICeDCCAWACAQAwEDEOMAwGA1UEAxMFZXRjZDAwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDAzusV9QNfQrCiJ2nuH2Fol0Letim2L04lO1FnOcRZSMhb
+h42WFhRmV8UNf2RtFlMg+VlIdtjUq1pZO8+foNEuJ+Wv23wCtRJwO3xrqKZKF6R5
+jDND84+eQfuwJxw/DcLmJfdVs7ENjEWhDHnaHCE8+Dzh/oi1dfmk9m9ilPwHq1Dc
+MXhOx9nNnQNOoj3zJZZzqZk8ysFdeH4WNrDOWhu8AljQxIt6iTA6dnnmIidEwBVi
+RgFfSGzfvKVx/jAU3ZqoGyG57Zd8Hnz+1fNLBnAi74Lppl3pPaj2FP8beXL2bYWh
+cnGW8U6hGPG1IYZ2jczKJRtkChW+IB0zXuQTLH3BAgMBAAGgIzAhBgkqhkiG9w0B
+CQ4xFDASMBAGA1UdEQQJMAeCBWV0Y2QwMA0GCSqGSIb3DQEBCwUAA4IBAQAbgiK2
+UV27VhX5SxQP49IhEzfyzzOzaYPArkDrTvhiKUeQHDfiVa5C6KrhjqFdSxWim5sg
+345jb1fUjdkXQC2kSVW2MXIPUSqg+0XMorH21qfjr8w0+D3CjCEJaLFFaRX7jNAc
+hZlKaHF2268td6P11bK35z64ZcO6vuYfyXWig29gJNj7zADUX2UJAuIipyXWoWCp
+sEkX5c/ATClLxU2kJiU6QjMkrPivDxG3ihlcwuhxGjF+ABpgGTzcnyRO9KnGPPkD
+EBjfIUYl0J+Fp2dshDqQATJG+Fo11TobQ6swcSnIRwHHoiRxbNRV4SDKUwA4JvOB
+pAKINclelCs62UeW
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd0.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd0.pem
new file mode 100644
index 00000000000..0438501f59c
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd0.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUOtnvopLqgxSAOKGQHE0KC13nQOYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwIBcNMjIwNDI5MDQyMjAwWhgPMjEyMjA0MDUwNDIy
+MDBaMBAxDjAMBgNVBAMTBWV0Y2QwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAwM7rFfUDX0Kwoidp7h9haJdC3rYpti9OJTtRZznEWUjIW4eNlhYUZlfF
+DX9kbRZTIPlZSHbY1KtaWTvPn6DRLiflr9t8ArUScDt8a6imShekeYwzQ/OPnkH7
+sCccPw3C5iX3VbOxDYxFoQx52hwhPPg84f6ItXX5pPZvYpT8B6tQ3DF4TsfZzZ0D
+TqI98yWWc6mZPMrBXXh+FjawzlobvAJY0MSLeokwOnZ55iInRMAVYkYBX0hs37yl
+cf4wFN2aqBshue2XfB58/tXzSwZwIu+C6aZd6T2o9hT/G3ly9m2FoXJxlvFOoRjx
+tSGGdo3MyiUbZAoVviAdM17kEyx9wQIDAQABo4GSMIGPMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
+BgNVHQ4EFgQUOcvtfMhXiC+ykG+GG+o6CqMLSKkwHwYDVR0jBBgwFoAUHSjKeto9
+GyIxoJunhUUSs+2qfv0wEAYDVR0RBAkwB4IFZXRjZDAwDQYJKoZIhvcNAQELBQAD
+ggEBAMHxcaHkHtz/COoNVrvZdNc4BkxV6nNTa+iK0q2lSkpDbyYMUcrIX/2okV53
+7iwhfMW+nt1VI0iTVyyxrZw8LVRwQr1iq2Jc11EVCmuvNCFLdOpXyG8KOiGEchtl
+jgI2iUDKlF/sYgPA/C4Pu9g7+Ulh1/OmFuvdnTeHNlezxv7QNgzR4tALXOkvfc7u
+jOoprMlYe7cFxdh6TZPn1Jsz2Yp3YqO7hUSPYDOgUk+1Zzk9umAa+J5uZ0oluPxV
+A+24DNKeDpQZ03Ca2qXWnilAZEql/j4zlDKKyT0dNxQ5P+LutM5weKvw+n7+hKnB
+KNw/2MngvEbsvqNIxgJV/l8JRv0=
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd1-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd1-key.pem
new file mode 100644
index 00000000000..d1e63e6d5f2
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd1-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAofyM/cb4lXoWXHCkySXLqYFFt6MfTfHWVGn0gh0Yz7Yck0G8
+KuITwBbNV8dcTGNklU8CGIH9XKiG092ozA0NYLvORlBCmEdFxl6n/pnGArQqhK8a
+SwG3rvqz9/WaHqrRozUuSH01UR2WNZYK5V8+5z6EHIwqgGkh+qunaP9o+45hzD3z
+CNn/7Om9hRDYKXp94Gty5A9yiF+R9VzqpEgEfyLRelbaiWVxg5B/+fGUkGO0pXEk
+blnvFQMEHPkQUamL1Aow6uS7uucxR60ebLQeVthNYDsjRYrD5v9NmHdWTW0gEC7f
+41eMsPjsR9cm9jkgbct560b3+nm/DFeJyoce3wIDAQABAoIBABn0go7jQ8+HqW+U
+aQMF+FUnrokXLRvrJSGPfKsgiGo3zupVfRYN9YYw4SQwgHyI7IP0DezpXzRqmHf4
+K2TOrpcSYKHpmFIYEXkQj7B+6Thjr/BksjzUgJ8b8pxHkTNawY56Jfldfp6JtBjP
++tz33vdkj/yXBPRgZB5rRL7RNfwgegwy3E+8a1FGPsiva3UJ2aGpYpZBxtmtDLDJ
++QdNB9BtXxOI81E2pfiZ6VwKo9sjHHthuLKVT32FOrqSTUFO86CBopO1Qu/pdjYN
+nrXEXuzyX7x1oBK4W6xJbrQRRzY6CVzQ+9yi9TkGhHWUJywNTYTm+cIHAIWRneCj
+HLjwOCECgYEAyArbvDcC/T0krAZIGhbWmKFKZ1TFslPoKysNeJaV2YOzwaIiG94l
+AZeGh0lj5SQiEGyZ6CMxj4m9CdiJWPGnzLcRF7uhYZCR70LdQgMutVk+7Cmsl4cN
+CW7qiLQlimyl8iETFHE7gUG/HHGg9oKpQnIMXmvIx610Krapl7KepI8CgYEAz0x9
+h7fC5ZYDIomgr1ZCUN2jdvsOoUgkdNxVl+gmxKEnvfu/2lWu/WKYV/Zl81jgTpFh
+BYgg4x6f54ApzXi4wix4sbBgUpqQAeP0JGSR/zp3HHHvMbSVOEV+LrtQqw1WDFgG
+l9huWbC2keNo858p7RfXz64vEQ7UGrQZITdPKLECgYEAmTQ3tZ4sG+aH/3eQIOr8
+z0g3w4uZHUpFtUnztzcc7MmBEfsqRE2N6foyfconR4a0z/9cn/zUKCpFLRU5m3T4
+HkDWRmbc4evm2GdNLRFi5GDFX/lKxYIY6fCYjrnpRmqPbTEHe9B80jGYPCCKNkE7
+gDf4YRgLXWHqZgwfNp8GAOECgYEAp0XuqVwj/YSOJQlvSuzM+FnQdMrjJEuFd3Lv
+8nMXDWWJHymMx06KtNYhXiFqZPHbliqDYqZU+1AT8gceNDnsxGqEPcW4BA/Kkmvx
+7ofks1HnGWHqhG5WVqDk0CrATjalr7I8J70yVlddCt25K5TMjQ/HSkMgoaAYkS+w
+c3gg/RECgYEAleTU3cMFDdZN/DVLZtU2l052VcNOTWcYbzgVBIMwmcwe0F81LMdL
+TRs0z9x9hh7DxoMs2Iocc6xG/AiBsVFcDZxAdFiAUVbo8d+FLN7ON9YRlmLforwP
+OHucy3ixkvMD0bB8eR8FD+C0ddJrNObNnJEokzVnUirbXpAovKX7CAc=
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd1.csr b/pulsar-metadata/src/test/resources/ssl/cert/etcd1.csr
new file mode 100644
index 00000000000..5db2530b897
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd1.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICeDCCAWACAQAwEDEOMAwGA1UEAxMFZXRjZDEwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCh/Iz9xviVehZccKTJJcupgUW3ox9N8dZUafSCHRjPthyT
+Qbwq4hPAFs1Xx1xMY2SVTwIYgf1cqIbT3ajMDQ1gu85GUEKYR0XGXqf+mcYCtCqE
+rxpLAbeu+rP39ZoeqtGjNS5IfTVRHZY1lgrlXz7nPoQcjCqAaSH6q6do/2j7jmHM
+PfMI2f/s6b2FENgpen3ga3LkD3KIX5H1XOqkSAR/ItF6VtqJZXGDkH/58ZSQY7Sl
+cSRuWe8VAwQc+RBRqYvUCjDq5Lu65zFHrR5stB5W2E1gOyNFisPm/02Yd1ZNbSAQ
+Lt/jV4yw+OxH1yb2OSBty3nrRvf6eb8MV4nKhx7fAgMBAAGgIzAhBgkqhkiG9w0B
+CQ4xFDASMBAGA1UdEQQJMAeCBWV0Y2QxMA0GCSqGSIb3DQEBCwUAA4IBAQAvNlV+
+KH3lD9B8vmfdbIUBcTxBJ7IISp+xK/SAEI9A69zjjvU/Yhcb6FY7tqeZ6QmmwgCl
+HNYFl5dK52nT/q1dXXf10Ea8xYdHIGy7fnye+LJiVUV1oC3sSUVKgX+YXibbqM0I
+DcnjXKJsNh04sbsOpYcIwNUwi1srfar1IXGi9fDjIqTbvJnig8rTEXCZ/JpNy2jO
+hGvKCF7zbBHM68uMSxfa1+Db0x1MB9MdjSN5R9o9IGbdpaich1wr8nA/sxmeI9Po
+isFZbOACgQ1fwLRRS/3s2IP+i/Ihcfj0aQTfKS1U/IY8g3y5HRHDlCTg3QstdNLS
+J9ZCCRYDUvfBFyUX
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd1.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd1.pem
new file mode 100644
index 00000000000..a5628efc640
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd1.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUVC1zhJiWmsQoNAvoIxgtCuOvESAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwIBcNMjIwNDI5MDQyMjAwWhgPMjEyMjA0MDUwNDIy
+MDBaMBAxDjAMBgNVBAMTBWV0Y2QxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAofyM/cb4lXoWXHCkySXLqYFFt6MfTfHWVGn0gh0Yz7Yck0G8KuITwBbN
+V8dcTGNklU8CGIH9XKiG092ozA0NYLvORlBCmEdFxl6n/pnGArQqhK8aSwG3rvqz
+9/WaHqrRozUuSH01UR2WNZYK5V8+5z6EHIwqgGkh+qunaP9o+45hzD3zCNn/7Om9
+hRDYKXp94Gty5A9yiF+R9VzqpEgEfyLRelbaiWVxg5B/+fGUkGO0pXEkblnvFQME
+HPkQUamL1Aow6uS7uucxR60ebLQeVthNYDsjRYrD5v9NmHdWTW0gEC7f41eMsPjs
+R9cm9jkgbct560b3+nm/DFeJyoce3wIDAQABo4GSMIGPMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
+BgNVHQ4EFgQU8EEBqp4josNuFliczxhJqNMBFcIwHwYDVR0jBBgwFoAUHSjKeto9
+GyIxoJunhUUSs+2qfv0wEAYDVR0RBAkwB4IFZXRjZDEwDQYJKoZIhvcNAQELBQAD
+ggEBALOI1S/C3hq1wxU1izFkyZ0RmRaKMyk9tVqvtcCUMAMMDN0RrDceGqoigx/D
+AdlaqnnO+2CeMKBqZLcPajUsoVErl0T36MLliu9zQY2IrwzP7gaMi/VAYrNRM8JU
+JckqSa4galNUuOKWUWIaIOUDk3NkzrZ+t8F8l32Nu46IzfmfrQB08upibZtbvnZl
+0TtLtpeoMiPkCCI4iyCTSVynODpJ/XZjNge1Ha/5Z1rFfUA3go9W3TSn4DJZ9+Bt
+AottVPQhcLPoGChsir+kVrtc4lLfK97XUUytHnePF2bqw+MuS+q/WB8V9sjduEV8
+vkDcnwa8idv0FUtCUfjMNHqaAbM=
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd2-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd2-key.pem
new file mode 100644
index 00000000000..df46c27684b
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd2-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAzib8SHTPm4snGgIX5MA6s/w4Cd86GssmFmAv+ur0T8a8ldxI
+aMJTEeeKHdrqsEdU/zBM3WZ7/WqPJ72xEy20rwNQa5OhT075NKrY/J/Lw0SGUAD8
+YH4ukZ6ZFBwCXJ072HpESTU1Mz7hZVOXt2uDul7RqoEyx6miSQ8uZH1XS7/Y+2Mv
+5Ig7D4gpUBJNRhj7INCASIf5T6GsnzLRj+xsw4bL5pYF6GzWLczVDpij24qJyfaq
+6aVeux3et2JHTuDuuVEapSXJ8HFTFmqADcLtpg3T2tn+IZkxqyxqcxFdPDc5C9Sb
+YfYL7RsvcETYZpSLpxDNqJ/X1aAicZ6V9FLfrQIDAQABAoIBAQCgvU0gwdPOmCWY
+5eDzf99Fz3dyWxfXJGdCMyW2I9TzDyakQupCpKJY54yN0tADOttPTr3gM7Sh16Bf
+gEy9g3nKrG7cMnQofo9ZPY2LDJ24V4nSf7JJGpn8+Qiq4VwGEqNDG7J60n30wybD
+9vXpEm/XtW8N30dU3zOme6vUttaGPFxbeS+Zu2J6tbY696iY+NiPMRFsmHXNnqce
+JxZbd7FdOd2gQH3yIzVgXJFaDZL4qCwuN87yLRqIP6DS54bDcp6r/x4bAhy7fGC4
+wJTzcsEJWfSM3djaK/RyQtWF4IyxmXfRIhCYxoewNcS5/qEVNwpAVmPhmW6Yq9wT
+UpjTShkRAoGBANA03zTxDxbqWeTHn6sER38bVrzN44Uf7yDim3By6qpfW57ts0x8
+JPgvkHS2bnjqbBueDEdBTduFO32GGvsPu2Y/d3OSrB9hyw4Y1i7JfPMUGRWPTLAH
+ttIJP/v2Pi7RSpgUWOI3c/3BvbFTYxo0rTk8XlFLCy4wMX6Y6Sk6RXwPAoGBAP15
+Za+MmOwV1G1zE5xDIjbn/Wrq88v8xpTPdQebEBVJ4ZPh80QAH6vMFpLuGloK7v5X
+VyfpNVJ9EpGDozqjLNEVr4UTPFPmZTONA1NkBE7f0CmSDsT0AZn5stcWnWxoct0m
+ueVl8tAx1J7gxIKt8fxFiptYmrvKiElQVDbP4VyDAoGAIyZJ6G4p1fyKYI0y73Qx
+fcvnwMgheQADJtIa28kKxOiEVzhZ5LewphWhlZKW4+LvjVoJSJ5FvqGngXF6bJgj
+Uwez6tcAJ+wik6atwghSB9eExJcEo1MqEhttqePQa6b5qOMGfmpOMrDajW0+ddYe
+yGLgqWi54npQYMb/1e34fRECgYBH60D53VeqbMs4PtWKmdXEZHCX/JAuCcvCf1aC
+UPHwSGUz04F1gt95nE+vYtWhRMbo7ZkyHs5dWFWDailPQus9a/zAMmQEnjOKu/D8
+oT1Dr+ddVC7SnTUzHhErTCRglQY6T3mhaBrMDcd5SOcY+QfW/1hDmZFVxTKEMsGi
+R7t2xwKBgQC9mvqJgJ1p+1HtI9mWMPQTV6l1h9+S7O0wxWHvDUR6MGiKq/BeWvWR
+wH7gMfiR7zQKPLLFpEkO3e347ctVRAEy8vAZsaSoZZplJBTmHQqZAWvZtEOLJ73I
+j1rT7LWXJZXlfNyhVyVy3sOzLuzGK8i6j3uCDG8cTqXLcIrmfKorbQ==
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd2.csr b/pulsar-metadata/src/test/resources/ssl/cert/etcd2.csr
new file mode 100644
index 00000000000..05d0caed7c3
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd2.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICeDCCAWACAQAwEDEOMAwGA1UEAxMFZXRjZDIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDOJvxIdM+biycaAhfkwDqz/DgJ3zoayyYWYC/66vRPxryV
+3EhowlMR54od2uqwR1T/MEzdZnv9ao8nvbETLbSvA1Brk6FPTvk0qtj8n8vDRIZQ
+APxgfi6RnpkUHAJcnTvYekRJNTUzPuFlU5e3a4O6XtGqgTLHqaJJDy5kfVdLv9j7
+Yy/kiDsPiClQEk1GGPsg0IBIh/lPoayfMtGP7GzDhsvmlgXobNYtzNUOmKPbionJ
+9qrppV67Hd63YkdO4O65URqlJcnwcVMWaoANwu2mDdPa2f4hmTGrLGpzEV08NzkL
+1Jth9gvtGy9wRNhmlIunEM2on9fVoCJxnpX0Ut+tAgMBAAGgIzAhBgkqhkiG9w0B
+CQ4xFDASMBAGA1UdEQQJMAeCBWV0Y2QyMA0GCSqGSIb3DQEBCwUAA4IBAQBhSO0B
+82APD+vZYTSRKVUmMrIDxi0ODS9wWOZ4aAgspOHaPcMiPfzdZ6A2PF3Jr0FHyVRy
+iQjBXTNqrn8LHy5PX2Ulv+SaSxag70hNTF5titIxx8dEYKGQdXRDL1EvIvaD+uQQ
+EsJPwMz32HYOCpqEb6K71L5TTzx1DT0gr4g6UQcYUPJvFQ63+Nq8oe+MxGg3/RBE
+XCUTKYPDb2lh3d6A9EFxqsP3A1RMRwn87jLLELucCAe12EmGaahiR/i1vPdyhcIB
+hg8IAy9o9fJ1yUaYQwhd370DTx2zMkHsHhMO48fNcCnn2LiNwD6equllf0/dAt0z
+Gj6OEmb+imvdHODf
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/etcd2.pem b/pulsar-metadata/src/test/resources/ssl/cert/etcd2.pem
new file mode 100644
index 00000000000..ba1178f59fc
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/etcd2.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUPsu/hXrFHnwO+QepFNnnRyqrOhYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwIBcNMjIwNDI5MDQyMjAwWhgPMjEyMjA0MDUwNDIy
+MDBaMBAxDjAMBgNVBAMTBWV0Y2QyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAzib8SHTPm4snGgIX5MA6s/w4Cd86GssmFmAv+ur0T8a8ldxIaMJTEeeK
+HdrqsEdU/zBM3WZ7/WqPJ72xEy20rwNQa5OhT075NKrY/J/Lw0SGUAD8YH4ukZ6Z
+FBwCXJ072HpESTU1Mz7hZVOXt2uDul7RqoEyx6miSQ8uZH1XS7/Y+2Mv5Ig7D4gp
+UBJNRhj7INCASIf5T6GsnzLRj+xsw4bL5pYF6GzWLczVDpij24qJyfaq6aVeux3e
+t2JHTuDuuVEapSXJ8HFTFmqADcLtpg3T2tn+IZkxqyxqcxFdPDc5C9SbYfYL7Rsv
+cETYZpSLpxDNqJ/X1aAicZ6V9FLfrQIDAQABo4GSMIGPMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAd
+BgNVHQ4EFgQUyK4bh1Bldq10Vtc8h9k4WJFl9tEwHwYDVR0jBBgwFoAUHSjKeto9
+GyIxoJunhUUSs+2qfv0wEAYDVR0RBAkwB4IFZXRjZDIwDQYJKoZIhvcNAQELBQAD
+ggEBAJn8bt+9OZE2T87m6/fUcsp49XscYMM3TPn6mCaCHlsn2Ljh8yepMVtmB5PN
+cix7lQjFDld3uMG/YGj8pCJHm7sarI35ydKr/Stx2FaE+MdIL4FAtEgOn2mXbnEl
+7fsGRDnSVg4n83SuyXGpEAitwidHsIG8EGcbTz5gIYUrG/osxnkSQ4uf143+iIex
+TJyi9Eb0iIoXTtVDpXAXPtHZT7AJH2At0UQCrvBLgSsR+dkBHg0YYU+qCl6ylioZ
+i2TJ0VLVrCIYyYz7CqhmM0aIiPEmBr0wJM2SM976O4g6vaI147qMpOXSbxTFsmoF
+vvysiWAcRaAA7NaKl6/fPQOFeNM=
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/server-key.pem b/pulsar-metadata/src/test/resources/ssl/cert/server-key.pem
new file mode 100644
index 00000000000..c10e1944c70
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/server-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEArBH548+4tHYbuMxc09Ws+Ao+SZ/cTJlIBjn0H5q4Y9HoRIIf
+wI9Np2xuQ16R6gklijECdtQ/8HBvkoMSVycaQnu6ZiGN1kWVD+yp+dWP8jm+qdQQ
+AUrlwXL7Sd4t3zsy9wo1eC+0od1hzsqojzE175mVszej9WJQLs/Gn8iv068UKaUZ
+UqH7ZLe2hHuntkFOEFYSv13Ydf9QMbhRt0/FbcrWF5YV8aQQ6pk4F+390PUVoLZY
+592PJQzZy+qqoKtP0xNld4aLrW8uZ2dF82B72NmhnIs3+GmbqihJXpCbus1+F8px
+xUQXkDQnff3Y/e4fkkm27+xU/O38SiNp9zhgDQIDAQABAoIBAD4q6Hv0Sql4mp9z
+xn4Kj0m2ebaj+TX7k1gcjCT3A3OdHSvTT6GEpOu+c0AH5PQHhfadqmRAXzGBje4D
+SRcP2571kOzhZptp3Gc6fFb1X+KGFmrJYVb2NnhkEIIpsa0beFhk6diYc8yfbX0H
+H0tM3k3I0ZtEmQpJEOs4ymxkV5JE8S82kt2EjrdGBO7jAUeo9trus8pXjVwQFaFM
+7dKvCE1X5mZmnkFLgw6ZuZCERBqP2MYcQOELUXylCTrcXOmmDhRt+xhSa99N26We
+TW58oYrnvi5P6sA9seoCSXsoG4miFBAPBQgz91Y9rmbcF5t0+IYwtV0ibIoXQs+c
+COYQ+gkCgYEAxPzM6e3JO2o/8dD64YtoTYVE01CovTkuZVEhsrwe3+ZdtKHWWoC+
+IVYwDskjJV3DjuTC35q5PucocjJOTEKy6GTz22MVAwG2HnxJ0I/A9c1X2t8uYT1M
+Qj6/xqQaCEL8jbWSTGsDdE1F+4rTpa3w939ztymZNb0DLbZJ1k4JhlsCgYEA3549
+jhT/Xer6PfSnt1OVZtLA/s9vrT5FV2l6+V8VA2iWzUbQqAChu1CMRqt46FWR4zfX
+oj37T0TkfhbsUB354JpMm5dxjas09EoQbgzegyyOJ6I+7AzIFhvliv6aZgo7kUhC
+bUfRipvFJSHz4JYFX6eoI/7k2JQkiCP7kDi+D7cCgYEAsHF34jkZPFzpNeJoNco2
+/OjYQydGXWdwhVPsiToztX2UhY1IgPw8UYPvu+6xynB4D/AcFUlzkEJhWhhxRaGg
+usRiISpY0b6gsCHHxEZfIkxx2xx5YKQVQGkQQg8POkM2EICIsGZSS8RDYRJyjT6G
+TkodDfz2vMm2pSVYcdqocW0CgYEA11HmkaY0UrnNJdUrnYIVLQIopfMV5Ko+WLIP
+oIlmkXanJphWmD6rfNNNj0rTTFwA8to1f1rRXv3GSodGGssSbawFshg8Vh6SCdw7
+P8ySgZpoc09t6RtRmsBX4ePQxgp6WyVKGTH7Wc0uItgznYC3QekNJUPuFYFoP9Lu
+C2wnzrcCgYEAmTeYA2+ypIXy77r7KLnh1ikPVrHYACgMSWHe3mj+T5A1201goflv
+GW2C+nFrylQRWqJhm51VGmXvxXQH05AjAFctYcYGmJXWn5d+sAf2rwqSlRDuemOP
+cQKFpOCr8zchby9HNKfaeip0Nrz+WheFG1d/2GgX3CppayTE1vYEkxY=
+-----END RSA PRIVATE KEY-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/server.csr b/pulsar-metadata/src/test/resources/ssl/cert/server.csr
new file mode 100644
index 00000000000..da80648b35c
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/server.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICfDCCAWQCAQAwETEPMA0GA1UEAxMGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArBH548+4tHYbuMxc09Ws+Ao+SZ/cTJlIBjn0H5q4Y9Ho
+RIIfwI9Np2xuQ16R6gklijECdtQ/8HBvkoMSVycaQnu6ZiGN1kWVD+yp+dWP8jm+
+qdQQAUrlwXL7Sd4t3zsy9wo1eC+0od1hzsqojzE175mVszej9WJQLs/Gn8iv068U
+KaUZUqH7ZLe2hHuntkFOEFYSv13Ydf9QMbhRt0/FbcrWF5YV8aQQ6pk4F+390PUV
+oLZY592PJQzZy+qqoKtP0xNld4aLrW8uZ2dF82B72NmhnIs3+GmbqihJXpCbus1+
+F8pxxUQXkDQnff3Y/e4fkkm27+xU/O38SiNp9zhgDQIDAQABoCYwJAYJKoZIhvcN
+AQkOMRcwFTATBgNVHREEDDAKgghldGNkLXNzbDANBgkqhkiG9w0BAQsFAAOCAQEA
+BdrViUC7OEOnhq7eVICszhRxaF7UGx4fMJqNMZL5z5r2dq3MJsfoOTvqyWFhy5rV
+4aiHShan6Ip+O//yibk/U1jcO8pQt2e+NA/5BYWf4hFC53Z+jF23sxW7zvieMqiC
+19aXn1YHJe6mmosBj0Y8sic+aXOfo0D1JTUk5tCKPCrHsc7RqOgcHWBCBvFL7uWE
+toksP76YyP9hfLIZSWtaeQE/nQxD9XtUcHOE+HrUmy0rsQ/Ojxt0zSKVYYHGVRWi
+hVtevu5uJJv/dSCmd1B3D+xjWkwwrNjl44CPHnv6/0R9ngc2Hl71qQJDWqQdrJXn
+kwTL0NSke2C/57zA9c2P5A==
+-----END CERTIFICATE REQUEST-----
diff --git a/pulsar-metadata/src/test/resources/ssl/cert/server.pem b/pulsar-metadata/src/test/resources/ssl/cert/server.pem
new file mode 100644
index 00000000000..a7a088ab53b
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/cert/server.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRDCCAiygAwIBAgIUKj+0Z9SYp0apXYQMnIaGrABH2CkwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAxMCQ0EwIBcNMjIwNDI5MDQyMjAwWhgPMjEyMjA0MDUwNDIy
+MDBaMBExDzANBgNVBAMTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAKwR+ePPuLR2G7jMXNPVrPgKPkmf3EyZSAY59B+auGPR6ESCH8CPTads
+bkNekeoJJYoxAnbUP/Bwb5KDElcnGkJ7umYhjdZFlQ/sqfnVj/I5vqnUEAFK5cFy
++0neLd87MvcKNXgvtKHdYc7KqI8xNe+ZlbM3o/ViUC7Pxp/Ir9OvFCmlGVKh+2S3
+toR7p7ZBThBWEr9d2HX/UDG4UbdPxW3K1heWFfGkEOqZOBft/dD1FaC2WOfdjyUM
+2cvqqqCrT9MTZXeGi61vLmdnRfNge9jZoZyLN/hpm6ooSV6Qm7rNfhfKccVEF5A0
+J3392P3uH5JJtu/sVPzt/Eojafc4YA0CAwEAAaOBlTCBkjAOBgNVHQ8BAf8EBAMC
+BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw
+HQYDVR0OBBYEFLbqyodNVvHtcBa31enG3uOahaqsMB8GA1UdIwQYMBaAFB0oynra
+PRsiMaCbp4VFErPtqn79MBMGA1UdEQQMMAqCCGV0Y2Qtc3NsMA0GCSqGSIb3DQEB
+CwUAA4IBAQBooiAWyAQrplByLHpZlXkJ1Gz4KFCztXYEpAThuZtT/pXgfRZCIwpz
+2lcygCNWlqKbwqc5eomm0NdfYcDu0eiExWBfiVhzXJDWKbus3pzR6tpinKexmNU9
+3rQ3DNiaIOTUf6494mX2qKTATce0uWBRiVAzSi/Ng7EF/xTif4pRcKdY62bD0/1J
+Jwu0dUE4HctOAiqX8Iq9gO4m5EKtXs0Z6r7gX3+UPIOPMX7hMAm3G6gIrVsyveeR
+8UrDHx+rdtSY8y0I3jKbGDF9hLNflsbtmoGoK6jzbU2edqhHfx13QXsBbqZMEHLX
+qXPO4Sx3hcVQ6neOVzMFDIQJOOonFqBa
+-----END CERTIFICATE-----
diff --git a/pulsar-metadata/src/test/resources/ssl/generate-self-signed-certificates.sh b/pulsar-metadata/src/test/resources/ssl/generate-self-signed-certificates.sh
new file mode 100755
index 00000000000..ca90ac5ca42
--- /dev/null
+++ b/pulsar-metadata/src/test/resources/ssl/generate-self-signed-certificates.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+
+# Copied from https://github.com/etcd-io/jetcd/blob/ff7d698b046367d243a8d9d5cfe528f9bb0e933f/jetcd-core/src/test/resources/ssl/generate-self-signed-certificates.sh
+
+ROOT="$(cd "$(dirname $0)" && pwd)"
+
+CFSSL_HOME=${ROOT}/cfssl
+CERT_HOME=${ROOT}/cert
+
+mkdir -p $CFSSL_HOME
+mkdir -p $CERT_HOME
+
+OS="$(uname -s)"
+case $OS in
+    "Linux")
+      PLATFORM="linux_amd64"
+    ;;
+    "Darwin")
+      PLATFORM="darwin_amd64"
+    ;;
+esac
+
+curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_${PLATFORM}> cfssl/cfssl
+curl -L https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_${PLATFORM}> cfssl/cfssljson
+chmod +x cfssl/{cfssl,cfssljson}
+
+cd $CERT_HOME
+
+echo '{"CN":"CA","key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -initca - | $CFSSL_HOME/cfssljson -bare ca -
+echo '{"signing":{"default":{"expiry":"876000h","usages":["signing","key encipherment","server auth","client auth"]}}}' > ca-config.json
+
+export ADDRESS=etcd-ssl
+export NAME=server
+echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | $CFSSL_HOME/cfssljson -bare $NAME
+
+export ADDRESS=etcd0
+export NAME=etcd0
+echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | $CFSSL_HOME/cfssljson -bare $NAME
+
+export ADDRESS=etcd1
+export NAME=etcd1
+echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | $CFSSL_HOME/cfssljson -bare $NAME
+
+export ADDRESS=etcd2
+export NAME=etcd2
+echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | $CFSSL_HOME/cfssljson -bare $NAME
+
+export ADDRESS=
+export NAME=client
+echo '{"CN":"'$NAME'","hosts":[""],"key":{"algo":"rsa","size":2048}}' | $CFSSL_HOME/cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -hostname="$ADDRESS" - | $CFSSL_HOME/cfssljson -bare $NAME
+
+openssl pkcs8 -topk8 -inform PEM -outform PEM -in client-key.pem -out client-key-pk8.pem -nocrypt