You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ha...@apache.org on 2026/06/25 02:42:43 UTC
(iotdb) 01/01: Support Thrift client mutual TLS
This is an automated email from the ASF dual-hosted git repository.
HTHou pushed a commit to branch codex/thrift-client-mtls
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit e6499595cfc662f602effd54bd4af2384bf74efb
Author: HTHou <ha...@apache.org>
AuthorDate: Thu Jun 25 10:41:51 2026 +0800
Support Thrift client mutual TLS
---
.../it/env/cluster/config/MppCommonConfig.java | 6 +
.../env/cluster/config/MppSharedCommonConfig.java | 7 +
.../iotdb/it/env/cluster/env/AbstractEnv.java | 29 +++
.../it/env/remote/config/RemoteCommonConfig.java | 5 +
.../org/apache/iotdb/itbase/env/CommonConfig.java | 2 +
.../iotdb/session/it/IoTDBClientMutualSSLIT.java | 284 +++++++++++++++++++++
.../java/org/apache/iotdb/cli/AbstractCli.java | 44 ++++
.../src/main/java/org/apache/iotdb/cli/Cli.java | 23 +-
.../org/apache/iotdb/tool/common/Constants.java | 8 +
.../org/apache/iotdb/tool/common/OptionsUtil.java | 20 ++
.../apache/iotdb/tool/data/AbstractDataTool.java | 21 ++
.../iotdb/tool/schema/AbstractSchemaTool.java | 18 ++
.../main/java/org/apache/iotdb/jdbc/Config.java | 4 +
.../org/apache/iotdb/jdbc/IoTDBConnection.java | 4 +-
.../apache/iotdb/jdbc/IoTDBConnectionParams.java | 18 ++
.../src/main/java/org/apache/iotdb/jdbc/Utils.java | 6 +
.../test/java/org/apache/iotdb/jdbc/UtilsTest.java | 8 +
.../apache/iotdb/rpc/BaseRpcTransportFactory.java | 16 +-
.../iotdb/session/AbstractSessionBuilder.java | 2 +
.../org/apache/iotdb/session/NodesSupplier.java | 12 +
.../java/org/apache/iotdb/session/Session.java | 16 ++
.../apache/iotdb/session/SessionConnection.java | 18 +-
.../apache/iotdb/session/TableSessionBuilder.java | 24 ++
.../org/apache/iotdb/session/ThriftConnection.java | 6 +-
.../org/apache/iotdb/session/pool/SessionPool.java | 22 ++
.../session/pool/TableSessionPoolBuilder.java | 24 ++
.../iotdb/db/service/ExternalRPCService.java | 80 ++++--
.../conf/iotdb-system.properties.template | 7 +
.../apache/iotdb/commons/conf/CommonConfig.java | 11 +
.../iotdb/commons/conf/CommonDescriptor.java | 4 +
30 files changed, 707 insertions(+), 42 deletions(-)
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java
index 4dd7f857162..bd2fb84e0b6 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java
@@ -632,6 +632,12 @@ public class MppCommonConfig extends MppBaseConfig implements CommonConfig {
return this;
}
+ @Override
+ public CommonConfig setThriftSSLClientAuth(boolean thriftSSLClientAuth) {
+ setProperty("thrift_ssl_client_auth", String.valueOf(thriftSSLClientAuth));
+ return this;
+ }
+
@Override
public CommonConfig setEnableInternalSSL(boolean enableInternalSSL) {
setProperty("enable_internal_ssl", String.valueOf(enableInternalSSL));
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java
index 8e0398de309..b1c2a4f8d6b 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java
@@ -658,6 +658,13 @@ public class MppSharedCommonConfig implements CommonConfig {
return this;
}
+ @Override
+ public CommonConfig setThriftSSLClientAuth(boolean thriftSSLClientAuth) {
+ cnConfig.setThriftSSLClientAuth(thriftSSLClientAuth);
+ dnConfig.setThriftSSLClientAuth(thriftSSLClientAuth);
+ return this;
+ }
+
@Override
public CommonConfig setEnableInternalSSL(boolean enableInternalSSL) {
cnConfig.setEnableInternalSSL(enableInternalSSL);
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
index 342b4aba26d..e5d4e19834d 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
@@ -692,6 +692,10 @@ public abstract class AbstractEnv implements BaseEnv {
return Boolean.parseBoolean(getDataNodeCommonConfigProperty("enable_thrift_ssl", "false"));
}
+ private boolean isThriftSSLClientAuthEnabled() {
+ return Boolean.parseBoolean(getDataNodeCommonConfigProperty("thrift_ssl_client_auth", "false"));
+ }
+
private String getDataNodeCommonConfigProperty(final String key, final String defaultValue) {
return ((MppCommonConfig) clusterConfig.getDataNodeCommonConfig())
.getProperty(key, defaultValue);
@@ -711,6 +715,11 @@ public abstract class AbstractEnv implements BaseEnv {
putIfPresent(
info, Config.TRUST_STORE_PWD, getDataNodeCommonConfigProperty("trust_store_pwd", ""));
putIfPresent(info, Config.SSL_PROTOCOL, getClientSSLProtocol());
+ if (isThriftSSLClientAuthEnabled()) {
+ putIfPresent(info, Config.KEY_STORE, getDataNodeCommonConfigProperty("key_store_path", ""));
+ putIfPresent(
+ info, Config.KEY_STORE_PWD, getDataNodeCommonConfigProperty("key_store_pwd", ""));
+ }
}
return info;
}
@@ -728,6 +737,11 @@ public abstract class AbstractEnv implements BaseEnv {
.trustStore(getDataNodeCommonConfigProperty("trust_store_path", ""))
.trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", ""))
.sslProtocol(getClientSSLProtocol());
+ if (isThriftSSLClientAuthEnabled()) {
+ builder
+ .keyStore(getDataNodeCommonConfigProperty("key_store_path", ""))
+ .keyStorePwd(getDataNodeCommonConfigProperty("key_store_pwd", ""));
+ }
}
return builder;
}
@@ -739,6 +753,11 @@ public abstract class AbstractEnv implements BaseEnv {
.trustStore(getDataNodeCommonConfigProperty("trust_store_path", ""))
.trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", ""))
.sslProtocol(getClientSSLProtocol());
+ if (isThriftSSLClientAuthEnabled()) {
+ builder
+ .keyStore(getDataNodeCommonConfigProperty("key_store_path", ""))
+ .keyStorePwd(getDataNodeCommonConfigProperty("key_store_pwd", ""));
+ }
}
return builder;
}
@@ -750,6 +769,11 @@ public abstract class AbstractEnv implements BaseEnv {
.trustStore(getDataNodeCommonConfigProperty("trust_store_path", ""))
.trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", ""))
.sslProtocol(getClientSSLProtocol());
+ if (isThriftSSLClientAuthEnabled()) {
+ builder
+ .keyStore(getDataNodeCommonConfigProperty("key_store_path", ""))
+ .keyStorePwd(getDataNodeCommonConfigProperty("key_store_pwd", ""));
+ }
}
return builder;
}
@@ -761,6 +785,11 @@ public abstract class AbstractEnv implements BaseEnv {
.trustStore(getDataNodeCommonConfigProperty("trust_store_path", ""))
.trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", ""))
.sslProtocol(getClientSSLProtocol());
+ if (isThriftSSLClientAuthEnabled()) {
+ builder
+ .keyStore(getDataNodeCommonConfigProperty("key_store_path", ""))
+ .keyStorePwd(getDataNodeCommonConfigProperty("key_store_pwd", ""));
+ }
}
return builder;
}
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java
index a2d10c01009..ba1f7106dd6 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java
@@ -447,6 +447,11 @@ public class RemoteCommonConfig implements CommonConfig {
return this;
}
+ @Override
+ public CommonConfig setThriftSSLClientAuth(boolean thriftSSLClientAuth) {
+ return this;
+ }
+
@Override
public CommonConfig setSubscriptionPrefetchTsFileBatchMaxDelayInMs(
int subscriptionPrefetchTsFileBatchMaxDelayInMs) {
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java b/integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java
index 5b51a6a8cf9..5a7a004fa88 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java
@@ -203,6 +203,8 @@ public interface CommonConfig {
CommonConfig setEnableThriftClientSSL(boolean enableThriftClientSSL);
+ CommonConfig setThriftSSLClientAuth(boolean thriftSSLClientAuth);
+
CommonConfig setEnableInternalSSL(boolean enableInternalSSL);
CommonConfig setKeyStorePath(String keyStorePath);
diff --git a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientMutualSSLIT.java b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientMutualSSLIT.java
new file mode 100644
index 00000000000..0feb9ba6f30
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientMutualSSLIT.java
@@ -0,0 +1,284 @@
+/*
+ * 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.iotdb.session.it;
+
+import org.apache.iotdb.isession.ISession;
+import org.apache.iotdb.isession.ITableSession;
+import org.apache.iotdb.isession.SessionConfig;
+import org.apache.iotdb.isession.SessionDataSet;
+import org.apache.iotdb.isession.pool.ISessionPool;
+import org.apache.iotdb.isession.pool.SessionDataSetWrapper;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.jdbc.Config;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.session.Session;
+import org.apache.iotdb.session.TableSessionBuilder;
+import org.apache.iotdb.session.pool.SessionPool;
+
+import org.apache.tsfile.read.common.RowRecord;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({
+ LocalStandaloneIT.class,
+ ClusterIT.class,
+ TableLocalStandaloneIT.class,
+ TableClusterIT.class
+})
+public class IoTDBClientMutualSSLIT {
+
+ private static final String STORE_PASSWORD = "thrift";
+ private static String keyDir;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ keyDir =
+ System.getProperty("user.dir")
+ + File.separator
+ + "target"
+ + File.separator
+ + "test-classes"
+ + File.separator;
+
+ EnvFactory.getEnv()
+ .getConfig()
+ .getCommonConfig()
+ .setEnableThriftClientSSL(true)
+ .setThriftSSLClientAuth(true)
+ .setKeyStorePath(keyStorePath())
+ .setKeyStorePwd(STORE_PASSWORD)
+ .setTrustStorePath(trustStorePath())
+ .setTrustStorePwd(STORE_PASSWORD)
+ .setSslProtocol(SessionConfig.DEFAULT_SSL_PROTOCOL);
+ EnvFactory.getEnv().initClusterEnvironment();
+ }
+
+ @After
+ public void tearDown() {
+ try (ISession session = newMutualSSLSession()) {
+ deleteTreeDatabase(session, "root.client_mtls_tree");
+ deleteTreeDatabase(session, "root.client_mtls_pool");
+ deleteTreeDatabase(session, "root.client_mtls_jdbc");
+ } catch (Exception ignored) {
+ // ignored
+ }
+ try (ITableSession session = newMutualSSLTableSession()) {
+ session.executeNonQueryStatement("DROP DATABASE IF EXISTS client_mtls_table");
+ } catch (Exception ignored) {
+ // ignored
+ }
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void sslClientWithoutKeyStoreCanNotConnectWhenClientAuthRequired() {
+ final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+ final Session session =
+ new Session.Builder()
+ .host(dataNode.getIp())
+ .port(dataNode.getPort())
+ .useSSL(true)
+ .trustStore(trustStorePath())
+ .trustStorePwd(STORE_PASSWORD)
+ .build();
+
+ assertThrows(IoTDBConnectionException.class, session::open);
+ }
+
+ @Test
+ public void treeSessionCanConnectWithMutualSSL() throws Exception {
+ try (ISession session = newMutualSSLSession()) {
+ session.executeNonQueryStatement("CREATE DATABASE root.client_mtls_tree");
+ session.executeNonQueryStatement(
+ "CREATE TIMESERIES root.client_mtls_tree.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN");
+ session.executeNonQueryStatement(
+ "INSERT INTO root.client_mtls_tree.d1(time, s1) VALUES (1, 11)");
+
+ try (SessionDataSet dataSet =
+ session.executeQueryStatement("SELECT s1 FROM root.client_mtls_tree.d1")) {
+ assertTrue(dataSet.hasNext());
+ final RowRecord record = dataSet.next();
+ assertEquals(1L, record.getTimestamp());
+ assertEquals(11, record.getFields().get(0).getIntV());
+ assertFalse(dataSet.hasNext());
+ }
+ }
+ }
+
+ @Test
+ public void sessionPoolCanConnectWithMutualSSL() throws Exception {
+ final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+ final ISessionPool pool =
+ new SessionPool.Builder()
+ .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString()))
+ .maxSize(1)
+ .useSSL(true)
+ .trustStore(trustStorePath())
+ .trustStorePwd(STORE_PASSWORD)
+ .keyStore(keyStorePath())
+ .keyStorePwd(STORE_PASSWORD)
+ .build();
+ try {
+ pool.executeNonQueryStatement("CREATE DATABASE root.client_mtls_pool");
+ pool.executeNonQueryStatement(
+ "CREATE TIMESERIES root.client_mtls_pool.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN");
+ pool.executeNonQueryStatement(
+ "INSERT INTO root.client_mtls_pool.d1(time, s1) VALUES (1, 22)");
+
+ try (SessionDataSetWrapper dataSet =
+ pool.executeQueryStatement("SELECT s1 FROM root.client_mtls_pool.d1")) {
+ assertTrue(dataSet.hasNext());
+ final RowRecord record = dataSet.next();
+ assertEquals(1L, record.getTimestamp());
+ assertEquals(22, record.getFields().get(0).getIntV());
+ assertFalse(dataSet.hasNext());
+ }
+ } finally {
+ pool.close();
+ }
+ }
+
+ @Test
+ public void tableSessionCanConnectWithMutualSSL() throws Exception {
+ try (ITableSession session = newMutualSSLTableSession()) {
+ session.executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS client_mtls_table");
+ session.executeNonQueryStatement("USE client_mtls_table");
+ session.executeNonQueryStatement(
+ "CREATE TABLE IF NOT EXISTS mtls_table (tag1 STRING TAG, value INT32 FIELD)");
+ session.executeNonQueryStatement(
+ "INSERT INTO mtls_table(time, tag1, value) VALUES (1, 'tag1', 33)");
+
+ try (SessionDataSet dataSet =
+ session.executeQueryStatement("SELECT time, value FROM mtls_table WHERE tag1 = 'tag1'")) {
+ assertTrue(dataSet.hasNext());
+ final RowRecord record = dataSet.next();
+ assertEquals(1L, record.getFields().get(0).getLongV());
+ assertEquals(33, record.getFields().get(1).getIntV());
+ assertFalse(dataSet.hasNext());
+ }
+ }
+ }
+
+ @Test
+ public void jdbcCanConnectWithMutualSSL() throws Exception {
+ final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+
+ try (Connection connection =
+ DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + dataNode.getIpAndPortString(), mutualSSLProperties());
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE root.client_mtls_jdbc");
+ statement.execute(
+ "CREATE TIMESERIES root.client_mtls_jdbc.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN");
+ statement.execute("INSERT INTO root.client_mtls_jdbc.d1(time, s1) VALUES (1, 44)");
+
+ try (ResultSet resultSet =
+ statement.executeQuery("SELECT s1 FROM root.client_mtls_jdbc.d1")) {
+ assertTrue(resultSet.next());
+ assertEquals(1L, resultSet.getLong(1));
+ assertEquals(44, resultSet.getInt(2));
+ assertFalse(resultSet.next());
+ }
+ }
+ }
+
+ private static ISession newMutualSSLSession() throws IoTDBConnectionException {
+ final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+ final Session session =
+ new Session.Builder()
+ .host(dataNode.getIp())
+ .port(dataNode.getPort())
+ .useSSL(true)
+ .trustStore(trustStorePath())
+ .trustStorePwd(STORE_PASSWORD)
+ .keyStore(keyStorePath())
+ .keyStorePwd(STORE_PASSWORD)
+ .build();
+ session.open();
+ return session;
+ }
+
+ private static ITableSession newMutualSSLTableSession() throws IoTDBConnectionException {
+ final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0);
+ return new TableSessionBuilder()
+ .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString()))
+ .useSSL(true)
+ .trustStore(trustStorePath())
+ .trustStorePwd(STORE_PASSWORD)
+ .keyStore(keyStorePath())
+ .keyStorePwd(STORE_PASSWORD)
+ .build();
+ }
+
+ private static Properties mutualSSLProperties() {
+ final Properties properties = new Properties();
+ properties.put("user", SessionConfig.DEFAULT_USER);
+ properties.put("password", SessionConfig.DEFAULT_PASSWORD);
+ properties.put(Config.USE_SSL, Boolean.TRUE.toString());
+ properties.put(Config.TRUST_STORE, trustStorePath());
+ properties.put(Config.TRUST_STORE_PWD, STORE_PASSWORD);
+ properties.put(Config.KEY_STORE, keyStorePath());
+ properties.put(Config.KEY_STORE_PWD, STORE_PASSWORD);
+ return properties;
+ }
+
+ private void deleteTreeDatabase(final ISession session, final String database) {
+ try {
+ session.executeNonQueryStatement("DELETE DATABASE " + database);
+ } catch (Exception ignored) {
+ // ignored
+ }
+ }
+
+ private static String keyStorePath() {
+ return keyDir + "test-keystore";
+ }
+
+ private static String trustStorePath() {
+ return keyDir + "test-truststore";
+ }
+}
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
index 017f0a1a6ca..088131c2570 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/AbstractCli.java
@@ -74,8 +74,10 @@ public abstract class AbstractCli {
static final String USE_SSL_ARGS = "usessl";
static final String TRUST_STORE_ARGS = "ts";
+ static final String KEY_STORE_ARGS = "ks";
static final String TRUST_STORE_PWD_ARGS = "tpw";
+ static final String KEY_STORE_PWD_ARGS = "kpw";
static final String SSL_PROTOCOL_ARGS = "ssl_protocol";
@@ -83,8 +85,10 @@ public abstract class AbstractCli {
private static final String USE_SSL = "use_ssl";
private static final String TRUST_STORE = "trust_store";
+ private static final String KEY_STORE = "key_store";
private static final String TRUST_STORE_PWD = "trust_store_pwd";
+ private static final String KEY_STORE_PWD = "key_store_pwd";
private static final String SSL_PROTOCOL = "ssl_protocol";
private static final String NULL = "null";
@@ -135,6 +139,8 @@ public abstract class AbstractCli {
static String trustStore;
// TODO: Make non-static
static String trustStorePwd;
+ static String keyStore;
+ static String keyStorePwd;
static String sslProtocol;
static String execute;
@@ -160,6 +166,8 @@ public abstract class AbstractCli {
keywordSet.add("-" + USE_SSL_ARGS);
keywordSet.add("-" + TRUST_STORE_ARGS);
keywordSet.add("-" + TRUST_STORE_PWD_ARGS);
+ keywordSet.add("-" + KEY_STORE_ARGS);
+ keywordSet.add("-" + KEY_STORE_PWD_ARGS);
keywordSet.add("-" + SSL_PROTOCOL_ARGS);
keywordSet.add("-" + EXECUTE_ARGS);
keywordSet.add("-" + ISO8601_ARGS);
@@ -219,6 +227,42 @@ public abstract class AbstractCli {
.build();
options.addOption(useSSL);
+ Option trustStore =
+ Option.builder(TRUST_STORE_ARGS)
+ .longOpt(TRUST_STORE)
+ .argName(TRUST_STORE)
+ .hasArg()
+ .desc("Trust store. (optional)")
+ .build();
+ options.addOption(trustStore);
+
+ Option trustStorePwd =
+ Option.builder(TRUST_STORE_PWD_ARGS)
+ .longOpt(TRUST_STORE_PWD)
+ .argName(TRUST_STORE_PWD)
+ .hasArg()
+ .desc("Trust store password. (optional)")
+ .build();
+ options.addOption(trustStorePwd);
+
+ Option keyStore =
+ Option.builder(KEY_STORE_ARGS)
+ .longOpt(KEY_STORE)
+ .argName(KEY_STORE)
+ .hasArg()
+ .desc("Key store for mutual SSL. (optional)")
+ .build();
+ options.addOption(keyStore);
+
+ Option keyStorePwd =
+ Option.builder(KEY_STORE_PWD_ARGS)
+ .longOpt(KEY_STORE_PWD)
+ .argName(KEY_STORE_PWD)
+ .hasArg()
+ .desc("Key store password for mutual SSL. (optional)")
+ .build();
+ options.addOption(keyStorePwd);
+
Option sslProtocol =
Option.builder(SSL_PROTOCOL_ARGS)
.longOpt(SSL_PROTOCOL)
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
index a9e911be6ed..63c3c5d1153 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/Cli.java
@@ -111,6 +111,12 @@ public class Cli extends AbstractCli {
info.setProperty("use_ssl", useSsl);
info.setProperty("trust_store", trustStore);
info.setProperty("trust_store_pwd", trustStorePwd);
+ if (keyStore != null) {
+ info.setProperty(Config.KEY_STORE, keyStore);
+ }
+ if (keyStorePwd != null) {
+ info.setProperty(Config.KEY_STORE_PWD, keyStorePwd);
+ }
if (sslProtocol != null) {
info.setProperty(Config.SSL_PROTOCOL, sslProtocol);
}
@@ -164,8 +170,21 @@ public class Cli extends AbstractCli {
useSsl = commandLine.getOptionValue(USE_SSL_ARGS);
sslProtocol = commandLine.getOptionValue(SSL_PROTOCOL_ARGS);
if (Boolean.parseBoolean(useSsl)) {
- trustStore = ctx.getLineReader().readLine("please input your trust_store:", '\0');
- trustStorePwd = ctx.getLineReader().readLine("please input your trust_store_pwd:", '\0');
+ trustStore = commandLine.getOptionValue(TRUST_STORE_ARGS);
+ if (trustStore == null) {
+ trustStore = ctx.getLineReader().readLine("please input your trust_store:", '\0');
+ }
+ trustStorePwd = commandLine.getOptionValue(TRUST_STORE_PWD_ARGS);
+ if (trustStorePwd == null) {
+ trustStorePwd = ctx.getLineReader().readLine("please input your trust_store_pwd:", '\0');
+ }
+ keyStore = commandLine.getOptionValue(KEY_STORE_ARGS);
+ keyStorePwd = commandLine.getOptionValue(KEY_STORE_PWD_ARGS);
+ if (keyStore != null && keyStorePwd == null) {
+ keyStorePwd = ctx.getLineReader().readLine("please input your key_store_pwd:", '\0');
+ } else if (keyStore == null && keyStorePwd != null) {
+ keyStore = ctx.getLineReader().readLine("please input your key_store:", '\0');
+ }
}
password = commandLine.getOptionValue(PW_ARGS);
if (password == null) {
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/Constants.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/Constants.java
index a1063260957..ed2d96dc860 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/Constants.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/Constants.java
@@ -68,6 +68,14 @@ public class Constants {
public static final String TRUST_STORE_PWD_NAME = "trust_store_password";
public static final String TRUST_STORE_PWD_DESC = "Trust store password. (optional)";
+ public static final String KEY_STORE_ARGS = "ks";
+ public static final String KEY_STORE_NAME = "key_store";
+ public static final String KEY_STORE_DESC = "Key store for mutual SSL. (optional)";
+
+ public static final String KEY_STORE_PWD_ARGS = "kpw";
+ public static final String KEY_STORE_PWD_NAME = "key_store_password";
+ public static final String KEY_STORE_PWD_DESC = "Key store password for mutual SSL. (optional)";
+
public static final String SSL_PROTOCOL_ARGS = "ssl_protocol";
public static final String SSL_PROTOCOL_NAME = "ssl_protocol";
public static final String SSL_PROTOCOL_DESC = "SSL protocol. (optional)";
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/OptionsUtil.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/OptionsUtil.java
index 1f79b303f6e..e7ca2317be6 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/OptionsUtil.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/common/OptionsUtil.java
@@ -133,6 +133,26 @@ public class OptionsUtil extends Constants {
.build();
options.addOption(opTrustStorePwd);
+ Option opKeyStore =
+ Option.builder(KEY_STORE_ARGS)
+ .longOpt(KEY_STORE_NAME)
+ .optionalArg(true)
+ .argName(KEY_STORE_NAME)
+ .hasArg()
+ .desc(KEY_STORE_DESC)
+ .build();
+ options.addOption(opKeyStore);
+
+ Option opKeyStorePwd =
+ Option.builder(KEY_STORE_PWD_ARGS)
+ .longOpt(KEY_STORE_PWD_NAME)
+ .optionalArg(true)
+ .argName(KEY_STORE_PWD_NAME)
+ .hasArg()
+ .desc(KEY_STORE_PWD_DESC)
+ .build();
+ options.addOption(opKeyStorePwd);
+
Option opSslProtocol =
Option.builder(SSL_PROTOCOL_ARGS)
.longOpt(SSL_PROTOCOL_NAME)
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/AbstractDataTool.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/AbstractDataTool.java
index 6ea79a5b582..07a21109c20 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/AbstractDataTool.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/AbstractDataTool.java
@@ -97,6 +97,8 @@ public abstract class AbstractDataTool {
protected static Boolean useSsl;
protected static String trustStore;
protected static String trustStorePwd;
+ protected static String keyStore;
+ protected static String keyStorePwd;
protected static String sslProtocol;
protected static Boolean aligned;
protected static String database;
@@ -140,6 +142,9 @@ public abstract class AbstractDataTool {
protected static Session.Builder configureSsl(Session.Builder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -148,6 +153,9 @@ public abstract class AbstractDataTool {
protected static SessionPool.Builder configureSsl(SessionPool.Builder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -156,6 +164,9 @@ public abstract class AbstractDataTool {
protected static TableSessionBuilder configureSsl(TableSessionBuilder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -164,6 +175,9 @@ public abstract class AbstractDataTool {
protected static TableSessionPoolBuilder configureSsl(TableSessionPoolBuilder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -219,6 +233,13 @@ public abstract class AbstractDataTool {
} else {
trustStorePwd = cliCtx.getLineReader().readLine("please input your trust_store_pwd:", '\0');
}
+ keyStore = commandLine.getOptionValue(Constants.KEY_STORE_ARGS);
+ keyStorePwd = commandLine.getOptionValue(Constants.KEY_STORE_PWD_ARGS);
+ if (keyStore != null && keyStorePwd == null) {
+ keyStorePwd = cliCtx.getLineReader().readLine("please input your key_store_pwd:", '\0');
+ } else if (keyStore == null && keyStorePwd != null) {
+ keyStore = cliCtx.getLineReader().readLine("please input your key_store:", '\0');
+ }
}
boolean hasPw = commandLine.hasOption(Constants.PW_ARGS);
if (hasPw) {
diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/AbstractSchemaTool.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/AbstractSchemaTool.java
index 2bee5f5d331..6f0e533065f 100644
--- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/AbstractSchemaTool.java
+++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/AbstractSchemaTool.java
@@ -55,6 +55,8 @@ public abstract class AbstractSchemaTool {
protected static Boolean useSsl;
protected static String trustStore;
protected static String trustStorePwd;
+ protected static String keyStore;
+ protected static String keyStorePwd;
protected static String sslProtocol;
protected static Session session;
protected static String queryPath;
@@ -78,6 +80,9 @@ public abstract class AbstractSchemaTool {
protected static Session.Builder configureSsl(Session.Builder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -86,6 +91,9 @@ public abstract class AbstractSchemaTool {
protected static SessionPool.Builder configureSsl(SessionPool.Builder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -94,6 +102,9 @@ public abstract class AbstractSchemaTool {
protected static TableSessionPoolBuilder configureSsl(TableSessionPoolBuilder builder) {
builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd);
+ if (keyStore != null) {
+ builder.keyStore(keyStore).keyStorePwd(keyStorePwd);
+ }
if (sslProtocol != null) {
builder.sslProtocol(sslProtocol);
}
@@ -147,6 +158,13 @@ public abstract class AbstractSchemaTool {
} else {
trustStorePwd = cliCtx.getLineReader().readLine("please input your trust_store_pwd:", '\0');
}
+ keyStore = commandLine.getOptionValue(Constants.KEY_STORE_ARGS);
+ keyStorePwd = commandLine.getOptionValue(Constants.KEY_STORE_PWD_ARGS);
+ if (keyStore != null && keyStorePwd == null) {
+ keyStorePwd = cliCtx.getLineReader().readLine("please input your key_store_pwd:", '\0');
+ } else if (keyStore == null && keyStorePwd != null) {
+ keyStore = cliCtx.getLineReader().readLine("please input your key_store:", '\0');
+ }
}
boolean hasPw = commandLine.hasOption(Constants.PW_ARGS);
if (hasPw) {
diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java
index d79656cc8d2..54aca784d15 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Config.java
@@ -82,6 +82,10 @@ public class Config {
public static final String TRUST_STORE_PWD = "trust_store_pwd";
+ public static final String KEY_STORE = "key_store";
+
+ public static final String KEY_STORE_PWD = "key_store_pwd";
+
public static final String SSL_PROTOCOL = "ssl_protocol";
static final String DEFAULT_SSL_PROTOCOL = "TLS";
diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
index 0dc81115dd0..9a6dc91ceb4 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
@@ -544,12 +544,14 @@ public class IoTDBConnection implements Connection {
if (params.isUseSSL()) {
transport =
- DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig(
+ DeepCopyRpcTransportFactory.INSTANCE.getTransport(
params.getHost(),
params.getPort(),
getNetworkTimeout(),
params.getTrustStore(),
params.getTrustStorePwd(),
+ params.getKeyStore(),
+ params.getKeyStorePwd(),
params.getSslProtocol());
} else {
transport =
diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
index 8bf51379c5c..6c9a148517c 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
@@ -51,6 +51,8 @@ public class IoTDBConnectionParams {
private boolean useSSL = false;
private String trustStore;
private String trustStorePwd;
+ private String keyStore;
+ private String keyStorePwd;
private String sslProtocol = Config.DEFAULT_SSL_PROTOCOL;
private String sqlDialect = TREE;
@@ -185,6 +187,22 @@ public class IoTDBConnectionParams {
this.trustStorePwd = trustStorePwd;
}
+ public String getKeyStore() {
+ return keyStore;
+ }
+
+ public void setKeyStore(String keyStore) {
+ this.keyStore = keyStore;
+ }
+
+ public String getKeyStorePwd() {
+ return keyStorePwd;
+ }
+
+ public void setKeyStorePwd(String keyStorePwd) {
+ this.keyStorePwd = keyStorePwd;
+ }
+
public String getSslProtocol() {
return sslProtocol;
}
diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
index ea205ef7267..cf4fcb7d056 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
@@ -138,6 +138,12 @@ public class Utils {
if (info.containsKey(Config.TRUST_STORE_PWD)) {
params.setTrustStorePwd(info.getProperty(Config.TRUST_STORE_PWD));
}
+ if (info.containsKey(Config.KEY_STORE)) {
+ params.setKeyStore(info.getProperty(Config.KEY_STORE));
+ }
+ if (info.containsKey(Config.KEY_STORE_PWD)) {
+ params.setKeyStorePwd(info.getProperty(Config.KEY_STORE_PWD));
+ }
if (info.containsKey(Config.SSL_PROTOCOL)) {
params.setSslProtocol(RpcSslUtils.normalizeProtocol(info.getProperty(Config.SSL_PROTOCOL)));
}
diff --git a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
index d201a9b2d45..0787886c03a 100644
--- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
+++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
@@ -163,11 +163,19 @@ public class UtilsTest {
@Test
public void testParseSslConfig() throws IoTDBURLException {
Properties properties = new Properties();
+ properties.setProperty(Config.TRUST_STORE, "/tmp/truststore.p12");
+ properties.setProperty(Config.TRUST_STORE_PWD, "trust_pass");
+ properties.setProperty(Config.KEY_STORE, "/tmp/keystore.p12");
+ properties.setProperty(Config.KEY_STORE_PWD, "key_pass");
IoTDBConnectionParams params =
Utils.parseUrl(
"jdbc:iotdb://127.0.0.1:6667?use_ssl=true&ssl_protocol=ProviderProtocol", properties);
assertTrue(params.isUseSSL());
assertEquals("ProviderProtocol", params.getSslProtocol());
+ assertEquals("/tmp/truststore.p12", params.getTrustStore());
+ assertEquals("trust_pass", params.getTrustStorePwd());
+ assertEquals("/tmp/keystore.p12", params.getKeyStore());
+ assertEquals("key_pass", params.getKeyStorePwd());
}
}
diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/BaseRpcTransportFactory.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/BaseRpcTransportFactory.java
index 03bf2070bca..57f1dd5a894 100644
--- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/BaseRpcTransportFactory.java
+++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/BaseRpcTransportFactory.java
@@ -19,8 +19,6 @@
package org.apache.iotdb.rpc;
-import org.apache.iotdb.rpc.i18n.RpcMessages;
-
import org.apache.thrift.transport.TMemoryInputTransport;
import org.apache.thrift.transport.TSSLTransportFactory;
import org.apache.thrift.transport.TSocket;
@@ -28,10 +26,6 @@ import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.transport.TTransportFactory;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
@SuppressWarnings("java:S1135") // ignore todos
public class BaseRpcTransportFactory extends TTransportFactory {
@@ -120,11 +114,9 @@ public class BaseRpcTransportFactory extends TTransportFactory {
throws TTransportException {
TSSLTransportFactory.TSSLTransportParameters params =
RpcSslUtils.createTSSLTransportParameters(sslProtocol);
- if (Files.exists(Paths.get(trustStore)) && Files.exists(Paths.get(keyStore))) {
- RpcSslUtils.setTrustStore(params, trustStore, trustStorePwd);
+ RpcSslUtils.setTrustStore(params, trustStore, trustStorePwd);
+ if (hasText(keyStore)) {
RpcSslUtils.setKeyStore(params, keyStore, keyStorePwd);
- } else {
- throw new TTransportException(new IOException(RpcMessages.COULD_NOT_LOAD_KEYSTORE));
}
TTransport transport = TSSLTransportFactory.getClientSocket(ip, port, timeout, params);
return inner.getTransport(transport);
@@ -150,4 +142,8 @@ public class BaseRpcTransportFactory extends TTransportFactory {
public static void setThriftMaxFrameSize(int thriftMaxFrameSize) {
BaseRpcTransportFactory.thriftMaxFrameSize = thriftMaxFrameSize;
}
+
+ private static boolean hasText(String value) {
+ return value != null && !value.trim().isEmpty();
+ }
}
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
index 873e6dd248d..44500ed3f10 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/AbstractSessionBuilder.java
@@ -63,6 +63,8 @@ public abstract class AbstractSessionBuilder {
public boolean useSSL = false;
public String trustStore;
public String trustStorePwd;
+ public String keyStore;
+ public String keyStorePwd;
public String sslProtocol = SessionConfig.DEFAULT_SSL_PROTOCOL;
// max retry count, if set to 0, means that we won't do any retry
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/NodesSupplier.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/NodesSupplier.java
index a41db581e9c..c1f43fa30d4 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/NodesSupplier.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/NodesSupplier.java
@@ -61,6 +61,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
private final boolean useSSL;
private final String trustStore;
private final String trustStorePwd;
+ private final String keyStore;
+ private final String keyStorePwd;
private final String sslProtocol;
private final boolean enableRPCCompression;
private final String userName;
@@ -96,6 +98,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
boolean useSSL,
String trustStore,
String trustStorePwd,
+ String keyStore,
+ String keyStorePwd,
String sslProtocol,
boolean enableRPCCompression,
String version) {
@@ -112,6 +116,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
useSSL,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol,
enableRPCCompression,
version);
@@ -135,6 +141,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
boolean useSSL,
String trustStore,
String trustStorePwd,
+ String keyStore,
+ String keyStorePwd,
String sslProtocol,
boolean enableRPCCompression,
String version) {
@@ -144,6 +152,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
this.useSSL = useSSL;
this.trustStore = trustStore;
this.trustStorePwd = trustStorePwd;
+ this.keyStore = keyStore;
+ this.keyStorePwd = keyStorePwd;
this.sslProtocol = sslProtocol;
this.enableRPCCompression = enableRPCCompression;
this.zoneId = zoneId == null ? ZoneId.systemDefault() : zoneId;
@@ -193,6 +203,8 @@ public class NodesSupplier implements INodeSupplier, Runnable {
useSSL,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol,
userName,
password,
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
index 648a63845b4..8d182122de5 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/Session.java
@@ -134,6 +134,8 @@ public class Session implements ISession {
protected boolean useSSL;
protected String trustStore;
protected String trustStorePwd;
+ protected String keyStore;
+ protected String keyStorePwd;
protected String sslProtocol;
/**
@@ -475,6 +477,8 @@ public class Session implements ISession {
this.useSSL = builder.useSSL;
this.trustStore = builder.trustStore;
this.trustStorePwd = builder.trustStorePwd;
+ this.keyStore = builder.keyStore;
+ this.keyStorePwd = builder.keyStorePwd;
this.sslProtocol = builder.sslProtocol;
this.enableAutoFetch = builder.enableAutoFetch;
this.maxRetryCount = builder.maxRetryCount;
@@ -545,6 +549,8 @@ public class Session implements ISession {
useSSL,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol,
enableRPCCompaction,
version.toString());
@@ -4438,6 +4444,16 @@ public class Session implements ISession {
return this;
}
+ public Builder keyStore(String keyStore) {
+ this.keyStore = keyStore;
+ return this;
+ }
+
+ public Builder keyStorePwd(String keyStorePwd) {
+ this.keyStorePwd = keyStorePwd;
+ return this;
+ }
+
public Builder sslProtocol(String sslProtocol) {
this.sslProtocol = sslProtocol;
return this;
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
index 468b59abb0c..ec91fc9f8c0 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/SessionConnection.java
@@ -153,7 +153,13 @@ public class SessionConnection {
this.database = database;
try {
init(
- endPoint, session.useSSL, session.trustStore, session.trustStorePwd, session.sslProtocol);
+ endPoint,
+ session.useSSL,
+ session.trustStore,
+ session.trustStorePwd,
+ session.keyStore,
+ session.keyStorePwd,
+ session.sslProtocol);
} catch (StatementExecutionException e) {
throw new IoTDBConnectionException(e.getMessage());
} catch (IoTDBConnectionException e) {
@@ -186,6 +192,8 @@ public class SessionConnection {
boolean useSSL,
String trustStore,
String trustStorePwd,
+ String keyStore,
+ String keyStorePwd,
String sslProtocol)
throws IoTDBConnectionException, StatementExecutionException {
DeepCopyRpcTransportFactory.setDefaultBufferCapacity(session.thriftDefaultBufferSize);
@@ -196,12 +204,14 @@ public class SessionConnection {
}
if (useSSL) {
transport =
- DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig(
+ DeepCopyRpcTransportFactory.INSTANCE.getTransport(
endPoint.getIp(),
endPoint.getPort(),
session.connectionTimeoutInMs,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol);
} else {
transport =
@@ -278,6 +288,8 @@ public class SessionConnection {
session.useSSL,
session.trustStore,
session.trustStorePwd,
+ session.keyStore,
+ session.keyStorePwd,
session.sslProtocol);
} catch (IoTDBConnectionException e) {
if (!reconnect()) {
@@ -1100,6 +1112,8 @@ public class SessionConnection {
session.useSSL,
session.trustStore,
session.trustStorePwd,
+ session.keyStore,
+ session.keyStorePwd,
session.sslProtocol);
connectedSuccess = true;
} catch (IoTDBConnectionException e) {
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
index e724c0fd532..2dca0d351dc 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/TableSessionBuilder.java
@@ -239,6 +239,30 @@ public class TableSessionBuilder extends AbstractSessionBuilder {
return this;
}
+ /**
+ * Sets the key store path for mutual SSL connections.
+ *
+ * @param keyStore the key store path.
+ * @return the current {@link TableSessionBuilder} instance.
+ * @defaultValue null
+ */
+ public TableSessionBuilder keyStore(String keyStore) {
+ this.keyStore = keyStore;
+ return this;
+ }
+
+ /**
+ * Sets the key store password for mutual SSL connections.
+ *
+ * @param keyStorePwd the key store password.
+ * @return the current {@link TableSessionBuilder} instance.
+ * @defaultValue null
+ */
+ public TableSessionBuilder keyStorePwd(String keyStorePwd) {
+ this.keyStorePwd = keyStorePwd;
+ return this;
+ }
+
/**
* Sets the SSL protocol for secure connections.
*
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/ThriftConnection.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/ThriftConnection.java
index 3ab3abd581d..2a7f970bd50 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/ThriftConnection.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/ThriftConnection.java
@@ -78,6 +78,8 @@ public class ThriftConnection {
boolean useSSL,
String trustStore,
String trustStorePwd,
+ String keyStore,
+ String keyStorePwd,
String sslProtocol,
String username,
String password,
@@ -90,12 +92,14 @@ public class ThriftConnection {
try {
if (useSSL) {
transport =
- DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig(
+ DeepCopyRpcTransportFactory.INSTANCE.getTransport(
endPoint.getIp(),
endPoint.getPort(),
connectionTimeoutInMs,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol);
} else {
transport =
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
index 8136b2be68f..3a9c56a5e50 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/SessionPool.java
@@ -117,6 +117,10 @@ public class SessionPool implements ISessionPool {
private String trustStorePwd;
+ private String keyStore;
+
+ private String keyStorePwd;
+
private String sslProtocol = SessionConfig.DEFAULT_SSL_PROTOCOL;
private ZoneId zoneId;
@@ -540,6 +544,8 @@ public class SessionPool implements ISessionPool {
this.useSSL = builder.useSSL;
this.trustStore = builder.trustStore;
this.trustStorePwd = builder.trustStorePwd;
+ this.keyStore = builder.keyStore;
+ this.keyStorePwd = builder.keyStorePwd;
this.sslProtocol = builder.sslProtocol;
this.maxRetryCount = builder.maxRetryCount;
this.retryIntervalInMs = builder.retryIntervalInMs;
@@ -598,6 +604,8 @@ public class SessionPool implements ISessionPool {
.useSSL(useSSL)
.trustStore(trustStore)
.trustStorePwd(trustStorePwd)
+ .keyStore(keyStore)
+ .keyStorePwd(keyStorePwd)
.sslProtocol(sslProtocol)
.maxRetryCount(maxRetryCount)
.retryIntervalInMs(retryIntervalInMs)
@@ -624,6 +632,8 @@ public class SessionPool implements ISessionPool {
.useSSL(useSSL)
.trustStore(trustStore)
.trustStorePwd(trustStorePwd)
+ .keyStore(keyStore)
+ .keyStorePwd(keyStorePwd)
.sslProtocol(sslProtocol)
.maxRetryCount(maxRetryCount)
.retryIntervalInMs(retryIntervalInMs)
@@ -669,6 +679,8 @@ public class SessionPool implements ISessionPool {
useSSL,
trustStore,
trustStorePwd,
+ keyStore,
+ keyStorePwd,
sslProtocol,
enableThriftCompression,
version.toString());
@@ -3645,6 +3657,16 @@ public class SessionPool implements ISessionPool {
return this;
}
+ public Builder keyStore(String keyStore) {
+ this.keyStore = keyStore;
+ return this;
+ }
+
+ public Builder keyStorePwd(String keyStorePwd) {
+ this.keyStorePwd = keyStorePwd;
+ return this;
+ }
+
public Builder sslProtocol(String sslProtocol) {
this.sslProtocol = sslProtocol;
return this;
diff --git a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
index 2c7aba1a452..ab3e1f1c6a6 100644
--- a/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
+++ b/iotdb-client/session/src/main/java/org/apache/iotdb/session/pool/TableSessionPoolBuilder.java
@@ -281,6 +281,30 @@ public class TableSessionPoolBuilder extends AbstractSessionPoolBuilder {
return this;
}
+ /**
+ * Sets the key store path for mutual SSL connections.
+ *
+ * @param keyStore the key store path.
+ * @return the current {@link TableSessionPoolBuilder} instance.
+ * @defaultValue null
+ */
+ public TableSessionPoolBuilder keyStore(String keyStore) {
+ this.keyStore = keyStore;
+ return this;
+ }
+
+ /**
+ * Sets the key store password for mutual SSL connections.
+ *
+ * @param keyStorePwd the key store password.
+ * @return the current {@link TableSessionPoolBuilder} instance.
+ * @defaultValue null
+ */
+ public TableSessionPoolBuilder keyStorePwd(String keyStorePwd) {
+ this.keyStorePwd = keyStorePwd;
+ return this;
+ }
+
/**
* Sets the SSL protocol for secure connections.
*
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
index dcc8690de8a..89650657a61 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
@@ -65,32 +65,56 @@ public class ExternalRPCService extends ThriftService implements ExternalRPCServ
@Override
public void initThriftServiceThread() throws IllegalAccessException {
try {
- thriftServiceThread =
- commonConfig.isEnableThriftClientSSL()
- ? new ThriftServiceThread(
- processor,
- getID().getName(),
- ThreadName.CLIENT_RPC_PROCESSOR.getName(),
- getBindIP(),
- getBindPort(),
- config.getRpcMaxConcurrentClientNum(),
- config.getThriftServerAwaitTimeForStopService(),
- new RPCServiceThriftHandler(impl),
- config.isRpcThriftCompressionEnable(),
- commonConfig.getKeyStorePath(),
- commonConfig.getKeyStorePwd(),
- ZeroCopyRpcTransportFactory.INSTANCE)
- : new ThriftServiceThread(
- processor,
- getID().getName(),
- ThreadName.CLIENT_RPC_PROCESSOR.getName(),
- getBindIP(),
- getBindPort(),
- config.getRpcMaxConcurrentClientNum(),
- config.getThriftServerAwaitTimeForStopService(),
- new RPCServiceThriftHandler(impl),
- config.isRpcThriftCompressionEnable(),
- ZeroCopyRpcTransportFactory.INSTANCE);
+ if (!commonConfig.isEnableThriftClientSSL()) {
+ thriftServiceThread =
+ new ThriftServiceThread(
+ processor,
+ getID().getName(),
+ ThreadName.CLIENT_RPC_PROCESSOR.getName(),
+ getBindIP(),
+ getBindPort(),
+ config.getRpcMaxConcurrentClientNum(),
+ config.getThriftServerAwaitTimeForStopService(),
+ new RPCServiceThriftHandler(impl),
+ config.isRpcThriftCompressionEnable(),
+ ZeroCopyRpcTransportFactory.INSTANCE);
+ } else if (commonConfig.isThriftSSLClientAuth()) {
+ if (!hasText(commonConfig.getTrustStorePath())) {
+ throw new IllegalAccessException(
+ "trust_store_path must be set when thrift_ssl_client_auth is true");
+ }
+ thriftServiceThread =
+ new ThriftServiceThread(
+ processor,
+ getID().getName(),
+ ThreadName.CLIENT_RPC_PROCESSOR.getName(),
+ getBindIP(),
+ getBindPort(),
+ config.getRpcMaxConcurrentClientNum(),
+ config.getThriftServerAwaitTimeForStopService(),
+ new RPCServiceThriftHandler(impl),
+ config.isRpcThriftCompressionEnable(),
+ commonConfig.getKeyStorePath(),
+ commonConfig.getKeyStorePwd(),
+ commonConfig.getTrustStorePath(),
+ commonConfig.getTrustStorePwd(),
+ ZeroCopyRpcTransportFactory.INSTANCE);
+ } else {
+ thriftServiceThread =
+ new ThriftServiceThread(
+ processor,
+ getID().getName(),
+ ThreadName.CLIENT_RPC_PROCESSOR.getName(),
+ getBindIP(),
+ getBindPort(),
+ config.getRpcMaxConcurrentClientNum(),
+ config.getThriftServerAwaitTimeForStopService(),
+ new RPCServiceThriftHandler(impl),
+ config.isRpcThriftCompressionEnable(),
+ commonConfig.getKeyStorePath(),
+ commonConfig.getKeyStorePwd(),
+ ZeroCopyRpcTransportFactory.INSTANCE);
+ }
} catch (RPCServiceException e) {
throw new IllegalAccessException(e.getMessage());
}
@@ -118,6 +142,10 @@ public class ExternalRPCService extends ThriftService implements ExternalRPCServ
return getBindPort();
}
+ private boolean hasText(String value) {
+ return value != null && !value.trim().isEmpty();
+ }
+
private static class RPCServiceHolder {
private static final ExternalRPCService INSTANCE = new ExternalRPCService();
diff --git a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
index 1e808d7d75a..6d75a72aaab 100644
--- a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
+++ b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
@@ -444,6 +444,13 @@ dn_metric_internal_reporter_type=MEMORY
# Privilege: SECURITY
enable_thrift_ssl=false
+# Whether client authentication is required for Thrift SSL connections.
+# This only takes effect when enable_thrift_ssl=true.
+# effectiveMode: restart
+# Datatype: boolean
+# Privilege: SECURITY
+thrift_ssl_client_auth=false
+
# Whether enable SSL for Rest Service
# effectiveMode: restart
# Datatype: boolean
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
index 53127fa2cdc..257b2a8ad51 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonConfig.java
@@ -490,6 +490,9 @@ public class CommonConfig {
/** Enable the Thrift Client ssl. */
private boolean enableThriftClientSSL = false;
+ /** Whether the external Thrift SSL service requires client certificate authentication. */
+ private boolean thriftSSLClientAuth = false;
+
/** Enable the cluster internal connection ssl. */
private boolean enableInternalSSL = false;
@@ -2998,6 +3001,14 @@ public class CommonConfig {
this.enableThriftClientSSL = enableThriftClientSSL;
}
+ public boolean isThriftSSLClientAuth() {
+ return thriftSSLClientAuth;
+ }
+
+ public void setThriftSSLClientAuth(boolean thriftSSLClientAuth) {
+ this.thriftSSLClientAuth = thriftSSLClientAuth;
+ }
+
public boolean isEnableInternalSSL() {
return enableInternalSSL;
}
diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
index 824ec639ef6..56e8587eb07 100644
--- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
+++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/conf/CommonDescriptor.java
@@ -679,6 +679,10 @@ public class CommonDescriptor {
Boolean.parseBoolean(
properties.getProperty(
"enable_thrift_ssl", Boolean.toString(config.isEnableThriftClientSSL()))));
+ config.setThriftSSLClientAuth(
+ Boolean.parseBoolean(
+ properties.getProperty(
+ "thrift_ssl_client_auth", Boolean.toString(config.isThriftSSLClientAuth()))));
config.setKeyStorePath(properties.getProperty("key_store_path", config.getKeyStorePath()));
config.setKeyStorePwd(properties.getProperty("key_store_pwd", config.getKeyStorePwd()));
config.setTrustStorePath(