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:42 UTC
(iotdb) branch codex/thrift-client-mtls created (now e6499595cfc)
This is an automated email from the ASF dual-hosted git repository.
HTHou pushed a change to branch codex/thrift-client-mtls
in repository https://gitbox.apache.org/repos/asf/iotdb.git
at e6499595cfc Support Thrift client mutual TLS
This branch includes the following new commits:
new e6499595cfc Support Thrift client mutual TLS
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(iotdb) 01/01: Support Thrift client mutual TLS
Posted by ha...@apache.org.
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(