diff --git a/external-service-impl/rest/pom.xml b/external-service-impl/rest/pom.xml index fbc02b6dfb86c..5287a25440cc8 100644 --- a/external-service-impl/rest/pom.xml +++ b/external-service-impl/rest/pom.xml @@ -38,13 +38,6 @@ org.glassfish.jersey.inject jersey-hk2 runtime - - - - jakarta.annotation - jakarta.annotation-api - - org.apache.iotdb @@ -87,11 +80,6 @@ jakarta.validation jakarta.validation-api - - - jakarta.annotation - jakarta.annotation-api - diff --git a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java index acba4e3039009..506facdb4ce25 100644 --- a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/RestService.java @@ -21,6 +21,7 @@ import org.apache.iotdb.externalservice.api.IExternalService; import org.apache.iotdb.rest.i18n.RestMessages; import org.apache.iotdb.rest.protocol.filter.ApiOriginFilter; +import org.apache.iotdb.rpc.RpcSslUtils; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; @@ -52,6 +53,7 @@ private void startSSL( String trustStorePath, String keyStorePwd, String trustStorePwd, + String sslProtocol, int idleTime, boolean clientAuth) { server = new Server(); @@ -61,6 +63,7 @@ private void startSSL( httpsConfig.addCustomizer(new SecureRequestCustomizer()); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + configureSSL(sslContextFactory, sslProtocol); sslContextFactory.setKeyStorePath(keyStorePath); sslContextFactory.setKeyStorePassword(keyStorePwd); if (clientAuth) { @@ -125,6 +128,7 @@ public void start() { config.getTrustStorePath(), config.getKeyStorePwd(), config.getTrustStorePwd(), + config.getSslProtocol(), config.getIdleTimeoutInSeconds(), config.isClientAuth()); } else { @@ -142,4 +146,9 @@ public void stop() { server.destroy(); } } + + private void configureSSL(SslContextFactory.Server sslContextFactory, String sslProtocol) { + String protocol = RpcSslUtils.normalizeProtocol(sslProtocol); + sslContextFactory.setProtocol(protocol); + } } diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppBaseConfig.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppBaseConfig.java index e0758de70c952..69b2e540e9682 100644 --- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppBaseConfig.java +++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppBaseConfig.java @@ -120,6 +120,10 @@ protected final void setProperty(@NotNull String key, String value) { } } + public final String getProperty(@NotNull String key, String defaultValue) { + return properties.getProperty(key, defaultValue); + } + /** Create an instance but with empty properties. */ public abstract MppBaseConfig emptyClone(); } 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 c7ff02002dc58..dc6dda073169c 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 @@ -635,6 +635,12 @@ public CommonConfig setEnforceStrongPassword(boolean enforceStrongPassword) { return this; } + @Override + public CommonConfig setEnableThriftClientSSL(boolean enableThriftClientSSL) { + setProperty("enable_thrift_ssl", String.valueOf(enableThriftClientSSL)); + return this; + } + @Override public CommonConfig setEnableInternalSSL(boolean enableInternalSSL) { setProperty("enable_internal_ssl", String.valueOf(enableInternalSSL)); @@ -665,6 +671,12 @@ public CommonConfig setTrustStorePwd(String trustStorePwd) { return this; } + @Override + public CommonConfig setSslProtocol(String sslProtocol) { + setProperty("ssl_protocol", sslProtocol); + return this; + } + @Override public CommonConfig setDatanodeMemoryProportion(String datanodeMemoryProportion) { setProperty("datanode_memory_proportion", datanodeMemoryProportion); 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 5ed27b2f5024e..67774979d28ee 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 @@ -659,6 +659,13 @@ public CommonConfig setEnforceStrongPassword(boolean enforceStrongPassword) { return this; } + @Override + public CommonConfig setEnableThriftClientSSL(boolean enableThriftClientSSL) { + cnConfig.setEnableThriftClientSSL(enableThriftClientSSL); + dnConfig.setEnableThriftClientSSL(enableThriftClientSSL); + return this; + } + @Override public CommonConfig setEnableInternalSSL(boolean enableInternalSSL) { cnConfig.setEnableInternalSSL(enableInternalSSL); @@ -694,6 +701,13 @@ public CommonConfig setTrustStorePwd(String trustStorePwd) { return this; } + @Override + public CommonConfig setSslProtocol(String sslProtocol) { + cnConfig.setSslProtocol(sslProtocol); + dnConfig.setSslProtocol(sslProtocol); + return this; + } + @Override public CommonConfig setDatanodeMemoryProportion(String datanodeMemoryProportion) { dnConfig.setDatanodeMemoryProportion(datanodeMemoryProportion); 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 0e7eefb3a4188..7bcee29be6e91 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 @@ -614,6 +614,83 @@ public void cleanClusterEnvironment() { clusterConfig = new MppClusterConfig(); } + private boolean isThriftClientSSLEnabled() { + return Boolean.parseBoolean(getDataNodeCommonConfigProperty("enable_thrift_ssl", "false")); + } + + private String getDataNodeCommonConfigProperty(final String key, final String defaultValue) { + return ((MppCommonConfig) clusterConfig.getDataNodeCommonConfig()) + .getProperty(key, defaultValue); + } + + private String getClientSSLProtocol() { + return getDataNodeCommonConfigProperty("ssl_protocol", SessionConfig.DEFAULT_SSL_PROTOCOL); + } + + private Properties constructConnectionProperties( + final String username, final String password, final String sqlDialect) { + final Properties info = BaseEnv.constructProperties(username, password, sqlDialect); + if (isThriftClientSSLEnabled()) { + info.put(Config.USE_SSL, Boolean.TRUE.toString()); + putIfPresent( + info, Config.TRUST_STORE, getDataNodeCommonConfigProperty("trust_store_path", "")); + putIfPresent( + info, Config.TRUST_STORE_PWD, getDataNodeCommonConfigProperty("trust_store_pwd", "")); + putIfPresent(info, Config.SSL_PROTOCOL, getClientSSLProtocol()); + } + return info; + } + + private void putIfPresent(final Properties properties, final String key, final String value) { + if (value != null && !value.isEmpty()) { + properties.put(key, value); + } + } + + private Session.Builder configureClientSSL(final Session.Builder builder) { + if (isThriftClientSSLEnabled()) { + builder + .useSSL(true) + .trustStore(getDataNodeCommonConfigProperty("trust_store_path", "")) + .trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", "")) + .sslProtocol(getClientSSLProtocol()); + } + return builder; + } + + private TableSessionBuilder configureClientSSL(final TableSessionBuilder builder) { + if (isThriftClientSSLEnabled()) { + builder + .useSSL(true) + .trustStore(getDataNodeCommonConfigProperty("trust_store_path", "")) + .trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", "")) + .sslProtocol(getClientSSLProtocol()); + } + return builder; + } + + private SessionPool.Builder configureClientSSL(final SessionPool.Builder builder) { + if (isThriftClientSSLEnabled()) { + builder + .useSSL(true) + .trustStore(getDataNodeCommonConfigProperty("trust_store_path", "")) + .trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", "")) + .sslProtocol(getClientSSLProtocol()); + } + return builder; + } + + private TableSessionPoolBuilder configureClientSSL(final TableSessionPoolBuilder builder) { + if (isThriftClientSSLEnabled()) { + builder + .useSSL(true) + .trustStore(getDataNodeCommonConfigProperty("trust_store_path", "")) + .trustStorePwd(getDataNodeCommonConfigProperty("trust_store_pwd", "")) + .sslProtocol(getClientSSLProtocol()); + } + return builder; + } + @Override public Connection getConnection( final String username, final String password, final String sqlDialect) throws SQLException { @@ -695,7 +772,8 @@ public ISession getSessionConnection() throws IoTDBConnectionException { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); final Session session = - new Session.Builder().host(dataNode.getIp()).port(dataNode.getPort()).build(); + configureClientSSL(new Session.Builder().host(dataNode.getIp()).port(dataNode.getPort())) + .build(); session.open(); return session; } @@ -705,10 +783,11 @@ public ISession getSessionConnection(ZoneId zoneId) throws IoTDBConnectionExcept final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); final Session session = - new Session.Builder() - .host(dataNode.getIp()) - .port(dataNode.getPort()) - .zoneId(zoneId) + configureClientSSL( + new Session.Builder() + .host(dataNode.getIp()) + .port(dataNode.getPort()) + .zoneId(zoneId)) .build(); session.open(); return session; @@ -720,11 +799,12 @@ public ISession getSessionConnection(final String userName, final String passwor final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); final Session session = - new Session.Builder() - .host(dataNode.getIp()) - .port(dataNode.getPort()) - .username(userName) - .password(password) + configureClientSSL( + new Session.Builder() + .host(dataNode.getIp()) + .port(dataNode.getPort()) + .username(userName) + .password(password)) .build(); session.open(); return session; @@ -734,16 +814,17 @@ public ISession getSessionConnection(final String userName, final String passwor public ISession getSessionConnection(final List nodeUrls) throws IoTDBConnectionException { final Session session = - new Session.Builder() - .nodeUrls(nodeUrls) - .username(SessionConfig.DEFAULT_USER) - .password(SessionConfig.DEFAULT_PASSWORD) - .fetchSize(SessionConfig.DEFAULT_FETCH_SIZE) - .zoneId(null) - .thriftDefaultBufferSize(SessionConfig.DEFAULT_INITIAL_BUFFER_CAPACITY) - .thriftMaxFrameSize(SessionConfig.DEFAULT_MAX_FRAME_SIZE) - .enableRedirection(SessionConfig.DEFAULT_REDIRECTION_MODE) - .version(SessionConfig.DEFAULT_VERSION) + configureClientSSL( + new Session.Builder() + .nodeUrls(nodeUrls) + .username(SessionConfig.DEFAULT_USER) + .password(SessionConfig.DEFAULT_PASSWORD) + .fetchSize(SessionConfig.DEFAULT_FETCH_SIZE) + .zoneId(null) + .thriftDefaultBufferSize(SessionConfig.DEFAULT_INITIAL_BUFFER_CAPACITY) + .thriftMaxFrameSize(SessionConfig.DEFAULT_MAX_FRAME_SIZE) + .enableRedirection(SessionConfig.DEFAULT_REDIRECTION_MODE) + .version(SessionConfig.DEFAULT_VERSION)) .build(); session.open(); return session; @@ -753,8 +834,9 @@ public ISession getSessionConnection(final List nodeUrls) public ITableSession getTableSessionConnection() throws IoTDBConnectionException { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new TableSessionBuilder() - .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + return configureClientSSL( + new TableSessionBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString()))) .build(); } @@ -763,10 +845,11 @@ public ITableSession getTableSessionConnection(String userName, String password) throws IoTDBConnectionException { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new TableSessionBuilder() - .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) - .username(userName) - .password(password) + return configureClientSSL( + new TableSessionBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + .username(userName) + .password(password)) .build(); } @@ -775,23 +858,25 @@ public ITableSession getTableSessionConnectionWithDB(final String database) throws IoTDBConnectionException { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new TableSessionBuilder() - .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) - .database(database) + return configureClientSSL( + new TableSessionBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + .database(database)) .build(); } public ITableSession getTableSessionConnection(List nodeUrls) throws IoTDBConnectionException { - return new TableSessionBuilder() - .nodeUrls(nodeUrls) - .username(SessionConfig.DEFAULT_USER) - .password(SessionConfig.DEFAULT_PASSWORD) - .fetchSize(SessionConfig.DEFAULT_FETCH_SIZE) - .zoneId(null) - .thriftDefaultBufferSize(SessionConfig.DEFAULT_INITIAL_BUFFER_CAPACITY) - .thriftMaxFrameSize(SessionConfig.DEFAULT_MAX_FRAME_SIZE) - .enableRedirection(SessionConfig.DEFAULT_REDIRECTION_MODE) + return configureClientSSL( + new TableSessionBuilder() + .nodeUrls(nodeUrls) + .username(SessionConfig.DEFAULT_USER) + .password(SessionConfig.DEFAULT_PASSWORD) + .fetchSize(SessionConfig.DEFAULT_FETCH_SIZE) + .zoneId(null) + .thriftDefaultBufferSize(SessionConfig.DEFAULT_INITIAL_BUFFER_CAPACITY) + .thriftMaxFrameSize(SessionConfig.DEFAULT_MAX_FRAME_SIZE) + .enableRedirection(SessionConfig.DEFAULT_REDIRECTION_MODE)) .build(); } @@ -799,12 +884,13 @@ public ITableSession getTableSessionConnection(List nodeUrls) public ISessionPool getSessionPool(final int maxSize) { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new SessionPool.Builder() - .host(dataNode.getIp()) - .port(dataNode.getPort()) - .user(SessionConfig.DEFAULT_USER) - .password(SessionConfig.DEFAULT_PASSWORD) - .maxSize(maxSize) + return configureClientSSL( + new SessionPool.Builder() + .host(dataNode.getIp()) + .port(dataNode.getPort()) + .user(SessionConfig.DEFAULT_USER) + .password(SessionConfig.DEFAULT_PASSWORD) + .maxSize(maxSize)) .build(); } @@ -812,11 +898,12 @@ public ISessionPool getSessionPool(final int maxSize) { public ITableSessionPool getTableSessionPool(final int maxSize) { final DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new TableSessionPoolBuilder() - .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) - .user(SessionConfig.DEFAULT_USER) - .password(SessionConfig.DEFAULT_PASSWORD) - .maxSize(maxSize) + return configureClientSSL( + new TableSessionPoolBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + .user(SessionConfig.DEFAULT_USER) + .password(SessionConfig.DEFAULT_PASSWORD) + .maxSize(maxSize)) .build(); } @@ -824,12 +911,13 @@ public ITableSessionPool getTableSessionPool(final int maxSize) { public ITableSessionPool getTableSessionPool(final int maxSize, final String database) { DataNodeWrapper dataNode = this.dataNodeWrapperList.get(rand.nextInt(this.dataNodeWrapperList.size())); - return new TableSessionPoolBuilder() - .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) - .user(SessionConfig.DEFAULT_USER) - .password(SessionConfig.DEFAULT_PASSWORD) - .database(database) - .maxSize(maxSize) + return configureClientSSL( + new TableSessionPoolBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + .user(SessionConfig.DEFAULT_USER) + .password(SessionConfig.DEFAULT_PASSWORD) + .database(database) + .maxSize(maxSize)) .build(); } @@ -853,7 +941,7 @@ protected NodeConnection getWriteConnectionWithSpecifiedDataNode( Config.IOTDB_URL_PREFIX + endpoint + getParam(version, NODE_NETWORK_TIMEOUT_MS, ZERO_TIME_ZONE), - BaseEnv.constructProperties(username, password, sqlDialect)); + constructConnectionProperties(username, password, sqlDialect)); return new NodeConnection( endpoint, NodeConnection.NodeRole.DATA_NODE, @@ -910,7 +998,7 @@ protected List getReadConnections( Config.IOTDB_URL_PREFIX + endpoint + getParam(version, NODE_NETWORK_TIMEOUT_MS, ZERO_TIME_ZONE), - BaseEnv.constructProperties(username, password, sqlDialect)))); + constructConnectionProperties(username, password, sqlDialect)))); }); return readConnRequestDelegate.requestAll(); } @@ -957,7 +1045,7 @@ protected List getReadConnections( Config.IOTDB_URL_PREFIX + dataNode.getIpAndPortString() + getParam(version, NODE_NETWORK_TIMEOUT_MS, ZERO_TIME_ZONE), - BaseEnv.constructProperties(username, password, sqlDialect)))); + constructConnectionProperties(username, password, sqlDialect)))); return readConnRequestDelegate.requestAll(); } @@ -988,8 +1076,10 @@ protected void testJDBCConnection() { Config.IOTDB_URL_PREFIX + dataNodeEndpoint + getParam(null, NODE_NETWORK_TIMEOUT_MS, ZERO_TIME_ZONE), - System.getProperty("User", "root"), - System.getProperty("Password", "root"))) { + constructConnectionProperties( + System.getProperty("User", "root"), + System.getProperty("Password", "root"), + TREE_SQL_DIALECT))) { logger.info("Successfully connecting to DataNode: {}.", dataNodeEndpoint); return null; } catch (final Exception e) { 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 03948e2371f79..01ef8f468fa6e 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 CommonConfig setEnforceStrongPassword(boolean enforceStrongPassword) { return this; } + @Override + public CommonConfig setEnableThriftClientSSL(boolean enableThriftClientSSL) { + return this; + } + @Override public CommonConfig setSubscriptionPrefetchTsFileBatchMaxDelayInMs( int subscriptionPrefetchTsFileBatchMaxDelayInMs) { @@ -484,6 +489,11 @@ public CommonConfig setTrustStorePwd(String trustStorePwd) { return this; } + @Override + public CommonConfig setSslProtocol(String sslProtocol) { + return this; + } + @Override public CommonConfig setDatanodeMemoryProportion(String datanodeMemoryProportion) { return this; 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 ad9168faebfb2..767a047239f98 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 @@ -204,6 +204,8 @@ default CommonConfig setDefaultDatabaseLevel(int defaultDatabaseLevel) { CommonConfig setEnforceStrongPassword(boolean enforceStrongPassword); + CommonConfig setEnableThriftClientSSL(boolean enableThriftClientSSL); + CommonConfig setEnableInternalSSL(boolean enableInternalSSL); CommonConfig setKeyStorePath(String keyStorePath); @@ -214,6 +216,8 @@ default CommonConfig setDefaultDatabaseLevel(int defaultDatabaseLevel) { CommonConfig setTrustStorePwd(String trustStorePwd); + CommonConfig setSslProtocol(String sslProtocol); + CommonConfig setDatanodeMemoryProportion(String datanodeMemoryProportion); CommonConfig setEnableAuditLog(boolean enableAuditLog); diff --git a/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientSSLIT.java b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientSSLIT.java new file mode 100644 index 0000000000000..23906fdb4c87d --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/session/it/IoTDBClientSSLIT.java @@ -0,0 +1,233 @@ +/* + * 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.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.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 IoTDBClientSSLIT { + + 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) + .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 = newSSLSession()) { + deleteTreeDatabase(session, "root.client_ssl_tree"); + deleteTreeDatabase(session, "root.client_ssl_jdbc"); + } catch (Exception ignored) { + // ignored + } + try (ITableSession session = newSSLTableSession()) { + session.executeNonQueryStatement("DROP DATABASE IF EXISTS client_ssl_table"); + } catch (Exception ignored) { + // ignored + } + } + + @AfterClass + public static void tearDownClass() { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void nonSSLClientCanNotConnectToSSLPort() { + final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0); + final Session session = + new Session.Builder().host(dataNode.getIp()).port(dataNode.getPort()).build(); + + assertThrows(IoTDBConnectionException.class, session::open); + } + + @Test + public void treeSessionCanConnectWithSSL() throws Exception { + try (ISession session = newSSLSession()) { + session.executeNonQueryStatement("CREATE DATABASE root.client_ssl_tree"); + session.executeNonQueryStatement( + "CREATE TIMESERIES root.client_ssl_tree.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN"); + session.executeNonQueryStatement( + "INSERT INTO root.client_ssl_tree.d1(time, s1) VALUES (1, 11)"); + + try (SessionDataSet dataSet = + session.executeQueryStatement("SELECT s1 FROM root.client_ssl_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 tableSessionCanConnectWithSSL() throws Exception { + try (ITableSession session = newSSLTableSession()) { + session.executeNonQueryStatement("CREATE DATABASE IF NOT EXISTS client_ssl_table"); + session.executeNonQueryStatement("USE client_ssl_table"); + session.executeNonQueryStatement( + "CREATE TABLE IF NOT EXISTS ssl_table (tag1 STRING TAG, value INT32 FIELD)"); + session.executeNonQueryStatement( + "INSERT INTO ssl_table(time, tag1, value) VALUES (1, 'tag1', 22)"); + + try (SessionDataSet dataSet = + session.executeQueryStatement("SELECT time, value FROM ssl_table WHERE tag1 = 'tag1'")) { + assertTrue(dataSet.hasNext()); + final RowRecord record = dataSet.next(); + assertEquals(1L, record.getFields().get(0).getLongV()); + assertEquals(22, record.getFields().get(1).getIntV()); + assertFalse(dataSet.hasNext()); + } + } + } + + @Test + public void jdbcCanConnectWithSSL() throws Exception { + final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0); + + try (Connection connection = + DriverManager.getConnection( + Config.IOTDB_URL_PREFIX + dataNode.getIpAndPortString(), sslProperties()); + Statement statement = connection.createStatement()) { + statement.execute("CREATE DATABASE root.client_ssl_jdbc"); + statement.execute( + "CREATE TIMESERIES root.client_ssl_jdbc.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN"); + statement.execute("INSERT INTO root.client_ssl_jdbc.d1(time, s1) VALUES (1, 33)"); + + try (ResultSet resultSet = statement.executeQuery("SELECT s1 FROM root.client_ssl_jdbc.d1")) { + assertTrue(resultSet.next()); + assertEquals(1L, resultSet.getLong(1)); + assertEquals(33, resultSet.getInt(2)); + assertFalse(resultSet.next()); + } + } + } + + private static ISession newSSLSession() 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) + .build(); + session.open(); + return session; + } + + private static ITableSession newSSLTableSession() throws IoTDBConnectionException { + final DataNodeWrapper dataNode = EnvFactory.getEnv().getDataNodeWrapper(0); + return new TableSessionBuilder() + .nodeUrls(Collections.singletonList(dataNode.getIpAndPortString())) + .useSSL(true) + .trustStore(trustStorePath()) + .trustStorePwd(STORE_PASSWORD) + .build(); + } + + private static Properties sslProperties() { + 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); + 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/integration-test/src/test/resources/test-keystore b/integration-test/src/test/resources/test-keystore index 2346c547259fd..766057947174c 100644 Binary files a/integration-test/src/test/resources/test-keystore and b/integration-test/src/test/resources/test-keystore differ diff --git a/integration-test/src/test/resources/test-truststore b/integration-test/src/test/resources/test-truststore index 92c5b819f21f1..c6b93a476c560 100644 Binary files a/integration-test/src/test/resources/test-truststore and b/integration-test/src/test/resources/test-truststore differ 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 3d8535aac3c3e..017f0a1a6ca2b 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 @@ -77,12 +77,15 @@ public abstract class AbstractCli { static final String TRUST_STORE_PWD_ARGS = "tpw"; + static final String SSL_PROTOCOL_ARGS = "ssl_protocol"; + private static final String EXECUTE_NAME = "execute"; private static final String USE_SSL = "use_ssl"; private static final String TRUST_STORE = "trust_store"; private static final String TRUST_STORE_PWD = "trust_store_pwd"; + private static final String SSL_PROTOCOL = "ssl_protocol"; private static final String NULL = "null"; static final int CODE_OK = 0; @@ -132,6 +135,7 @@ public abstract class AbstractCli { static String trustStore; // TODO: Make non-static static String trustStorePwd; + static String sslProtocol; static String execute; static boolean hasExecuteSQL = false; @@ -156,6 +160,7 @@ static void init() { keywordSet.add("-" + USE_SSL_ARGS); keywordSet.add("-" + TRUST_STORE_ARGS); keywordSet.add("-" + TRUST_STORE_PWD_ARGS); + keywordSet.add("-" + SSL_PROTOCOL_ARGS); keywordSet.add("-" + EXECUTE_ARGS); keywordSet.add("-" + ISO8601_ARGS); keywordSet.add("-" + RPC_COMPRESS_ARGS); @@ -214,6 +219,15 @@ static Options createOptions() { .build(); options.addOption(useSSL); + Option sslProtocol = + Option.builder(SSL_PROTOCOL_ARGS) + .longOpt(SSL_PROTOCOL) + .argName(SSL_PROTOCOL) + .hasArg() + .desc("SSL protocol. (optional)") + .build(); + options.addOption(sslProtocol); + Option execute = Option.builder(EXECUTE_ARGS) .argName(EXECUTE_NAME) 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 d1bbd6165f694..a9e911be6edc3 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,9 @@ private static void constructProperties() { info.setProperty("use_ssl", useSsl); info.setProperty("trust_store", trustStore); info.setProperty("trust_store_pwd", trustStorePwd); + if (sslProtocol != null) { + info.setProperty(Config.SSL_PROTOCOL, sslProtocol); + } } info.setProperty("user", username); info.setProperty("password", password); @@ -159,6 +162,7 @@ private static boolean parseCommandLine( private static void serve(CliContext ctx) { try { 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'); 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 0d4c5f218b73b..a106326095756 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,10 @@ 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 SSL_PROTOCOL_ARGS = "ssl_protocol"; + public static final String SSL_PROTOCOL_NAME = "ssl_protocol"; + public static final String SSL_PROTOCOL_DESC = "SSL protocol. (optional)"; + public static final String FILE_TYPE_ARGS = "ft"; public static final String FILE_TYPE_NAME = "file_type"; public static final String FILE_TYPE_ARGS_NAME = "format"; 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 b60809eba636a..1f79b303f6e4e 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,16 @@ public static Options createCommonOptions(Options options) { .build(); options.addOption(opTrustStorePwd); + Option opSslProtocol = + Option.builder(SSL_PROTOCOL_ARGS) + .longOpt(SSL_PROTOCOL_NAME) + .optionalArg(true) + .argName(SSL_PROTOCOL_NAME) + .hasArg() + .desc(SSL_PROTOCOL_DESC) + .build(); + options.addOption(opSslProtocol); + return options; } 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 4ef95c78eda14..6ea79a5b58216 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 @@ -34,6 +34,9 @@ import org.apache.iotdb.rpc.IoTDBConnectionException; import org.apache.iotdb.rpc.StatementExecutionException; import org.apache.iotdb.session.Session; +import org.apache.iotdb.session.TableSessionBuilder; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.iotdb.session.pool.TableSessionPoolBuilder; import org.apache.iotdb.tool.common.Constants; import org.apache.iotdb.tool.common.ImportTsFileOperation; @@ -94,6 +97,7 @@ public abstract class AbstractDataTool { protected static Boolean useSsl; protected static String trustStore; protected static String trustStorePwd; + protected static String sslProtocol; protected static Boolean aligned; protected static String database; protected static String startTime; @@ -134,6 +138,38 @@ public abstract class AbstractDataTool { protected AbstractDataTool() {} + protected static Session.Builder configureSsl(Session.Builder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + + protected static SessionPool.Builder configureSsl(SessionPool.Builder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + + protected static TableSessionBuilder configureSsl(TableSessionBuilder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + + protected static TableSessionPoolBuilder configureSsl(TableSessionPoolBuilder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + protected static String checkRequiredArg( String arg, String name, CommandLine commandLine, String defaultValue) throws ArgsErrorException { @@ -170,6 +206,7 @@ protected static void parseBasicParams(CommandLine commandLine) String useSslStr = commandLine.getOptionValue(Constants.USE_SSL_ARGS); useSsl = Boolean.parseBoolean(useSslStr); if (useSsl) { + sslProtocol = commandLine.getOptionValue(Constants.SSL_PROTOCOL_ARGS); String givenTS = commandLine.getOptionValue(Constants.TRUST_STORE_ARGS); if (givenTS != null) { trustStore = givenTS; diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTable.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTable.java index dcf05baa14e66..458c447ba4f5b 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTable.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTable.java @@ -71,8 +71,7 @@ public void init() throws IoTDBConnectionException, StatementExecutionException .database(database) .thriftMaxFrameSize(rpcMaxFrameSize); if (useSsl) { - tableSessionBuilder = - tableSessionBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + tableSessionBuilder = configureSsl(tableSessionBuilder); } tableSession = tableSessionBuilder.build(); SessionDataSet sessionDataSet = tableSession.executeQueryStatement("show databases", timeout); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTree.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTree.java index 9d7d0ca521d09..fd8aeb0cbe352 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTree.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ExportDataTree.java @@ -80,8 +80,7 @@ public void init() throws IoTDBConnectionException, StatementExecutionException, .enableRedirection(SessionConfig.DEFAULT_REDIRECTION_MODE) .version(SessionConfig.DEFAULT_VERSION); if (useSsl) { - sessionBuilder = - sessionBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + sessionBuilder = configureSsl(sessionBuilder); } session = sessionBuilder.build(); session.open(false); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTable.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTable.java index 506da8b8e17ce..d103c63555b67 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTable.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTable.java @@ -88,8 +88,7 @@ public void init() throws InterruptedException { .enableAutoFetch(false) .database(database); if (useSsl) { - tableSessionPoolBuilder = - tableSessionPoolBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + tableSessionPoolBuilder = configureSsl(tableSessionPoolBuilder); } sessionPool = tableSessionPoolBuilder.build(); final File file = new File(targetPath); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTree.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTree.java index 95aa92631422a..c976e847740d2 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTree.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/data/ImportDataTree.java @@ -72,8 +72,7 @@ public void init() .enableRedirection(false) .enableAutoFetch(false); if (useSsl) { - sessionPoolBuilder = - sessionPoolBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + sessionPoolBuilder = configureSsl(sessionPoolBuilder); } sessionPool = sessionPoolBuilder.build(); sessionPool.setEnableQueryRedirection(false); 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 09442bdab878f..2bee5f5d33151 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 @@ -25,6 +25,8 @@ import org.apache.iotdb.cli.utils.JlineUtils; import org.apache.iotdb.exception.ArgsErrorException; import org.apache.iotdb.session.Session; +import org.apache.iotdb.session.pool.SessionPool; +import org.apache.iotdb.session.pool.TableSessionPoolBuilder; import org.apache.iotdb.tool.common.Constants; import org.apache.commons.cli.CommandLine; @@ -53,6 +55,7 @@ public abstract class AbstractSchemaTool { protected static Boolean useSsl; protected static String trustStore; protected static String trustStorePwd; + protected static String sslProtocol; protected static Session session; protected static String queryPath; protected static int threadNum = 8; @@ -73,6 +76,30 @@ public abstract class AbstractSchemaTool { protected AbstractSchemaTool() {} + protected static Session.Builder configureSsl(Session.Builder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + + protected static SessionPool.Builder configureSsl(SessionPool.Builder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + + protected static TableSessionPoolBuilder configureSsl(TableSessionPoolBuilder builder) { + builder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + if (sslProtocol != null) { + builder.sslProtocol(sslProtocol); + } + return builder; + } + protected static String checkRequiredArg( String arg, String name, CommandLine commandLine, String defaultValue) throws ArgsErrorException { @@ -107,6 +134,7 @@ protected static void parseBasicParams(CommandLine commandLine) String useSslStr = commandLine.getOptionValue(Constants.USE_SSL_ARGS); useSsl = Boolean.parseBoolean(useSslStr); if (useSsl) { + sslProtocol = commandLine.getOptionValue(Constants.SSL_PROTOCOL_ARGS); String givenTS = commandLine.getOptionValue(Constants.TRUST_STORE_ARGS); if (givenTS != null) { trustStore = givenTS; diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTable.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTable.java index 1ed3e2c6b862a..c9b15870d2175 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTable.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTable.java @@ -64,8 +64,7 @@ public void init() throws InterruptedException { .enableAutoFetch(false) .database(database); if (useSsl) { - tableSessionPoolBuilder = - tableSessionPoolBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + tableSessionPoolBuilder = configureSsl(tableSessionPoolBuilder); } sessionPool = tableSessionPoolBuilder.build(); checkDatabase(); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTree.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTree.java index 334d8a8d8dc42..da304f87c4986 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTree.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ExportSchemaTree.java @@ -51,8 +51,7 @@ public void init() .username(username) .password(password); if (useSsl) { - sessionBuilder = - sessionBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + sessionBuilder = configureSsl(sessionBuilder); } session = sessionBuilder.build(); session.open(false); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTable.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTable.java index 011042c34d29c..004ec8b4152bb 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTable.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTable.java @@ -60,8 +60,7 @@ public void init() throws InterruptedException { .enableAutoFetch(false) .database(database); if (useSsl) { - tableSessionPoolBuilder = - tableSessionPoolBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + tableSessionPoolBuilder = configureSsl(tableSessionPoolBuilder); } sessionPool = tableSessionPoolBuilder.build(); final File file = new File(targetPath); diff --git a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTree.java b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTree.java index 9526447fd9654..5d7dfc35d8a0d 100644 --- a/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTree.java +++ b/iotdb-client/cli/src/main/java/org/apache/iotdb/tool/schema/ImportSchemaTree.java @@ -73,8 +73,7 @@ public void init() .enableRedirection(false) .enableAutoFetch(false); if (useSsl) { - sessionPoolBuilder = - sessionPoolBuilder.useSSL(true).trustStore(trustStore).trustStorePwd(trustStorePwd); + sessionPoolBuilder = configureSsl(sessionPoolBuilder); } sessionPool = sessionPoolBuilder.build(); sessionPool.setEnableQueryRedirection(false); diff --git a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionConfig.java b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionConfig.java index 611014c615c3b..7885d50cf5da1 100644 --- a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionConfig.java +++ b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionConfig.java @@ -56,5 +56,7 @@ public class SessionConfig { public static final String SQL_DIALECT = "tree"; + public static final String DEFAULT_SSL_PROTOCOL = "TLS"; + private SessionConfig() {} } 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 0b1049330eae7..d79656cc8d295 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 @@ private Config() { public static final String TRUST_STORE_PWD = "trust_store_pwd"; + public static final String SSL_PROTOCOL = "ssl_protocol"; + + static final String DEFAULT_SSL_PROTOCOL = "TLS"; + public static final String SQL_DIALECT = "sql_dialect"; public static final String DATABASE = "db"; 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 7f37e2206fb87..0dc81115dd01b 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,13 @@ private void openTransport() throws TTransportException { if (params.isUseSSL()) { transport = - DeepCopyRpcTransportFactory.INSTANCE.getTransport( + DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig( params.getHost(), params.getPort(), getNetworkTimeout(), params.getTrustStore(), - params.getTrustStorePwd()); + params.getTrustStorePwd(), + params.getSslProtocol()); } else { transport = DeepCopyRpcTransportFactory.INSTANCE.getTransport( 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 1112caabd4e20..8bf51379c5c2a 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,7 @@ public class IoTDBConnectionParams { private boolean useSSL = false; private String trustStore; private String trustStorePwd; + private String sslProtocol = Config.DEFAULT_SSL_PROTOCOL; private String sqlDialect = TREE; @@ -184,6 +185,14 @@ public void setTrustStorePwd(String trustStorePwd) { this.trustStorePwd = trustStorePwd; } + public String getSslProtocol() { + return sslProtocol; + } + + public void setSslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + } + public String getSqlDialect() { return sqlDialect; } 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 00e46cc340d15..ea205ef726757 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 @@ -19,6 +19,8 @@ package org.apache.iotdb.jdbc; +import org.apache.iotdb.rpc.RpcSslUtils; + import java.nio.charset.Charset; import java.time.DateTimeException; import java.time.ZoneId; @@ -136,6 +138,9 @@ static IoTDBConnectionParams parseUrl(String url, Properties info) throws IoTDBU if (info.containsKey(Config.TRUST_STORE_PWD)) { params.setTrustStorePwd(info.getProperty(Config.TRUST_STORE_PWD)); } + if (info.containsKey(Config.SSL_PROTOCOL)) { + params.setSslProtocol(RpcSslUtils.normalizeProtocol(info.getProperty(Config.SSL_PROTOCOL))); + } if (info.containsKey(Config.SQL_DIALECT)) { params.setSqlDialect(info.getProperty(Config.SQL_DIALECT)); } @@ -175,6 +180,7 @@ private static boolean parseUrlParam(String subURL, Properties info) { case Config.USE_SSL: case Config.TRUST_STORE: case Config.TRUST_STORE_PWD: + case Config.SSL_PROTOCOL: case Config.VERSION: case Config.NETWORK_TIMEOUT: case Config.SQL_DIALECT: 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 4c401b88017af..d201a9b2d4569 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 @@ -159,4 +159,15 @@ public void testRpcCompress() throws IoTDBURLException { Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?rpc_compress=true", properties); assertTrue(Config.rpcThriftCompressionEnable); } + + @Test + public void testParseSslConfig() throws IoTDBURLException { + Properties properties = new Properties(); + 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()); + } } 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 22e104b6a58ac..03bf2070bca98 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 @@ -83,9 +83,15 @@ public TTransport getTransportWithNoTimeout(String ip, int port) throws TTranspo public TTransport getTransport( String ip, int port, int timeout, String trustStore, String trustStorePwd) throws TTransportException { + return getTransportWithSSLConfig(ip, port, timeout, trustStore, trustStorePwd, null); + } + + public TTransport getTransportWithSSLConfig( + String ip, int port, int timeout, String trustStore, String trustStorePwd, String sslProtocol) + throws TTransportException { TSSLTransportFactory.TSSLTransportParameters params = - new TSSLTransportFactory.TSSLTransportParameters(); - params.setTrustStore(trustStore, trustStorePwd); + RpcSslUtils.createTSSLTransportParameters(sslProtocol); + RpcSslUtils.setTrustStore(params, trustStore, trustStorePwd); TTransport transport = TSSLTransportFactory.getClientSocket(ip, port, timeout, params); return inner.getTransport(transport); } @@ -99,11 +105,24 @@ public TTransport getTransport( String keyStore, String keyStorePwd) throws TTransportException { + return getTransport(ip, port, timeout, trustStore, trustStorePwd, keyStore, keyStorePwd, null); + } + + public TTransport getTransport( + String ip, + int port, + int timeout, + String trustStore, + String trustStorePwd, + String keyStore, + String keyStorePwd, + String sslProtocol) + throws TTransportException { TSSLTransportFactory.TSSLTransportParameters params = - new TSSLTransportFactory.TSSLTransportParameters(); + RpcSslUtils.createTSSLTransportParameters(sslProtocol); if (Files.exists(Paths.get(trustStore)) && Files.exists(Paths.get(keyStore))) { - params.setTrustStore(trustStore, trustStorePwd); - params.setKeyStore(keyStore, keyStorePwd); + RpcSslUtils.setTrustStore(params, trustStore, trustStorePwd); + RpcSslUtils.setKeyStore(params, keyStore, keyStorePwd); } else { throw new TTransportException(new IOException(RpcMessages.COULD_NOT_LOAD_KEYSTORE)); } diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcSslUtils.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcSslUtils.java new file mode 100644 index 0000000000000..c6ceb5b4c5d64 --- /dev/null +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/RpcSslUtils.java @@ -0,0 +1,245 @@ +/* + * 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.rpc; + +import org.apache.thrift.transport.TSSLTransportFactory; +import org.apache.thrift.transport.TTransportException; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.AccessDeniedException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.Locale; +import java.util.stream.Stream; + +public final class RpcSslUtils { + + private static final String DEFAULT_PROTOCOL = "TLS"; + private static final String PKCS12_STORE_TYPE = "PKCS12"; + private static final String JKS_STORE_TYPE = "JKS"; + + private static volatile String protocol = DEFAULT_PROTOCOL; + + private RpcSslUtils() {} + + public static void configure(String sslProtocol) { + protocol = normalizeProtocol(sslProtocol); + } + + public static TSSLTransportFactory.TSSLTransportParameters createTSSLTransportParameters() { + return createTSSLTransportParameters(protocol); + } + + public static TSSLTransportFactory.TSSLTransportParameters createTSSLTransportParameters( + String sslProtocol) { + return new TSSLTransportFactory.TSSLTransportParameters(normalizeProtocol(sslProtocol), null); + } + + public static void setKeyStore( + TSSLTransportFactory.TSSLTransportParameters params, String keyStorePath, String keyStorePwd) + throws TTransportException { + try { + params.setKeyStore( + keyStorePath, + keyStorePwd, + KeyManagerFactory.getDefaultAlgorithm(), + detectStoreType(keyStorePath, keyStorePwd)); + } catch (GeneralSecurityException | IOException e) { + throw new TTransportException(e); + } + } + + public static void setTrustStore( + TSSLTransportFactory.TSSLTransportParameters params, + String trustStorePath, + String trustStorePwd) + throws TTransportException { + try { + params.setTrustStore( + trustStorePath, + trustStorePwd, + TrustManagerFactory.getDefaultAlgorithm(), + detectStoreType(trustStorePath, trustStorePwd)); + } catch (GeneralSecurityException | IOException e) { + throw new TTransportException(e); + } + } + + public static SSLContext createSSLContext( + String keyStorePath, + String keyStorePassword, + String trustStorePath, + String trustStorePassword) + throws GeneralSecurityException, IOException { + return createSSLContext( + keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, protocol); + } + + public static SSLContext createSSLContext( + String keyStorePath, + String keyStorePassword, + String trustStorePath, + String trustStorePassword, + String sslProtocol) + throws GeneralSecurityException, IOException { + SSLContext context = SSLContext.getInstance(normalizeProtocol(sslProtocol)); + KeyManager[] keyManagers = + hasText(keyStorePath) ? loadKeyManagers(keyStorePath, keyStorePassword) : null; + TrustManager[] trustManagers = + hasText(trustStorePath) ? loadTrustManagers(trustStorePath, trustStorePassword) : null; + context.init(keyManagers, trustManagers, null); + return context; + } + + public static KeyManager[] createKeyManagers(String keyStorePath, String keyStorePassword) + throws GeneralSecurityException, IOException { + return loadKeyManagers(keyStorePath, keyStorePassword); + } + + public static TrustManager[] createTrustManagers(String trustStorePath, String trustStorePassword) + throws GeneralSecurityException, IOException { + return loadTrustManagers(trustStorePath, trustStorePassword); + } + + public static String getProtocol() { + return protocol; + } + + public static void validateKeyStore(String keyStorePath, String keyStorePassword) + throws TTransportException { + validateStore(keyStorePath, keyStorePassword); + } + + public static void validateTrustStore(String trustStorePath, String trustStorePassword) + throws TTransportException { + validateStore(trustStorePath, trustStorePassword); + } + + private static KeyManager[] loadKeyManagers(String keyStorePath, String keyStorePassword) + throws GeneralSecurityException, IOException { + KeyStore keyStore = loadStore(keyStorePath, keyStorePassword); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, toPassword(keyStorePassword)); + return kmf.getKeyManagers(); + } + + private static TrustManager[] loadTrustManagers(String trustStorePath, String trustStorePassword) + throws GeneralSecurityException, IOException { + KeyStore trustStore = loadStore(trustStorePath, trustStorePassword); + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + return tmf.getTrustManagers(); + } + + private static String detectStoreType(String storePath, String storePassword) + throws GeneralSecurityException, IOException { + return loadStore(storePath, storePassword).getType(); + } + + private static KeyStore loadStore(String storePath, String storePassword) + throws GeneralSecurityException, IOException { + Exception lastException = null; + for (String storeType : storeTypeCandidates()) { + try { + return loadStore(storePath, storePassword, storeType); + } catch (AccessDeniedException | FileNotFoundException e) { + throw e; + } catch (GeneralSecurityException | IOException e) { + lastException = e; + } + } + if (lastException instanceof GeneralSecurityException) { + throw (GeneralSecurityException) lastException; + } + if (lastException instanceof IOException) { + throw (IOException) lastException; + } + throw new IOException("No supported keystore or truststore type is available"); + } + + private static KeyStore loadStore(String storePath, String storePassword, String storeType) + throws GeneralSecurityException, IOException { + KeyStore store = KeyStore.getInstance(storeType); + try (InputStream inputStream = Files.newInputStream(Path.of(storePath))) { + store.load(inputStream, toPassword(storePassword)); + } catch (AccessDeniedException e) { + throw new AccessDeniedException("Failed to load keystore or truststore file"); + } catch (FileNotFoundException | NoSuchFileException e) { + throw new FileNotFoundException("keystore or truststore file not found: " + storePath); + } + return store; + } + + private static void validateStore(String storePath, String storePassword) + throws TTransportException { + try { + KeyStore store = loadStore(storePath, storePassword); + Enumeration aliases = store.aliases(); + while (aliases.hasMoreElements()) { + X509Certificate cert = (X509Certificate) store.getCertificate(aliases.nextElement()); + if (cert != null) { + cert.checkValidity(); + } + } + } catch (Exception e) { + throw new TTransportException(e); + } + } + + private static char[] toPassword(String password) { + return password == null ? null : password.toCharArray(); + } + + public static String normalizeProtocol(String value) { + String trimmed = trimToEmpty(value); + return trimmed.isEmpty() ? DEFAULT_PROTOCOL : trimmed; + } + + private static String trimToEmpty(String value) { + return value == null ? "" : value.trim(); + } + + private static boolean hasText(String value) { + return value != null && !value.trim().isEmpty(); + } + + private static String[] storeTypeCandidates() { + return Stream.of(KeyStore.getDefaultType(), PKCS12_STORE_TYPE, JKS_STORE_TYPE) + .map(String::trim) + .map(s -> s.toUpperCase(Locale.ROOT)) + .filter(s -> !s.isEmpty()) + .distinct() + .toArray(String[]::new); + } +} diff --git a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java index 940c50453a26b..143de1d9ab76a 100644 --- a/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java +++ b/iotdb-client/service-rpc/src/test/java/org/apache/iotdb/rpc/RpcUtilsTest.java @@ -99,4 +99,11 @@ public void testVerifySuccessListThrowsOnFailure() { Assert.assertTrue(e.getMessage().contains("failed")); } } + + @Test + public void testSslProtocolNormalization() { + Assert.assertEquals("TLS", RpcSslUtils.normalizeProtocol(null)); + Assert.assertEquals("TLSv1.3", RpcSslUtils.normalizeProtocol(" TLSv1.3 ")); + Assert.assertEquals("ProviderProtocol", RpcSslUtils.normalizeProtocol(" ProviderProtocol ")); + } } 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 f410b167463af..873e6dd248d8a 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,7 @@ public abstract class AbstractSessionBuilder { public boolean useSSL = false; public String trustStore; public String trustStorePwd; + public String sslProtocol = SessionConfig.DEFAULT_SSL_PROTOCOL; // max retry count, if set to 0, means that we won't do any retry // we can use any available DataNodes(fetched in background thread if enableAutoFetch is true, 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 d57844b499705..a41db581e9cc7 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,7 @@ public class NodesSupplier implements INodeSupplier, Runnable { private final boolean useSSL; private final String trustStore; private final String trustStorePwd; + private final String sslProtocol; private final boolean enableRPCCompression; private final String userName; @@ -95,6 +96,7 @@ public static NodesSupplier createNodeSupplier( boolean useSSL, String trustStore, String trustStorePwd, + String sslProtocol, boolean enableRPCCompression, String version) { @@ -110,6 +112,7 @@ public static NodesSupplier createNodeSupplier( useSSL, trustStore, trustStorePwd, + sslProtocol, enableRPCCompression, version); @@ -132,6 +135,7 @@ private NodesSupplier( boolean useSSL, String trustStore, String trustStorePwd, + String sslProtocol, boolean enableRPCCompression, String version) { this.availableNodes.addAll(new HashSet<>(endPointList)); @@ -140,6 +144,7 @@ private NodesSupplier( this.useSSL = useSSL; this.trustStore = trustStore; this.trustStorePwd = trustStorePwd; + this.sslProtocol = sslProtocol; this.enableRPCCompression = enableRPCCompression; this.zoneId = zoneId == null ? ZoneId.systemDefault() : zoneId; this.thriftDefaultBufferSize = thriftDefaultBufferSize; @@ -188,6 +193,7 @@ private boolean createConnection(TEndPoint endPoint) { useSSL, trustStore, trustStorePwd, + sslProtocol, userName, password, enableRPCCompression, 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 f352a0612e6a7..648a63845b4bf 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,7 @@ public class Session implements ISession { protected boolean useSSL; protected String trustStore; protected String trustStorePwd; + protected String sslProtocol; /** * Timeout of query can be set by users. A negative number means using the default configuration @@ -474,6 +475,7 @@ public Session(AbstractSessionBuilder builder) { this.useSSL = builder.useSSL; this.trustStore = builder.trustStore; this.trustStorePwd = builder.trustStorePwd; + this.sslProtocol = builder.sslProtocol; this.enableAutoFetch = builder.enableAutoFetch; this.maxRetryCount = builder.maxRetryCount; this.retryIntervalInMs = builder.retryIntervalInMs; @@ -543,6 +545,7 @@ public synchronized void open(boolean enableRPCCompaction, int connectionTimeout useSSL, trustStore, trustStorePwd, + sslProtocol, enableRPCCompaction, version.toString()); } else { @@ -4435,6 +4438,11 @@ public Builder trustStorePwd(String trustStorePwd) { return this; } + public Builder sslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + return this; + } + public Session build() { if (nodeUrls != null && (!SessionConfig.DEFAULT_HOST.equals(host) || rpcPort != SessionConfig.DEFAULT_PORT)) { 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 c3e62cdd3e92e..468b59abb0c23 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 @@ -152,7 +152,8 @@ public SessionConnection( this.sqlDialect = sqlDialect; this.database = database; try { - init(endPoint, session.useSSL, session.trustStore, session.trustStorePwd); + init( + endPoint, session.useSSL, session.trustStore, session.trustStorePwd, session.sslProtocol); } catch (StatementExecutionException e) { throw new IoTDBConnectionException(e.getMessage()); } catch (IoTDBConnectionException e) { @@ -180,7 +181,12 @@ public SessionConnection( initClusterConn(); } - private void init(TEndPoint endPoint, boolean useSSL, String trustStore, String trustStorePwd) + private void init( + TEndPoint endPoint, + boolean useSSL, + String trustStore, + String trustStorePwd, + String sslProtocol) throws IoTDBConnectionException, StatementExecutionException { DeepCopyRpcTransportFactory.setDefaultBufferCapacity(session.thriftDefaultBufferSize); DeepCopyRpcTransportFactory.setThriftMaxFrameSize(session.thriftMaxFrameSize); @@ -190,12 +196,13 @@ private void init(TEndPoint endPoint, boolean useSSL, String trustStore, String } if (useSSL) { transport = - DeepCopyRpcTransportFactory.INSTANCE.getTransport( + DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig( endPoint.getIp(), endPoint.getPort(), session.connectionTimeoutInMs, trustStore, - trustStorePwd); + trustStorePwd, + sslProtocol); } else { transport = DeepCopyRpcTransportFactory.INSTANCE.getTransport( @@ -266,7 +273,12 @@ private void initClusterConn() throws IoTDBConnectionException { for (TEndPoint tEndPoint : endPointList) { try { session.defaultEndPoint = tEndPoint; - init(tEndPoint, session.useSSL, session.trustStore, session.trustStorePwd); + init( + tEndPoint, + session.useSSL, + session.trustStore, + session.trustStorePwd, + session.sslProtocol); } catch (IoTDBConnectionException e) { if (!reconnect()) { logger.error(SessionMessages.CLUSTER_NO_NODES); @@ -1083,7 +1095,12 @@ private boolean reconnect() { } tryHostNum++; try { - init(endPoint, session.useSSL, session.trustStore, session.trustStorePwd); + init( + endPoint, + session.useSSL, + session.trustStore, + session.trustStorePwd, + session.sslProtocol); connectedSuccess = true; } catch (IoTDBConnectionException e) { logger.warn(SessionMessages.NODE_DOWN_TRY_NEXT, endPoint); 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 be53797165a6a..e724c0fd53220 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,18 @@ public TableSessionBuilder trustStorePwd(String trustStorePwd) { return this; } + /** + * Sets the SSL protocol for secure connections. + * + * @param sslProtocol the SSL protocol. + * @return the current {@link TableSessionBuilder} instance. + * @defaultValue TLS + */ + public TableSessionBuilder sslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + return this; + } + /** * Enables or disables rpc compression for the connection. * 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 e3b9e8fc98987..3ab3abd581d9a 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,7 @@ public void init( boolean useSSL, String trustStore, String trustStorePwd, + String sslProtocol, String username, String password, boolean enableRPCCompression, @@ -89,12 +90,13 @@ public void init( try { if (useSSL) { transport = - DeepCopyRpcTransportFactory.INSTANCE.getTransport( + DeepCopyRpcTransportFactory.INSTANCE.getTransportWithSSLConfig( endPoint.getIp(), endPoint.getPort(), connectionTimeoutInMs, trustStore, - trustStorePwd); + trustStorePwd, + sslProtocol); } else { transport = DeepCopyRpcTransportFactory.INSTANCE.getTransport( 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 3eaabe7f1fa78..8136b2be68fd1 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 @@ -116,6 +116,9 @@ public class SessionPool implements ISessionPool { private String trustStore; private String trustStorePwd; + + private String sslProtocol = SessionConfig.DEFAULT_SSL_PROTOCOL; + private ZoneId zoneId; // this field only take effect in write request, nothing to do with any other type requests, // like query, load and so on. @@ -466,6 +469,7 @@ public SessionPool( this.useSSL = useSSL; this.trustStore = trustStore; this.trustStorePwd = trustStorePwd; + this.sslProtocol = SessionConfig.DEFAULT_SSL_PROTOCOL; initThreadPool(); initAvailableNodes(Collections.singletonList(new TEndPoint(host, port))); } @@ -536,6 +540,7 @@ public SessionPool(AbstractSessionPoolBuilder builder) { this.useSSL = builder.useSSL; this.trustStore = builder.trustStore; this.trustStorePwd = builder.trustStorePwd; + this.sslProtocol = builder.sslProtocol; this.maxRetryCount = builder.maxRetryCount; this.retryIntervalInMs = builder.retryIntervalInMs; this.sqlDialect = builder.sqlDialect; @@ -593,6 +598,7 @@ private Session constructNewSession() { .useSSL(useSSL) .trustStore(trustStore) .trustStorePwd(trustStorePwd) + .sslProtocol(sslProtocol) .maxRetryCount(maxRetryCount) .retryIntervalInMs(retryIntervalInMs) .sqlDialect(sqlDialect) @@ -618,6 +624,7 @@ private Session constructNewSession() { .useSSL(useSSL) .trustStore(trustStore) .trustStorePwd(trustStorePwd) + .sslProtocol(sslProtocol) .maxRetryCount(maxRetryCount) .retryIntervalInMs(retryIntervalInMs) .sqlDialect(sqlDialect) @@ -662,6 +669,7 @@ private void initAvailableNodes(List endPointList) { useSSL, trustStore, trustStorePwd, + sslProtocol, enableThriftCompression, version.toString()); } @@ -3637,6 +3645,11 @@ public Builder trustStorePwd(String trustStorePwd) { return this; } + public Builder sslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + return this; + } + public Builder host(String host) { this.host = host; 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 d5f52f3a8dc73..2c7aba1a45213 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,18 @@ public TableSessionPoolBuilder trustStorePwd(String trustStorePwd) { return this; } + /** + * Sets the SSL protocol for secure connections. + * + * @param sslProtocol the SSL protocol. + * @return the current {@link TableSessionPoolBuilder} instance. + * @defaultValue TLS + */ + public TableSessionPoolBuilder sslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + return this; + } + /** * Builds and returns a configured {@link ITableSessionPool} instance. * diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/client/SyncIoTConsensusServiceClient.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/client/SyncIoTConsensusServiceClient.java index 43b2f60d89f47..3f3eb2d358e60 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/client/SyncIoTConsensusServiceClient.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/client/SyncIoTConsensusServiceClient.java @@ -61,7 +61,8 @@ public SyncIoTConsensusServiceClient( commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/NoHostnameVerificationTrustManager.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/NoHostnameVerificationTrustManager.java deleted file mode 100644 index fb5c908548861..0000000000000 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/NoHostnameVerificationTrustManager.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.consensus.ratis.utils; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedTrustManager; -import javax.net.ssl.X509TrustManager; - -import java.net.Socket; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -public class NoHostnameVerificationTrustManager extends X509ExtendedTrustManager { - - private final X509TrustManager delegate; - - public NoHostnameVerificationTrustManager(X509TrustManager delegate) { - this.delegate = delegate; - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return delegate.getAcceptedIssuers(); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - delegate.checkClientTrusted(chain, authType); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - delegate.checkServerTrusted(chain, authType); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) - throws CertificateException { - if (delegate instanceof X509ExtendedTrustManager) { - ((X509ExtendedTrustManager) delegate).checkClientTrusted(chain, authType, socket); - } else { - delegate.checkClientTrusted(chain, authType); - } - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) - throws CertificateException { - // Skip hostname check by calling base method - delegate.checkServerTrusted(chain, authType); - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) - throws CertificateException { - if (delegate instanceof X509ExtendedTrustManager) { - ((X509ExtendedTrustManager) delegate).checkClientTrusted(chain, authType, engine); - } else { - delegate.checkClientTrusted(chain, authType); - } - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) - throws CertificateException { - // Skip hostname check by calling base method - delegate.checkServerTrusted(chain, authType); - } -} diff --git a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/Utils.java b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/Utils.java index 2c24ff12b6d36..442ac4840649e 100644 --- a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/Utils.java +++ b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/utils/Utils.java @@ -29,6 +29,7 @@ import org.apache.iotdb.consensus.config.RatisConfig; import org.apache.iotdb.consensus.i18n.ConsensusMessages; import org.apache.iotdb.rpc.AutoScalingBufferWriteTransport; +import org.apache.iotdb.rpc.RpcSslUtils; import org.apache.ratis.client.RaftClientConfigKeys; import org.apache.ratis.conf.Parameters; @@ -53,19 +54,12 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileNotFoundException; -import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.file.AccessDeniedException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.KeyStore; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -371,31 +365,10 @@ public static Parameters initRatisConfig(RaftProperties properties, RatisConfig String keyStorePassword = config.getGrpc().getSslKeyStorePassword(); String trustStorePath = config.getGrpc().getSslTrustStorePath(); String trustStorePassword = config.getGrpc().getSslTrustStorePassword(); - try (InputStream keyStoreStream = Files.newInputStream(Paths.get(keyStorePath)); - InputStream trustStoreStream = Files.newInputStream(Paths.get(trustStorePath))) { - // === 1) create KeyManager === - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(keyStoreStream, keyStorePassword.toCharArray()); - - KeyManagerFactory kmf = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(keyStore, keyStorePassword.toCharArray()); - KeyManager keyManager = kmf.getKeyManagers()[0]; - - // === 2) create TrustManager === - KeyStore trustStore = KeyStore.getInstance("JKS"); - trustStore.load(trustStoreStream, trustStorePassword.toCharArray()); - - TrustManagerFactory tmf = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(trustStore); - TrustManager originalTrustManager = tmf.getTrustManagers()[0]; - - // The self-signed certification may not set Subject Alternative Name (SAN) - // Thrift with ssl didn't check it, but Grpc did. - // Wrap to disable the verification + try { + KeyManager keyManager = RpcSslUtils.createKeyManagers(keyStorePath, keyStorePassword)[0]; TrustManager trustManager = - new NoHostnameVerificationTrustManager((X509TrustManager) originalTrustManager); + RpcSslUtils.createTrustManagers(trustStorePath, trustStorePassword)[0]; GrpcConfigKeys.TLS.setConf(parameters, new GrpcTlsConfig(keyManager, trustManager, true)); } catch (AccessDeniedException e) { LOGGER.error(ConsensusMessages.FAILED_TO_LOAD_KEYSTORE); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java index f6b7848d8ac9d..b7af9dcb201a4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceConfig.java @@ -45,6 +45,9 @@ public class IoTDBRestServiceConfig { /** ssl trust Store password. */ private String trustStorePwd = ""; + /** SSL protocol. */ + private String sslProtocol = ""; + /** ssl timeout. */ private int idleTimeoutInSeconds = 50000; @@ -78,6 +81,14 @@ public void setTrustStorePwd(String trustStorePwd) { this.trustStorePwd = trustStorePwd; } + public String getSslProtocol() { + return sslProtocol; + } + + public void setSslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + } + public int getIdleTimeoutInSeconds() { return idleTimeoutInSeconds; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java index 2e4ed0c28a6d0..807ef3054e712 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/rest/IoTDBRestServiceDescriptor.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.conf.TrimProperties; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.i18n.DataNodeMiscMessages; +import org.apache.iotdb.rpc.RpcSslUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -108,6 +109,9 @@ private void loadProps(TrimProperties trimProperties) { conf.setTrustStorePath( trimProperties.getProperty("trust_store_path", conf.getTrustStorePath())); conf.setTrustStorePwd(trimProperties.getProperty("trust_store_pwd", conf.getTrustStorePwd())); + conf.setSslProtocol( + RpcSslUtils.normalizeProtocol( + trimProperties.getProperty("ssl_protocol", conf.getSslProtocol()))); conf.setIdleTimeoutInSeconds( Integer.parseInt( trimProperties.getProperty( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java index 12153a19eedb5..ec55111a200b4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java @@ -283,7 +283,8 @@ public void connect(TEndPoint endpoint, int timeoutMs) throws TException { commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( // As there is a try-catch already, we do not need to use TSocket.wrap endpoint.getIp(), endpoint.getPort(), timeoutMs); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/an/AINodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/an/AINodeClient.java index 46af33b3172d2..6efb5a8c02167 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/an/AINodeClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/an/AINodeClient.java @@ -222,7 +222,8 @@ public void connect(TEndPoint endpoint, int timeoutMs) throws TException { COMMON_CONFIG.getTrustStorePath(), COMMON_CONFIG.getTrustStorePwd(), COMMON_CONFIG.getKeyStorePath(), - COMMON_CONFIG.getKeyStorePwd()) + COMMON_CONFIG.getKeyStorePwd(), + COMMON_CONFIG.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( // As there is a try-catch already, we do not need to use TSocket.wrap endpoint.getIp(), endpoint.getPort(), timeoutMs); 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 1e855a9704c88..55a716f52813a 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 @@ -474,6 +474,14 @@ trust_store_path= # Datatype: String trust_store_pwd= +# SSL protocol used by server-side SSL services. +# The protocol is passed to the current JSSE provider, such as TLS, TLSv1.2, +# TLSv1.3, or another provider-specific SSL protocol name. +# effectiveMode: restart +# Datatype: String +# Privilege: SECURITY +ssl_protocol=TLS + #################### ### Connection Configuration #################### diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncAINodeClient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncAINodeClient.java index 054b644609958..636007107048e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncAINodeClient.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncAINodeClient.java @@ -63,7 +63,8 @@ public SyncAINodeClient( COMMON_CONFIG.getTrustStorePath(), COMMON_CONFIG.getTrustStorePwd(), COMMON_CONFIG.getKeyStorePath(), - COMMON_CONFIG.getKeyStorePwd()) + COMMON_CONFIG.getKeyStorePwd(), + COMMON_CONFIG.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncConfigNodeIServiceClient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncConfigNodeIServiceClient.java index ced2c92b4a0ef..323e00bace104 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncConfigNodeIServiceClient.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncConfigNodeIServiceClient.java @@ -63,7 +63,8 @@ public SyncConfigNodeIServiceClient( commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeInternalServiceClient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeInternalServiceClient.java index 854b4a4aa18b6..3f51cfa5d3e9c 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeInternalServiceClient.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeInternalServiceClient.java @@ -64,7 +64,8 @@ public SyncDataNodeInternalServiceClient( commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeMPPDataExchangeServiceClient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeMPPDataExchangeServiceClient.java index 4dcde11bfac6b..b4393bfd4eedd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeMPPDataExchangeServiceClient.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncDataNodeMPPDataExchangeServiceClient.java @@ -63,7 +63,8 @@ public SyncDataNodeMPPDataExchangeServiceClient( commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncIoTConsensusV2ServiceClient.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncIoTConsensusV2ServiceClient.java index 3073434c92186..0b74424277593 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncIoTConsensusV2ServiceClient.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/sync/SyncIoTConsensusV2ServiceClient.java @@ -63,7 +63,8 @@ public SyncIoTConsensusV2ServiceClient( commonConfig.getTrustStorePath(), commonConfig.getTrustStorePwd(), commonConfig.getKeyStorePath(), - commonConfig.getKeyStorePwd()) + commonConfig.getKeyStorePwd(), + commonConfig.getSslProtocol()) : DeepCopyRpcTransportFactory.INSTANCE.getTransport( new TSocket( TConfigurationConst.defaultTConfiguration, 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 6ec3478109eba..25315ba4c2f89 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 @@ -499,6 +499,9 @@ public class CommonConfig { /** ssl trust Store password. */ private String trustStorePwd = ""; + /** SSL protocol. */ + private String sslProtocol = "TLS"; + private String userEncryptTokenHint = "not set yet"; private boolean enforceStrongPassword = false; @@ -3009,6 +3012,14 @@ public void setTrustStorePwd(String trustStorePwd) { this.trustStorePwd = trustStorePwd; } + public String getSslProtocol() { + return sslProtocol; + } + + public void setSslProtocol(String sslProtocol) { + this.sslProtocol = sslProtocol; + } + public boolean isEnforceStrongPassword() { return enforceStrongPassword; } 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 3242639c7e1a9..9c039cf25c207 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 @@ -24,6 +24,7 @@ import org.apache.iotdb.commons.utils.CommonDateTimeUtils; import org.apache.iotdb.confignode.rpc.thrift.TAuditConfig; import org.apache.iotdb.confignode.rpc.thrift.TGlobalConfig; +import org.apache.iotdb.rpc.RpcSslUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -658,5 +659,13 @@ public void initThriftSSL(TrimProperties properties) { config.setTrustStorePath( properties.getProperty("trust_store_path", config.getTrustStorePath())); config.setTrustStorePwd(properties.getProperty("trust_store_pwd", config.getTrustStorePwd())); + config.setSslProtocol( + RpcSslUtils.normalizeProtocol( + properties.getProperty("ssl_protocol", config.getSslProtocol()))); + configureRpcSsl(); + } + + public void configureRpcSsl() { + RpcSslUtils.configure(config.getSslProtocol()); } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/AbstractThriftServiceThread.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/AbstractThriftServiceThread.java index 84361727a32e0..cd2716108cebd 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/AbstractThriftServiceThread.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/AbstractThriftServiceThread.java @@ -24,6 +24,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.runtime.RPCServiceException; import org.apache.iotdb.commons.i18n.ServiceMessages; +import org.apache.iotdb.rpc.RpcSslUtils; import org.apache.thrift.TBaseAsyncProcessor; import org.apache.thrift.TProcessor; @@ -45,13 +46,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.net.InetSocketAddress; -import java.nio.file.AccessDeniedException; -import java.security.KeyStore; -import java.security.cert.X509Certificate; -import java.util.Enumeration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -184,13 +179,13 @@ protected AbstractThriftServiceThread( this.serviceName = serviceName; try { - validateCertificate(keyStorePath, keyStorePwd); + RpcSslUtils.validateKeyStore(keyStorePath, keyStorePwd); TSSLTransportFactory.TSSLTransportParameters params = - new TSSLTransportFactory.TSSLTransportParameters(); - params.setKeyStore(keyStorePath, keyStorePwd); + RpcSslUtils.createTSSLTransportParameters(); + RpcSslUtils.setKeyStore(params, keyStorePath, keyStorePwd); if (trustStorePath != null && !trustStorePath.isEmpty()) { - validateCertificate(trustStorePath, trustStorePwd); - params.setTrustStore(trustStorePath, trustStorePwd); + RpcSslUtils.validateTrustStore(trustStorePath, trustStorePwd); + RpcSslUtils.setTrustStore(params, trustStorePath, trustStorePwd); params.requireClientAuth(true); } InetSocketAddress socketAddress = new InetSocketAddress(bindAddress, port); @@ -206,41 +201,6 @@ protected AbstractThriftServiceThread( } } - private static void validateCertificate(String keyStorePath, String keystorePassword) - throws TTransportException { - try { - KeyStore keystore = KeyStore.getInstance("JKS"); - try (FileInputStream fis = new FileInputStream(keyStorePath)) { - keystore.load(fis, keystorePassword.toCharArray()); - } - - Enumeration aliases = keystore.aliases(); - while (aliases.hasMoreElements()) { - String currentAlias = aliases.nextElement(); - checkCertificate(keystore, currentAlias); - } - } catch (AccessDeniedException e) { - throw new TTransportException(ServiceMessages.FAILED_TO_LOAD_KEYSTORE_OR_TRUSTSTORE); - } catch (FileNotFoundException e) { - throw new TTransportException(ServiceMessages.KEYSTORE_OR_TRUSTSTORE_NOT_FOUND); - } catch (Exception e) { - throw new TTransportException(e); - } - } - - private static void checkCertificate(KeyStore keystore, String alias) throws Exception { - if (!keystore.containsAlias(alias)) { - return; - } - - X509Certificate cert = (X509Certificate) keystore.getCertificate(alias); - if (cert == null) { - return; - } - - cert.checkValidity(); - } - @SuppressWarnings("squid:S107") protected AbstractThriftServiceThread( TProcessor processor, diff --git a/pom.xml b/pom.xml index c577243cb0ac2..3f3b235f19a1e 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,7 @@ 1.5.6 2.0.9 1.0.11 - 3.2.2 + 3.3.0-adac7ef-SNAPSHOT 1.0.4 1.2.9 3.7.9 @@ -331,6 +331,16 @@ org.apache.ratis ratis-common ${ratis.version} + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-context + + org.apache.ratis