This is an automated email from the ASF dual-hosted git repository.
HTHou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 83f6303c253 Add TLS channel failure reporting hooks (#18299)
83f6303c253 is described below
commit 83f6303c253e186a1b44496f185e23cb2cd36edd
Author: Haonan <[email protected]>
AuthorDate: Mon Aug 24 20:34:19 2026 +0800
Add TLS channel failure reporting hooks (#18299)
---
external-service-impl/rest/pom.xml | 4 +
.../java/org/apache/iotdb/rest/RestService.java | 4 +
.../rest/TrustedChannelAuditHandshakeListener.java | 75 +++++++
.../TrustedChannelAuditHandshakeListenerTest.java | 96 +++++++++
.../service/thrift/ConfigNodeRPCService.java | 8 +-
.../iotdb/consensus/config/ConsensusConfig.java | 23 ++-
.../apache/iotdb/consensus/iot/IoTConsensus.java | 4 +-
.../iot/service/IoTConsensusRPCService.java | 16 +-
.../iotdb/consensus/pipe/IoTConsensusV2.java | 4 +-
.../pipe/service/IoTConsensusV2RPCService.java | 16 +-
.../db/consensus/DataRegionConsensusImpl.java | 3 +
.../execution/exchange/MPPDataExchangeService.java | 7 +-
.../db/service/DataNodeInternalRPCService.java | 8 +-
.../iotdb/db/service/ExternalRPCService.java | 14 +-
.../apache/iotdb/commons/i18n/CommonMessages.java | 2 +
.../apache/iotdb/commons/i18n/CommonMessages.java | 2 +
.../iotdb/commons/audit/AbstractAuditLogger.java | 75 +++++++
.../apache/iotdb/commons/audit/AuditEventType.java | 2 +-
...Type.java => TrustedChannelFailureHandler.java} | 42 +---
.../TrustedChannelAuditServerEventHandler.java | 152 ++++++++++++++
.../commons/audit/AbstractAuditLoggerTest.java | 152 ++++++++++++++
.../TrustedChannelAuditServerEventHandlerTest.java | 220 +++++++++++++++++++++
pom.xml | 5 +
23 files changed, 889 insertions(+), 45 deletions(-)
diff --git a/external-service-impl/rest/pom.xml
b/external-service-impl/rest/pom.xml
index f6cedd00796..fc064d28254 100644
--- a/external-service-impl/rest/pom.xml
+++ b/external-service-impl/rest/pom.xml
@@ -98,6 +98,10 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.iotdb</groupId>
<artifactId>node-commons</artifactId>
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 506facdb4ce..6ecba9aece8 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
@@ -16,6 +16,7 @@
*/
package org.apache.iotdb.rest;
+import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.conf.rest.IoTDBRestServiceConfig;
import org.apache.iotdb.db.conf.rest.IoTDBRestServiceDescriptor;
import org.apache.iotdb.externalservice.api.IExternalService;
@@ -79,6 +80,9 @@ public class RestService implements IExternalService {
new HttpConnectionFactory(httpsConfig));
httpsConnector.setPort(port);
httpsConnector.setIdleTimeout(idleTime);
+ httpsConnector.addBean(
+ new TrustedChannelAuditHandshakeListener(
+
DNAuditLogger.getInstance()::recordTrustedChannelFailureAuditLogIfNecessary));
server.addConnector(httpsConnector);
server.setHandler(constructServletContextHandler());
diff --git
a/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListener.java
b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListener.java
new file mode 100644
index 00000000000..c4245efab5e
--- /dev/null
+++
b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListener.java
@@ -0,0 +1,75 @@
+/*
+ * 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.rest;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Objects;
+
+final class TrustedChannelAuditHandshakeListener implements
SslHandshakeListener {
+
+ private final TrustedChannelFailureHandler failureHandler;
+
+ TrustedChannelAuditHandshakeListener(TrustedChannelFailureHandler
failureHandler) {
+ this.failureHandler = Objects.requireNonNull(failureHandler);
+ }
+
+ @Override
+ public void handshakeFailed(Event event, Throwable failure) {
+ recordHandshakeFailure(event.getEndPoint(), failure);
+ }
+
+ void recordHandshakeFailure(EndPoint endPoint, Throwable failure) {
+ if (endPoint == null || failure == null) {
+ return;
+ }
+
+ TEndPoint initiator = toEndPoint(endPoint.getRemoteSocketAddress());
+ TEndPoint target = toEndPoint(endPoint.getLocalSocketAddress());
+ if (initiator == null || target == null) {
+ return;
+ }
+
+ try {
+ failureHandler.onFailure(failure, initiator, target);
+ } catch (RuntimeException auditFailure) {
+ if (auditFailure != failure) {
+ failure.addSuppressed(auditFailure);
+ }
+ }
+ }
+
+ private static TEndPoint toEndPoint(SocketAddress socketAddress) {
+ if (!(socketAddress instanceof InetSocketAddress inetSocketAddress)) {
+ return null;
+ }
+ String host =
+ inetSocketAddress.getAddress() == null
+ ? inetSocketAddress.getHostString()
+ : inetSocketAddress.getAddress().getHostAddress();
+ return new TEndPoint(host, inetSocketAddress.getPort());
+ }
+}
diff --git
a/external-service-impl/rest/src/test/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListenerTest.java
b/external-service-impl/rest/src/test/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListenerTest.java
new file mode 100644
index 00000000000..fbfdfca1c9d
--- /dev/null
+++
b/external-service-impl/rest/src/test/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListenerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.rest;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.junit.Test;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import java.lang.reflect.Proxy;
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class TrustedChannelAuditHandshakeListenerTest {
+
+ @Test
+ public void testRecordSocketEndpointsOnHandshakeFailure() {
+ AtomicReference<Throwable> actualFailure = new AtomicReference<>();
+ AtomicReference<TEndPoint> actualInitiator = new AtomicReference<>();
+ AtomicReference<TEndPoint> actualTarget = new AtomicReference<>();
+ TrustedChannelAuditHandshakeListener listener =
+ new TrustedChannelAuditHandshakeListener(
+ (failure, initiator, target) -> {
+ actualFailure.set(failure);
+ actualInitiator.set(initiator);
+ actualTarget.set(target);
+ });
+ SSLHandshakeException failure = new SSLHandshakeException("test");
+ EndPoint endPoint =
+ newEndPoint(
+ new InetSocketAddress("192.0.2.10", 45123), new
InetSocketAddress("192.0.2.20", 18080));
+
+ listener.recordHandshakeFailure(endPoint, failure);
+
+ assertSame(failure, actualFailure.get());
+ assertEquals(new TEndPoint("192.0.2.10", 45123), actualInitiator.get());
+ assertEquals(new TEndPoint("192.0.2.20", 18080), actualTarget.get());
+ }
+
+ @Test
+ public void testReportingFailureDoesNotSelfSuppressHandshakeFailure() {
+ RuntimeException failure = new RuntimeException("test");
+ TrustedChannelAuditHandshakeListener listener =
+ new TrustedChannelAuditHandshakeListener(
+ (ignoredFailure, initiator, target) -> {
+ throw failure;
+ });
+ EndPoint endPoint =
+ newEndPoint(
+ new InetSocketAddress("192.0.2.10", 45123), new
InetSocketAddress("192.0.2.20", 18080));
+
+ listener.recordHandshakeFailure(endPoint, failure);
+
+ assertEquals(0, failure.getSuppressed().length);
+ }
+
+ private static EndPoint newEndPoint(
+ InetSocketAddress remoteAddress, InetSocketAddress localAddress) {
+ return (EndPoint)
+ Proxy.newProxyInstance(
+ EndPoint.class.getClassLoader(),
+ new Class<?>[] {EndPoint.class},
+ (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "getRemoteAddress":
+ case "getRemoteSocketAddress":
+ return remoteAddress;
+ case "getLocalAddress":
+ case "getLocalSocketAddress":
+ return localAddress;
+ default:
+ return null;
+ }
+ });
+ }
+}
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
index 15be2e90d43..f325da85ab0 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCService.java
@@ -18,6 +18,7 @@
*/
package org.apache.iotdb.confignode.service.thrift;
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
import org.apache.iotdb.commons.concurrent.ThreadName;
import org.apache.iotdb.commons.conf.CommonConfig;
import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -25,6 +26,7 @@ import
org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.commons.service.metric.MetricService;
import org.apache.iotdb.confignode.conf.ConfigNodeConfig;
import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
@@ -70,7 +72,11 @@ public class ConfigNodeRPCService extends ThriftService
implements ConfigNodeRPC
getBindPort(),
configConf.getCnRpcMaxConcurrentClientNum(),
configConf.getThriftServerAwaitTimeForStopService(),
- new ConfigNodeRPCServiceHandler(),
+ new TrustedChannelAuditServerEventHandler(
+ new ConfigNodeRPCServiceHandler(),
+ new TEndPoint(getBindIP(), getBindPort()),
+
configNodeRPCServiceProcessor.configManager.getAuditLogger()
+ ::recordTrustedChannelFailureAuditLogIfNecessary),
commonConfig.isRpcThriftCompressionEnabled(),
commonConfig.getKeyStorePath(),
commonConfig.getKeyStorePwd(),
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/config/ConsensusConfig.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/config/ConsensusConfig.java
index d54299992fe..114a8aed4fc 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/config/ConsensusConfig.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/config/ConsensusConfig.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.consensus.config;
import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
import org.apache.iotdb.commons.disk.strategy.DirectoryStrategyType;
import java.util.List;
@@ -37,6 +38,7 @@ public class ConsensusConfig {
private final IoTConsensusConfig iotConsensusConfig;
private final IoTConsensusV2Config iotConsensusV2Config;
private final DirectoryStrategyType directoryStrategyType;
+ private final TrustedChannelFailureHandler trustedChannelFailureHandler;
private ConsensusConfig(
TEndPoint thisNode,
@@ -47,7 +49,8 @@ public class ConsensusConfig {
RatisConfig ratisConfig,
IoTConsensusConfig iotConsensusConfig,
IoTConsensusV2Config iotConsensusV2Config,
- DirectoryStrategyType directoryStrategyType) {
+ DirectoryStrategyType directoryStrategyType,
+ TrustedChannelFailureHandler trustedChannelFailureHandler) {
this.thisNodeEndPoint = thisNode;
this.thisNodeId = thisNodeId;
this.storageDir = storageDir;
@@ -57,6 +60,7 @@ public class ConsensusConfig {
this.iotConsensusConfig = iotConsensusConfig;
this.iotConsensusV2Config = iotConsensusV2Config;
this.directoryStrategyType = directoryStrategyType;
+ this.trustedChannelFailureHandler = trustedChannelFailureHandler;
}
public TEndPoint getThisNodeEndPoint() {
@@ -95,6 +99,10 @@ public class ConsensusConfig {
return directoryStrategyType;
}
+ public TrustedChannelFailureHandler getTrustedChannelFailureHandler() {
+ return trustedChannelFailureHandler;
+ }
+
public static ConsensusConfig.Builder newBuilder() {
return new ConsensusConfig.Builder();
}
@@ -111,6 +119,8 @@ public class ConsensusConfig {
private IoTConsensusV2Config iotConsensusV2Config;
private DirectoryStrategyType directoryStrategyType =
DirectoryStrategyType.MIN_FOLDER_OCCUPIED_SPACE_FIRST_STRATEGY;
+ private TrustedChannelFailureHandler trustedChannelFailureHandler =
+ TrustedChannelFailureHandler.NO_OP;
public ConsensusConfig build() {
return new ConsensusConfig(
@@ -124,7 +134,8 @@ public class ConsensusConfig {
.orElseGet(() -> IoTConsensusConfig.newBuilder().build()),
Optional.ofNullable(iotConsensusV2Config)
.orElseGet(() -> IoTConsensusV2Config.newBuilder().build()),
- directoryStrategyType);
+ directoryStrategyType,
+ trustedChannelFailureHandler);
}
public Builder setThisNode(TEndPoint thisNode) {
@@ -171,5 +182,13 @@ public class ConsensusConfig {
this.directoryStrategyType = directoryStrategyType;
return this;
}
+
+ public Builder setTrustedChannelFailureHandler(
+ TrustedChannelFailureHandler trustedChannelFailureHandler) {
+ this.trustedChannelFailureHandler =
+ Optional.ofNullable(trustedChannelFailureHandler)
+ .orElse(TrustedChannelFailureHandler.NO_OP);
+ return this;
+ }
}
}
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java
index aa7ecbbf4ec..477d8a5cb11 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensus.java
@@ -132,7 +132,9 @@ public class IoTConsensus implements IConsensus {
this.recvFolderStrategyType = config.getDirectoryStrategyType();
this.config = config.getIotConsensusConfig();
this.registry = registry;
- this.service = new IoTConsensusRPCService(thisNode,
config.getIotConsensusConfig());
+ this.service =
+ new IoTConsensusRPCService(
+ thisNode, config.getIotConsensusConfig(),
config.getTrustedChannelFailureHandler());
this.clientManager =
new IClientManager.Factory<TEndPoint, AsyncIoTConsensusServiceClient>()
.createClientManager(
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/service/IoTConsensusRPCService.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/service/IoTConsensusRPCService.java
index cfa175b2de5..d7dec0d9782 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/service/IoTConsensusRPCService.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/service/IoTConsensusRPCService.java
@@ -20,11 +20,13 @@
package org.apache.iotdb.consensus.iot.service;
import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
import org.apache.iotdb.commons.concurrent.ThreadName;
import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.consensus.config.IoTConsensusConfig;
import org.apache.iotdb.consensus.iot.thrift.IoTConsensusIService;
import org.apache.iotdb.rpc.ZeroCopyRpcTransportFactory;
@@ -40,11 +42,20 @@ public class IoTConsensusRPCService extends ThriftService
implements IoTConsensu
private final TEndPoint thisNode;
private final IoTConsensusConfig config;
+ private final TrustedChannelFailureHandler trustedChannelFailureHandler;
private IoTConsensusRPCServiceProcessor iotConsensusRPCServiceProcessor;
public IoTConsensusRPCService(TEndPoint thisNode, IoTConsensusConfig config)
{
+ this(thisNode, config, TrustedChannelFailureHandler.NO_OP);
+ }
+
+ public IoTConsensusRPCService(
+ TEndPoint thisNode,
+ IoTConsensusConfig config,
+ TrustedChannelFailureHandler trustedChannelFailureHandler) {
this.thisNode = thisNode;
this.config = config;
+ this.trustedChannelFailureHandler = trustedChannelFailureHandler;
}
@Override
@@ -83,7 +94,10 @@ public class IoTConsensusRPCService extends ThriftService
implements IoTConsensu
getBindPort(),
config.getRpc().getRpcMaxConcurrentClientNum(),
config.getRpc().getThriftServerAwaitTimeForStopService(),
- new
IoTConsensusRPCServiceHandler(iotConsensusRPCServiceProcessor),
+ new TrustedChannelAuditServerEventHandler(
+ new
IoTConsensusRPCServiceHandler(iotConsensusRPCServiceProcessor),
+ thisNode,
+ trustedChannelFailureHandler),
config.getRpc().isRpcThriftCompressionEnabled(),
config.getRpc().getSslKeyStorePath(),
config.getRpc().getSslKeyStorePassword(),
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java
index 4a207331564..7b9c2cc0e00 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/IoTConsensusV2.java
@@ -108,7 +108,9 @@ public class IoTConsensusV2 implements IConsensus {
this.storageDir = new File(config.getStorageDir());
this.config = config.getIoTConsensusV2Config();
this.registry = registry;
- this.rpcService = new IoTConsensusV2RPCService(thisNode,
config.getIoTConsensusV2Config());
+ this.rpcService =
+ new IoTConsensusV2RPCService(
+ thisNode, config.getIoTConsensusV2Config(),
config.getTrustedChannelFailureHandler());
this.asyncClientManager =
IoTV2GlobalComponentContainer.getInstance().getGlobalAsyncClientManager();
this.syncClientManager =
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/service/IoTConsensusV2RPCService.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/service/IoTConsensusV2RPCService.java
index c218c8051e8..9739ade8e1d 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/service/IoTConsensusV2RPCService.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/pipe/service/IoTConsensusV2RPCService.java
@@ -20,11 +20,13 @@
package org.apache.iotdb.consensus.pipe.service;
import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
import org.apache.iotdb.commons.concurrent.ThreadName;
import org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.consensus.config.IoTConsensusV2Config;
import org.apache.iotdb.consensus.iotconsensusv2.thrift.IoTConsensusV2IService;
import org.apache.iotdb.rpc.ZeroCopyRpcTransportFactory;
@@ -34,11 +36,20 @@ public class IoTConsensusV2RPCService extends ThriftService
private final TEndPoint thisNode;
private final IoTConsensusV2Config config;
+ private final TrustedChannelFailureHandler trustedChannelFailureHandler;
private IoTConsensusV2RPCServiceProcessor iotConsensusV2RPCServiceProcessor;
public IoTConsensusV2RPCService(TEndPoint thisNode, IoTConsensusV2Config
config) {
+ this(thisNode, config, TrustedChannelFailureHandler.NO_OP);
+ }
+
+ public IoTConsensusV2RPCService(
+ TEndPoint thisNode,
+ IoTConsensusV2Config config,
+ TrustedChannelFailureHandler trustedChannelFailureHandler) {
this.thisNode = thisNode;
this.config = config;
+ this.trustedChannelFailureHandler = trustedChannelFailureHandler;
}
@Override
@@ -71,7 +82,10 @@ public class IoTConsensusV2RPCService extends ThriftService
getBindPort(),
config.getRpc().getRpcMaxConcurrentClientNum(),
config.getRpc().getThriftServerAwaitTimeForStopService(),
- new
IoTConsensusV2RPCServiceHandler(iotConsensusV2RPCServiceProcessor),
+ new TrustedChannelAuditServerEventHandler(
+ new
IoTConsensusV2RPCServiceHandler(iotConsensusV2RPCServiceProcessor),
+ thisNode,
+ trustedChannelFailureHandler),
config.getRpc().isRpcThriftCompressionEnabled(),
config.getRpc().getSslKeyStorePath(),
config.getRpc().getSslKeyStorePassword(),
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
index a4c8e00f1ad..fbda403b320 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
@@ -39,6 +39,7 @@ import org.apache.iotdb.consensus.config.IoTConsensusV2Config;
import org.apache.iotdb.consensus.config.IoTConsensusV2Config.ReplicateMode;
import org.apache.iotdb.consensus.config.RatisConfig;
import org.apache.iotdb.consensus.config.RatisConfig.Snapshot;
+import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.conf.DataNodeMemoryConfig;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
@@ -140,6 +141,8 @@ public class DataRegionConsensusImpl {
return ConsensusConfig.newBuilder()
.setThisNodeId(CONF.getDataNodeId())
.setThisNode(new TEndPoint(CONF.getInternalAddress(),
CONF.getDataRegionConsensusPort()))
+ .setTrustedChannelFailureHandler(
+
DNAuditLogger.getInstance()::recordTrustedChannelFailureAuditLogIfNecessary)
.setStorageDir(CONF.getDataRegionConsensusDir())
.setRecvSnapshotDirs(Arrays.asList(CONF.getLocalDataDirs()))
// IoTConsensus always balances received snapshot files by least
occupied space,
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/MPPDataExchangeService.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/MPPDataExchangeService.java
index a4cef2e4b71..6afcd2a3a91 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/MPPDataExchangeService.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/MPPDataExchangeService.java
@@ -32,7 +32,9 @@ import
org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.commons.service.metric.MetricService;
+import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.i18n.DataNodeQueryMessages;
@@ -103,7 +105,10 @@ public class MPPDataExchangeService extends ThriftService
implements MPPDataExch
getBindPort(),
config.getRpcMaxConcurrentClientNum(),
config.getThriftServerAwaitTimeForStopService(),
- new MPPDataExchangeServiceThriftHandler(),
+ new TrustedChannelAuditServerEventHandler(
+ new MPPDataExchangeServiceThriftHandler(),
+ new TEndPoint(getBindIP(), getBindPort()),
+
DNAuditLogger.getInstance()::recordTrustedChannelFailureAuditLogIfNecessary),
config.isRpcThriftCompressionEnable(),
commonConfig.getKeyStorePath(),
commonConfig.getKeyStorePwd(),
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
index b58d647fcd7..f9b9c6e0101 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNodeInternalRPCService.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.service;
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
import org.apache.iotdb.commons.concurrent.ThreadName;
import org.apache.iotdb.commons.conf.CommonConfig;
import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -26,7 +27,9 @@ import
org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.commons.service.metric.MetricService;
+import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import
org.apache.iotdb.db.protocol.thrift.handler.InternalServiceThriftHandler;
@@ -74,7 +77,10 @@ public class DataNodeInternalRPCService extends ThriftService
getBindPort(),
config.getRpcMaxConcurrentClientNum(),
config.getThriftServerAwaitTimeForStopService(),
- new InternalServiceThriftHandler(),
+ new TrustedChannelAuditServerEventHandler(
+ new InternalServiceThriftHandler(),
+ new TEndPoint(getBindIP(), getBindPort()),
+
DNAuditLogger.getInstance()::recordTrustedChannelFailureAuditLogIfNecessary),
config.isRpcThriftCompressionEnable(),
commonConfig.getKeyStorePath(),
commonConfig.getKeyStorePwd(),
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
index f9b34dd9e58..782788547bf 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/ExternalRPCService.java
@@ -18,6 +18,7 @@
*/
package org.apache.iotdb.db.service;
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
import org.apache.iotdb.commons.concurrent.ThreadName;
import org.apache.iotdb.commons.conf.CommonConfig;
import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -25,7 +26,9 @@ import
org.apache.iotdb.commons.exception.runtime.RPCServiceException;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.ThriftService;
import org.apache.iotdb.commons.service.ThriftServiceThread;
+import org.apache.iotdb.commons.service.TrustedChannelAuditServerEventHandler;
import org.apache.iotdb.commons.service.metric.MetricService;
+import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.i18n.DataNodeMiscMessages;
@@ -94,7 +97,7 @@ public class ExternalRPCService extends ThriftService
implements ExternalRPCServ
getBindPort(),
config.getRpcMaxConcurrentClientNum(),
config.getThriftServerAwaitTimeForStopService(),
- new RPCServiceThriftHandler(impl),
+ newTrustedChannelAuditHandler(),
config.isRpcThriftCompressionEnable(),
commonConfig.getKeyStorePath(),
commonConfig.getKeyStorePwd(),
@@ -111,7 +114,7 @@ public class ExternalRPCService extends ThriftService
implements ExternalRPCServ
getBindPort(),
config.getRpcMaxConcurrentClientNum(),
config.getThriftServerAwaitTimeForStopService(),
- new RPCServiceThriftHandler(impl),
+ newTrustedChannelAuditHandler(),
config.isRpcThriftCompressionEnable(),
commonConfig.getKeyStorePath(),
commonConfig.getKeyStorePwd(),
@@ -150,6 +153,13 @@ public class ExternalRPCService extends ThriftService
implements ExternalRPCServ
return value != null && !value.trim().isEmpty();
}
+ private TrustedChannelAuditServerEventHandler
newTrustedChannelAuditHandler() {
+ return new TrustedChannelAuditServerEventHandler(
+ new RPCServiceThriftHandler(impl),
+ new TEndPoint(getBindIP(), getBindPort()),
+
DNAuditLogger.getInstance()::recordTrustedChannelFailureAuditLogIfNecessary);
+ }
+
private static class RPCServiceHolder {
private static final ExternalRPCService INSTANCE = new
ExternalRPCService();
diff --git
a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/CommonMessages.java
b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/CommonMessages.java
index ae97d09ddc2..94194a086b4 100644
---
a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/CommonMessages.java
+++
b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/CommonMessages.java
@@ -222,5 +222,7 @@ public final class CommonMessages {
public static final String
EXCEPTION_THE_ORDER_BY_CLAUSE_OF_THE_DATA_ARGUMENT_MUST_CONTAIN_EXACTLY_THE_TIME_COLUMN_SPECIFIED_BY_THE_TIMECOL_ARGUMENT_4375BAE9
= "The ORDER BY clause of the DATA argument must contain exactly the time
column specified by the TIMECOL argument.";
public static final String EXCEPTION_UNSUPPORTED_M4_VALUE_TYPE_AF0EF286 =
"Unsupported M4 value type: ";
public static final String
EXCEPTION_DISK_SPACE_WARNING_THRESHOLD_MUST_BE_IN_0_1_BUT_WAS_7B345766 =
"disk_space_warning_threshold must be in [0, 1), but was ";
+ public static final String
LOG_TRUSTED_CHANNEL_FUNCTION_FAILED_INITIATOR_ARG_TARGET_ARG_E4C28443 =
+ "Trusted channel function failed: initiator=%s, target=%s";
}
diff --git
a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/CommonMessages.java
b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/CommonMessages.java
index 71c45ccaf3f..07d886a44f3 100644
---
a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/CommonMessages.java
+++
b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/CommonMessages.java
@@ -215,5 +215,7 @@ public final class CommonMessages {
public static final String
EXCEPTION_THE_ORDER_BY_CLAUSE_OF_THE_DATA_ARGUMENT_MUST_CONTAIN_EXACTLY_THE_TIME_COLUMN_SPECIFIED_BY_THE_TIMECOL_ARGUMENT_4375BAE9
= "DATA 参数的 ORDER BY 子句必须仅包含 TIMECOL 参数指定的时间列。";
public static final String EXCEPTION_UNSUPPORTED_M4_VALUE_TYPE_AF0EF286 =
"不支持的 M4 值类型:";
public static final String
EXCEPTION_DISK_SPACE_WARNING_THRESHOLD_MUST_BE_IN_0_1_BUT_WAS_7B345766 =
"disk_space_warning_threshold 必须在 [0, 1) 范围内,但实际为 ";
+ public static final String
LOG_TRUSTED_CHANNEL_FUNCTION_FAILED_INITIATOR_ARG_TARGET_ARG_E4C28443 =
+ "可信信道功能失效:发起者=%s,目标端=%s";
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java
index d7c00c9128b..d4b94265f23 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java
@@ -19,12 +19,23 @@
package org.apache.iotdb.commons.audit;
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.auth.entity.User;
import org.apache.iotdb.commons.conf.CommonConfig;
import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.i18n.CommonMessages;
+import org.apache.iotdb.commons.utils.NodeUrlUtils;
+
+import javax.net.ssl.SSLException;
import java.util.function.Supplier;
public abstract class AbstractAuditLogger {
+ private static final long INTERNAL_AUDIT_LOG_USER_ID = 4;
+ private static final ThreadLocal<Boolean> RECORDING_TRUSTED_CHANNEL_FAILURE =
+ ThreadLocal.withInitial(() -> false);
+
public static final String OBJECT_AUTHENTICATION_AUDIT_STR =
"User %s (ID=%d) requests authority on object %s with result %s";
public static final String AUDIT_LOG_NODE_ID = "node_id";
@@ -61,4 +72,68 @@ public abstract class AbstractAuditLogger {
auditObject.get(),
auditEntity.getResult()));
}
+
+ /**
+ * Records a failure of the trusted-channel function.
+ *
+ * <p>The caller determines the channel direction and supplies the actual
initiator and target
+ * identifiers. This keeps the audit hook independent of any concrete
SSL/TLS implementation.
+ */
+ public void recordTrustedChannelFailureAuditLog(
+ final IAuditEntity auditEntity,
+ final Supplier<String> initiator,
+ final Supplier<String> target) {
+ log(
+ auditEntity
+ .setAuditEventType(AuditEventType.TRUSTED_CHANNEL_FUNCTION_FAILURE)
+ .setAuditLogOperation(AuditLogOperation.CONTROL)
+ .setResult(false),
+ () ->
+ String.format(
+ CommonMessages
+
.LOG_TRUSTED_CHANNEL_FUNCTION_FAILED_INITIATOR_ARG_TARGET_ARG_E4C28443,
+ initiator.get(),
+ target.get()));
+ }
+
+ public static boolean isSslFailure(Throwable failure) {
+ Throwable cause = failure;
+ while (cause != null) {
+ if (cause instanceof SSLException) {
+ return true;
+ }
+ cause = cause.getCause();
+ }
+ return false;
+ }
+
+ public void recordTrustedChannelFailureAuditLogIfNecessary(
+ Throwable failure, TEndPoint initiator, TEndPoint target) {
+ if (Boolean.TRUE.equals(RECORDING_TRUSTED_CHANNEL_FAILURE.get())
+ || !isSslFailure(failure)
+ || initiator == null
+ || target == null) {
+ return;
+ }
+
+ final String initiatorIdentifier =
NodeUrlUtils.convertTEndPointUrl(initiator);
+ final String targetIdentifier = NodeUrlUtils.convertTEndPointUrl(target);
+ RECORDING_TRUSTED_CHANNEL_FAILURE.set(true);
+ try {
+ recordTrustedChannelFailureAuditLog(
+ new UserEntity(
+ INTERNAL_AUDIT_LOG_USER_ID,
+ User.BUILTIN_INTERNAL_AUDIT_LOG_USERNAME,
+ initiatorIdentifier)
+ .setPrivilegeType(PrivilegeType.SECURITY),
+ () -> initiatorIdentifier,
+ () -> targetIdentifier);
+ } catch (RuntimeException auditFailure) {
+ if (auditFailure != failure) {
+ failure.addSuppressed(auditFailure);
+ }
+ } finally {
+ RECORDING_TRUSTED_CHANNEL_FAILURE.remove();
+ }
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
index 58e6f752b60..d9e2a1f5c30 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
@@ -45,7 +45,7 @@ public enum AuditEventType {
LOGIN_EXCEED_LIMIT,
SESSION_TIME_EXCEEDED,
LOGIN_REJECT_IP,
- SESSION_ENCRYPT_FAILED,
+ TRUSTED_CHANNEL_FUNCTION_FAILURE,
SYSTEM_OPERATION,
DN_SHUTDOWN;
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/TrustedChannelFailureHandler.java
similarity index 55%
copy from
iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
copy to
iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/TrustedChannelFailureHandler.java
index 58e6f752b60..b14cb894400 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AuditEventType.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/TrustedChannelFailureHandler.java
@@ -19,39 +19,15 @@
package org.apache.iotdb.commons.audit;
-public enum AuditEventType {
- CHANGE_AUDIT_OPTION,
- AUDIT_STORAGE_FULL,
- GENERATE_KEY,
- DESTROY_KEY,
- EXECUTE_ENCRYPT,
- OBJECT_AUTHENTICATION,
- LBAC_AUTHENTICATION,
- EXPORT_DATA_WITH_LABEL,
- IMPORT_DATA_WITH_LABEL,
- INTEGRITY_CHECK,
- LOGIN_FAIL_MAX_TIMES,
- MODIFY_PASSWD,
- LOGIN,
- LOGOUT,
- LOGIN_FINAL,
- MODIFY_SECURITY_OPTIONS,
- MODIFY_DEFAULT_SECURITY_VALUES,
- MODIFY_ROLE_MEMBERSHIP,
- REVOKE_FAILED,
- GRANT_ROLE_FAILED,
- LOGIN_RESOURCE_RESTRICT,
- LOGIN_FAILED_TRIES,
- LOGIN_EXCEED_LIMIT,
- SESSION_TIME_EXCEEDED,
- LOGIN_REJECT_IP,
- SESSION_ENCRYPT_FAILED,
- SYSTEM_OPERATION,
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
- DN_SHUTDOWN;
+@FunctionalInterface
+public interface TrustedChannelFailureHandler {
- @Override
- public String toString() {
- return name();
- }
+ TrustedChannelFailureHandler NO_OP =
+ (failure, initiator, target) -> {
+ // Do nothing.
+ };
+
+ void onFailure(Throwable failure, TEndPoint initiator, TEndPoint target);
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandler.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandler.java
new file mode 100644
index 00000000000..f99e6df4cfe
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandler.java
@@ -0,0 +1,152 @@
+/*
+ * 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.commons.service;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
+import org.apache.iotdb.rpc.TElasticFramedTransport;
+
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.ServerContext;
+import org.apache.thrift.server.TServerEventHandler;
+import org.apache.thrift.transport.TSocket;
+import org.apache.thrift.transport.TTransport;
+
+import javax.net.ssl.SSLSocket;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.Objects;
+
+/**
+ * Performs the server-side TLS handshake before the first Thrift request is
processed and reports
+ * handshake failures together with the raw socket peer and the local service
endpoint.
+ */
+public class TrustedChannelAuditServerEventHandler implements
TServerEventHandler {
+
+ private final TServerEventHandler delegate;
+ private final TEndPoint target;
+ private final TrustedChannelFailureHandler failureHandler;
+
+ public TrustedChannelAuditServerEventHandler(
+ TServerEventHandler delegate, TEndPoint target,
TrustedChannelFailureHandler failureHandler) {
+ this.delegate = Objects.requireNonNull(delegate);
+ this.target = Objects.requireNonNull(target);
+ this.failureHandler = Objects.requireNonNull(failureHandler);
+ }
+
+ @Override
+ public void preServe() {
+ delegate.preServe();
+ }
+
+ @Override
+ public ServerContext createContext(TProtocol input, TProtocol output) {
+ startHandshakeIfNecessary(output);
+ try {
+ return delegate.createContext(input, output);
+ } catch (RuntimeException | Error contextFailure) {
+ // A delegate may have already allocated connection state before
createContext fails. The
+ // Thrift server subsequently invokes this wrapper with a null context,
which is deliberately
+ // ignored by deleteContext below, so clean up the partially created
delegate context here.
+ try {
+ delegate.deleteContext(null, input, output);
+ } catch (RuntimeException | Error cleanupFailure) {
+ if (cleanupFailure != contextFailure) {
+ contextFailure.addSuppressed(cleanupFailure);
+ }
+ }
+ throw contextFailure;
+ }
+ }
+
+ @Override
+ public void deleteContext(ServerContext serverContext, TProtocol input,
TProtocol output) {
+ if (serverContext != null) {
+ delegate.deleteContext(serverContext, input, output);
+ }
+ }
+
+ @Override
+ public void processContext(
+ ServerContext serverContext, TTransport inputTransport, TTransport
outputTransport) {
+ delegate.processContext(serverContext, inputTransport, outputTransport);
+ }
+
+ private void startHandshakeIfNecessary(TProtocol output) {
+ Socket socket = getSocket(output);
+ if (!(socket instanceof SSLSocket)) {
+ return;
+ }
+
+ try {
+ ((SSLSocket) socket).startHandshake();
+ } catch (IOException e) {
+ notifyFailure(e, socket.getRemoteSocketAddress(),
socket.getLocalSocketAddress());
+ try {
+ socket.close();
+ } catch (IOException closeFailure) {
+ if (closeFailure != e) {
+ e.addSuppressed(closeFailure);
+ }
+ }
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void notifyFailure(
+ Throwable failure, SocketAddress remoteAddress, SocketAddress
localAddress) {
+ TEndPoint initiator = toEndPoint(remoteAddress);
+ if (initiator == null) {
+ return;
+ }
+ TEndPoint actualTarget = toEndPoint(localAddress);
+ try {
+ failureHandler.onFailure(failure, initiator, actualTarget == null ?
target : actualTarget);
+ } catch (RuntimeException auditFailure) {
+ if (auditFailure != failure) {
+ failure.addSuppressed(auditFailure);
+ }
+ }
+ }
+
+ private static Socket getSocket(TProtocol protocol) {
+ if (protocol == null
+ || !(protocol.getTransport() instanceof TElasticFramedTransport
framedTransport)) {
+ return null;
+ }
+ TTransport socketTransport = framedTransport.getSocket();
+ return socketTransport instanceof TSocket socket ? socket.getSocket() :
null;
+ }
+
+ private static TEndPoint toEndPoint(SocketAddress socketAddress) {
+ if (!(socketAddress instanceof InetSocketAddress inetSocketAddress)) {
+ return null;
+ }
+ String host =
+ inetSocketAddress.getAddress() == null
+ ? inetSocketAddress.getHostString()
+ : inetSocketAddress.getAddress().getHostAddress();
+ return new TEndPoint(host, inetSocketAddress.getPort());
+ }
+}
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/audit/AbstractAuditLoggerTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/audit/AbstractAuditLoggerTest.java
new file mode 100644
index 00000000000..fcb3dbf6d1c
--- /dev/null
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/audit/AbstractAuditLoggerTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.commons.audit;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.i18n.CommonMessages;
+
+import org.apache.thrift.TException;
+import org.junit.Test;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class AbstractAuditLoggerTest {
+
+ @Test
+ public void testRecordTrustedChannelFailureAuditLog() {
+ TestAuditLogger auditLogger = new TestAuditLogger();
+ UserEntity auditEntity = new UserEntity(0, "system", "127.0.0.1");
+ AtomicInteger identifierEvaluationCount = new AtomicInteger();
+
+ auditLogger.recordTrustedChannelFailureAuditLog(
+ auditEntity,
+ () -> {
+ identifierEvaluationCount.incrementAndGet();
+ return "[email protected]:10730";
+ },
+ () -> {
+ identifierEvaluationCount.incrementAndGet();
+ return "[email protected]:10730";
+ });
+
+ assertSame(auditEntity, auditLogger.auditEntity);
+ assertEquals(
+ AuditEventType.TRUSTED_CHANNEL_FUNCTION_FAILURE,
+ auditLogger.auditEntity.getAuditEventType());
+ assertEquals(AuditLogOperation.CONTROL,
auditLogger.auditEntity.getAuditLogOperation());
+ assertFalse(auditLogger.auditEntity.getResult());
+ assertEquals(0, identifierEvaluationCount.get());
+ assertEquals(
+ String.format(
+
CommonMessages.LOG_TRUSTED_CHANNEL_FUNCTION_FAILED_INITIATOR_ARG_TARGET_ARG_E4C28443,
+ "[email protected]:10730",
+ "[email protected]:10730"),
+ auditLogger.auditLog.get());
+ assertEquals(2, identifierEvaluationCount.get());
+ }
+
+ @Test
+ public void testIsSslFailure() {
+ assertTrue(AbstractAuditLogger.isSslFailure(new SSLException("ssl
failure")));
+ assertTrue(
+ AbstractAuditLogger.isSslFailure(
+ new TException(new IOException(new
SSLHandshakeException("handshake failure")))));
+ assertFalse(
+ AbstractAuditLogger.isSslFailure(new TException(new
IOException("network failure"))));
+ assertFalse(AbstractAuditLogger.isSslFailure(null));
+ }
+
+ @Test
+ public void testRecordTrustedChannelFailureAuditLogIfNecessary() {
+ TestAuditLogger auditLogger = new TestAuditLogger();
+ TEndPoint initiator = new TEndPoint("10.0.0.1", 10730);
+ TEndPoint target = new TEndPoint("10.0.0.2", 10730);
+
+ auditLogger.recordTrustedChannelFailureAuditLogIfNecessary(
+ new IOException("network failure"), initiator, target);
+ assertNull(auditLogger.auditEntity);
+
+ auditLogger.recordTrustedChannelFailureAuditLogIfNecessary(
+ new SSLHandshakeException("handshake failure"), initiator, target);
+ assertEquals(
+ AuditEventType.TRUSTED_CHANNEL_FUNCTION_FAILURE,
+ auditLogger.auditEntity.getAuditEventType());
+ assertEquals(
+ String.format(
+
CommonMessages.LOG_TRUSTED_CHANNEL_FUNCTION_FAILED_INITIATOR_ARG_TARGET_ARG_E4C28443,
+ "10.0.0.1:10730",
+ "10.0.0.2:10730"),
+ auditLogger.auditLog.get());
+ }
+
+ @Test
+ public void testAuditFailureShouldNotReplaceTrustedChannelFailure() {
+ final RuntimeException auditFailure = new RuntimeException("audit
failure");
+ final ThrowingAuditLogger auditLogger = new
ThrowingAuditLogger(auditFailure);
+ final SSLHandshakeException channelFailure = new
SSLHandshakeException("handshake failure");
+ final TEndPoint initiator = new TEndPoint("10.0.0.1", 10730);
+ final TEndPoint target = new TEndPoint("10.0.0.2", 10730);
+
+ auditLogger.recordTrustedChannelFailureAuditLogIfNecessary(channelFailure,
initiator, target);
+
+ assertEquals(1, auditLogger.invocationCount.get());
+ assertEquals(1, channelFailure.getSuppressed().length);
+ assertSame(auditFailure, channelFailure.getSuppressed()[0]);
+ }
+
+ private static class TestAuditLogger extends AbstractAuditLogger {
+
+ private IAuditEntity auditEntity;
+ private Supplier<String> auditLog;
+
+ @Override
+ public void log(IAuditEntity auditLogFields, Supplier<String> log) {
+ auditEntity = auditLogFields;
+ auditLog = log;
+ }
+ }
+
+ private static class ThrowingAuditLogger extends AbstractAuditLogger {
+
+ private final RuntimeException auditFailure;
+ private final AtomicInteger invocationCount = new AtomicInteger();
+
+ private ThrowingAuditLogger(final RuntimeException auditFailure) {
+ this.auditFailure = auditFailure;
+ }
+
+ @Override
+ public void log(final IAuditEntity auditLogFields, final Supplier<String>
log) {
+ invocationCount.incrementAndGet();
+ throw auditFailure;
+ }
+ }
+}
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandlerTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandlerTest.java
new file mode 100644
index 00000000000..f231ef18d2a
--- /dev/null
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandlerTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.commons.service;
+
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.TrustedChannelFailureHandler;
+import org.apache.iotdb.rpc.TElasticFramedTransport;
+
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.server.ServerContext;
+import org.apache.thrift.server.TServerEventHandler;
+import org.apache.thrift.transport.TSocket;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocket;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.InetSocketAddress;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class TrustedChannelAuditServerEventHandlerTest {
+
+ @Test
+ public void testHandshakeBeforeCreatingDelegateContext() throws Exception {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ ServerContext context = mock(ServerContext.class);
+ TProtocol input = mock(TProtocol.class);
+ SSLSocket socket = mock(SSLSocket.class);
+ TProtocol output = createProtocol(socket);
+ TEndPoint target = new TEndPoint("10.0.0.2", 10730);
+ when(delegate.createContext(input, output)).thenReturn(context);
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate, target, TrustedChannelFailureHandler.NO_OP);
+
+ assertSame(context, handler.createContext(input, output));
+ InOrder inOrder = inOrder(socket, delegate);
+ inOrder.verify(socket).startHandshake();
+ inOrder.verify(delegate).createContext(input, output);
+ }
+
+ @Test
+ public void testHandshakeFailureReportsPeerAndSkipsDelegate() throws
Exception {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ TProtocol input = mock(TProtocol.class);
+ SSLSocket socket = mock(SSLSocket.class);
+ TProtocol output = createProtocol(socket);
+ SSLHandshakeException failure = new SSLHandshakeException("handshake
failure");
+ InetSocketAddress remoteAddress = new InetSocketAddress("192.0.2.10",
45123);
+ TEndPoint target = new TEndPoint("10.0.0.2", 10730);
+ AtomicReference<Throwable> reportedFailure = new AtomicReference<>();
+ AtomicReference<TEndPoint> reportedInitiator = new AtomicReference<>();
+ AtomicReference<TEndPoint> reportedTarget = new AtomicReference<>();
+ when(socket.getRemoteSocketAddress()).thenReturn(remoteAddress);
+ org.mockito.Mockito.doThrow(failure).when(socket).startHandshake();
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate,
+ target,
+ (throwable, initiator, endpoint) -> {
+ reportedFailure.set(throwable);
+ reportedInitiator.set(initiator);
+ reportedTarget.set(endpoint);
+ });
+
+ UncheckedIOException thrown =
+ assertThrows(UncheckedIOException.class, () ->
handler.createContext(input, output));
+
+ assertSame(failure, thrown.getCause());
+ assertSame(failure, reportedFailure.get());
+ assertEquals(new TEndPoint("192.0.2.10", 45123), reportedInitiator.get());
+ assertSame(target, reportedTarget.get());
+ verify(socket).close();
+ verify(delegate, never()).createContext(any(), any());
+
+ handler.deleteContext(null, input, output);
+ verify(delegate, never()).deleteContext(isNull(), any(), any());
+ }
+
+ @Test
+ public void testWrappedSslFailureReachesCallback() throws Exception {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ TProtocol input = mock(TProtocol.class);
+ SSLSocket socket = mock(SSLSocket.class);
+ TProtocol output = createProtocol(socket);
+ IOException failure =
+ new IOException(
+ "wrapped handshake failure", new SSLHandshakeException("handshake
failure"));
+ AtomicReference<Throwable> reportedFailure = new AtomicReference<>();
+ when(socket.getRemoteSocketAddress()).thenReturn(new
InetSocketAddress("192.0.2.10", 45123));
+ org.mockito.Mockito.doThrow(failure).when(socket).startHandshake();
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate,
+ new TEndPoint("10.0.0.2", 10730),
+ (throwable, initiator, endpoint) ->
reportedFailure.set(throwable));
+
+ UncheckedIOException thrown =
+ assertThrows(UncheckedIOException.class, () ->
handler.createContext(input, output));
+
+ assertSame(failure, thrown.getCause());
+ assertSame(failure, reportedFailure.get());
+ verify(delegate, never()).createContext(any(), any());
+ }
+
+ @Test
+ public void testHandshakeFailurePrefersActualLocalEndpoint() throws
Exception {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ TProtocol input = mock(TProtocol.class);
+ SSLSocket socket = mock(SSLSocket.class);
+ TProtocol output = createProtocol(socket);
+ SSLHandshakeException failure = new SSLHandshakeException("handshake
failure");
+ AtomicReference<TEndPoint> reportedTarget = new AtomicReference<>();
+ when(socket.getRemoteSocketAddress()).thenReturn(new
InetSocketAddress("192.0.2.10", 45123));
+ when(socket.getLocalSocketAddress()).thenReturn(new
InetSocketAddress("10.0.0.9", 10730));
+ org.mockito.Mockito.doThrow(failure).when(socket).startHandshake();
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate,
+ new TEndPoint("0.0.0.0", 10730),
+ (throwable, initiator, endpoint) -> reportedTarget.set(endpoint));
+
+ assertThrows(UncheckedIOException.class, () ->
handler.createContext(input, output));
+
+ assertEquals(new TEndPoint("10.0.0.9", 10730), reportedTarget.get());
+ }
+
+ @Test
+ public void testCloseFailureDoesNotSelfSuppressHandshakeFailure() throws
Exception {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ TProtocol input = mock(TProtocol.class);
+ SSLSocket socket = mock(SSLSocket.class);
+ TProtocol output = createProtocol(socket);
+ SSLHandshakeException failure = new SSLHandshakeException("handshake
failure");
+ when(socket.getRemoteSocketAddress()).thenReturn(new
InetSocketAddress("192.0.2.10", 45123));
+ org.mockito.Mockito.doThrow(failure).when(socket).startHandshake();
+ org.mockito.Mockito.doThrow(failure).when(socket).close();
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate, new TEndPoint("10.0.0.2", 10730),
TrustedChannelFailureHandler.NO_OP);
+
+ UncheckedIOException thrown =
+ assertThrows(UncheckedIOException.class, () ->
handler.createContext(input, output));
+
+ assertSame(failure, thrown.getCause());
+ assertEquals(0, failure.getSuppressed().length);
+ verify(delegate, never()).createContext(any(), any());
+ }
+
+ @Test
+ public void testDelegateContextFailureCleansUpExactlyOnce() {
+ TServerEventHandler delegate = mock(TServerEventHandler.class);
+ TProtocol input = mock(TProtocol.class);
+ TProtocol output = mock(TProtocol.class);
+ RuntimeException contextFailure = new RuntimeException("context failure");
+
org.mockito.Mockito.doThrow(contextFailure).when(delegate).createContext(input,
output);
+
+ TrustedChannelAuditServerEventHandler handler =
+ new TrustedChannelAuditServerEventHandler(
+ delegate, new TEndPoint("10.0.0.2", 10730),
TrustedChannelFailureHandler.NO_OP);
+
+ assertSame(
+ contextFailure,
+ assertThrows(RuntimeException.class, () ->
handler.createContext(input, output)));
+
+ // TThreadPoolServer invokes deleteContext with the null context after
createContext fails.
+ handler.deleteContext(null, input, output);
+
+ InOrder inOrder = inOrder(delegate);
+ inOrder.verify(delegate).createContext(input, output);
+ inOrder.verify(delegate).deleteContext(null, input, output);
+ verify(delegate).deleteContext(null, input, output);
+ }
+
+ private static TProtocol createProtocol(SSLSocket socket) {
+ TProtocol protocol = mock(TProtocol.class);
+ TElasticFramedTransport framedTransport =
mock(TElasticFramedTransport.class);
+ TSocket socketTransport = mock(TSocket.class);
+ when(protocol.getTransport()).thenReturn(framedTransport);
+ when(framedTransport.getSocket()).thenReturn(socketTransport);
+ when(socketTransport.getSocket()).thenReturn(socket);
+ return protocol;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 331a4524613..e427d8680a7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -539,6 +539,11 @@
<artifactId>jetty-http</artifactId>
<version>${jetty.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>