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 <[email protected]> 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(
