This is an automated email from the ASF dual-hosted git repository. HTHou pushed a commit to branch codex/trusted-channel-failure-hooks in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit e5d3cc52f940e4e635fe855e254a133c56a661c9 Author: HTHou <[email protected]> AuthorDate: Fri Jul 24 14:38:19 2026 +0800 Add trusted channel failure reporting hooks --- external-service-impl/rest/pom.xml | 4 + .../java/org/apache/iotdb/rest/RestService.java | 4 + .../rest/TrustedChannelAuditHandshakeListener.java | 73 +++++++++++ .../TrustedChannelAuditHandshakeListenerTest.java | 79 ++++++++++++ .../iotdb/confignode/audit/CNAuditLogger.java | 9 ++ .../async/AsyncDataNodeHeartbeatClientPool.java | 13 ++ .../CnToDnInternalServiceAsyncRequestManager.java | 13 ++ .../client/sync/SyncDataNodeClientPool.java | 16 +++ .../service/thrift/ConfigNodeRPCService.java | 8 +- .../iotdb/consensus/config/ConsensusConfig.java | 23 +++- .../apache/iotdb/consensus/iot/IoTConsensus.java | 4 +- .../iot/service/IoTConsensusRPCService.java | 14 ++- .../iotdb/consensus/pipe/IoTConsensusV2.java | 4 +- .../pipe/service/IoTConsensusV2RPCService.java | 14 ++- .../org/apache/iotdb/db/audit/DNAuditLogger.java | 14 +++ .../db/consensus/DataRegionConsensusImpl.java | 3 + .../iotdb/db/protocol/client/ConfigNodeClient.java | 10 ++ .../iotdb/db/protocol/client/an/AINodeClient.java | 11 ++ .../client/dn/AsyncTSStatusRPCHandler.java | 3 + .../execution/exchange/MPPDataExchangeService.java | 7 +- .../execution/exchange/sink/SinkChannel.java | 5 + .../execution/exchange/source/SourceHandle.java | 7 ++ .../plan/scheduler/AsyncPlanNodeSender.java | 3 +- .../plan/scheduler/AsyncSendPlanNodeHandler.java | 8 +- .../scheduler/FragmentInstanceDispatcherImpl.java | 3 + .../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 | 71 +++++++++++ .../apache/iotdb/commons/audit/AuditEventType.java | 2 +- ...Type.java => TrustedChannelFailureHandler.java} | 38 +----- .../client/request/AsyncRequestManager.java | 5 + .../TrustedChannelAuditServerEventHandler.java | 135 +++++++++++++++++++++ .../commons/audit/AbstractAuditLoggerTest.java | 114 +++++++++++++++++ .../TrustedChannelAuditServerEventHandlerTest.java | 121 ++++++++++++++++++ pom.xml | 5 + 37 files changed, 821 insertions(+), 48 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..3ba484a5ff7 --- /dev/null +++ b/external-service-impl/rest/src/main/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListener.java @@ -0,0 +1,73 @@ +/* + * 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) { + 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) { + failure.addSuppressed(auditFailure); + } + } + + private static TEndPoint toEndPoint(SocketAddress socketAddress) { + if (!(socketAddress instanceof InetSocketAddress)) { + return null; + } + InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; + 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..55ce2b7a194 --- /dev/null +++ b/external-service-impl/rest/src/test/java/org/apache/iotdb/rest/TrustedChannelAuditHandshakeListenerTest.java @@ -0,0 +1,79 @@ +/* + * 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()); + } + + 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/audit/CNAuditLogger.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/audit/CNAuditLogger.java index ccc1008eec3..8c739c54c9d 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/audit/CNAuditLogger.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/audit/CNAuditLogger.java @@ -19,8 +19,10 @@ package org.apache.iotdb.confignode.audit; +import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.audit.AbstractAuditLogger; import org.apache.iotdb.commons.audit.IAuditEntity; +import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.confignode.conf.ConfigNodeConfig; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.manager.ConfigManager; @@ -43,4 +45,11 @@ public class CNAuditLogger extends AbstractAuditLogger { @Override public void log(IAuditEntity auditLogFields, Supplier<String> log) {} + + public void recordTrustedChannelFailureAuditLogIfNecessary(Throwable failure, TEndPoint target) { + if (CommonDescriptor.getInstance().getConfig().isEnableInternalSSL()) { + recordTrustedChannelFailureAuditLogIfNecessary( + failure, new TEndPoint(CONF.getInternalAddress(), CONF.getInternalPort()), target); + } + } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/AsyncDataNodeHeartbeatClientPool.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/AsyncDataNodeHeartbeatClientPool.java index 516596a61b8..0c137615986 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/AsyncDataNodeHeartbeatClientPool.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/AsyncDataNodeHeartbeatClientPool.java @@ -27,6 +27,7 @@ import org.apache.iotdb.commons.client.async.AsyncDataNodeInternalServiceClient; import org.apache.iotdb.confignode.client.async.handlers.audit.DataNodeWriteAuditLogHandler; import org.apache.iotdb.confignode.client.async.handlers.heartbeat.DataNodeHeartbeatHandler; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.service.ConfigNode; import org.apache.iotdb.mpp.rpc.thrift.TAuditLogReq; import org.apache.iotdb.mpp.rpc.thrift.TDataNodeHeartbeatReq; @@ -57,6 +58,7 @@ public class AsyncDataNodeHeartbeatClientPool { client.getDataNodeHeartBeat(req, handler); dispatched = true; } catch (Exception e) { + recordTrustedChannelFailureAuditLogIfNecessary(e, endPoint); handleError(handler, e); } finally { returnClientIfNotDispatched(endPoint, client, dispatched); @@ -105,6 +107,17 @@ public class AsyncDataNodeHeartbeatClientPool { } } + private void recordTrustedChannelFailureAuditLogIfNecessary( + Throwable failure, TEndPoint targetEndPoint) { + if (ConfigNode.getInstance() == null || ConfigNode.getInstance().getConfigManager() == null) { + return; + } + ConfigNode.getInstance() + .getConfigManager() + .getAuditLogger() + .recordTrustedChannelFailureAuditLogIfNecessary(failure, targetEndPoint); + } + private static class AsyncDataNodeHeartbeatClientPoolHolder { private static final AsyncDataNodeHeartbeatClientPool INSTANCE = diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java index 9c90c469137..a651fd39b58 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/CnToDnInternalServiceAsyncRequestManager.java @@ -20,6 +20,7 @@ package org.apache.iotdb.confignode.client.async; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; +import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TFlushReq; import org.apache.iotdb.common.rpc.thrift.TNodeLocations; import org.apache.iotdb.common.rpc.thrift.TSetConfigurationReq; @@ -50,6 +51,7 @@ import org.apache.iotdb.confignode.client.async.handlers.rpc.subscription.PullCo import org.apache.iotdb.confignode.client.async.handlers.rpc.subscription.TopicPushMetaRPCHandler; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; +import org.apache.iotdb.confignode.service.ConfigNode; import org.apache.iotdb.mpp.rpc.thrift.TActiveTriggerInstanceReq; import org.apache.iotdb.mpp.rpc.thrift.TAlterEncodingCompressorReq; import org.apache.iotdb.mpp.rpc.thrift.TAlterTimeSeriesReq; @@ -557,6 +559,17 @@ public class CnToDnInternalServiceAsyncRequestManager } } + @Override + protected void onRequestFailure(Exception failure, TEndPoint targetEndPoint) { + if (ConfigNode.getInstance() == null || ConfigNode.getInstance().getConfigManager() == null) { + return; + } + ConfigNode.getInstance() + .getConfigManager() + .getAuditLogger() + .recordTrustedChannelFailureAuditLogIfNecessary(failure, targetEndPoint); + } + private static class ClientPoolHolder { private static final CnToDnInternalServiceAsyncRequestManager INSTANCE = diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java index f35b6d906a5..4611636f51f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/sync/SyncDataNodeClientPool.java @@ -29,6 +29,7 @@ import org.apache.iotdb.commons.client.exception.ClientManagerException; import org.apache.iotdb.commons.client.sync.SyncDataNodeInternalServiceClient; import org.apache.iotdb.commons.exception.UncheckedStartupException; import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; +import org.apache.iotdb.confignode.service.ConfigNode; import org.apache.iotdb.mpp.rpc.thrift.TCleanDataNodeCacheReq; import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq; import org.apache.iotdb.mpp.rpc.thrift.TCreatePeerReq; @@ -177,6 +178,7 @@ public class SyncDataNodeClientPool { return executeSyncRequest(requestType, client, req); } catch (Exception e) { lastException = e; + recordTrustedChannelFailureAuditLogIfNecessary(e, endPoint); if (retry != DEFAULT_RETRY_NUM - 1) { LOGGER.warn( ConfigNodeMessages.FAILED_ON_DATANODE_RETRYING, requestType, endPoint, retry + 1); @@ -197,6 +199,7 @@ public class SyncDataNodeClientPool { return executeSyncRequest(requestType, client, req); } catch (Exception e) { lastException = e; + recordTrustedChannelFailureAuditLogIfNecessary(e, endPoint); if (retry != retryNum - 1) { LOGGER.warn( ConfigNodeMessages.FAILED_ON_DATANODE_RETRYING, requestType, endPoint, retry + 1); @@ -248,16 +251,29 @@ public class SyncDataNodeClientPool { return client.changeRegionLeader(req); } catch (ClientManagerException e) { LOGGER.error(ConfigNodeMessages.CAN_T_CONNECT_TO_DATA_NODE, dataNode, e); + recordTrustedChannelFailureAuditLogIfNecessary(e, dataNode); status = new TSStatus(TSStatusCode.CAN_NOT_CONNECT_DATANODE.getStatusCode()); status.setMessage(e.getMessage()); } catch (TException e) { LOGGER.error(ConfigNodeMessages.CHANGE_REGIONS_LEADER_ERROR_ON_DATE_NODE, dataNode, e); + recordTrustedChannelFailureAuditLogIfNecessary(e, dataNode); status = new TSStatus(TSStatusCode.REGION_LEADER_CHANGE_ERROR.getStatusCode()); status.setMessage(e.getMessage()); } return new TRegionLeaderChangeResp(status, -1L); } + private void recordTrustedChannelFailureAuditLogIfNecessary( + Throwable failure, TEndPoint targetEndPoint) { + if (ConfigNode.getInstance() == null || ConfigNode.getInstance().getConfigManager() == null) { + return; + } + ConfigNode.getInstance() + .getConfigManager() + .getAuditLogger() + .recordTrustedChannelFailureAuditLogIfNecessary(failure, targetEndPoint); + } + private static class ClientPoolHolder { private static final SyncDataNodeClientPool INSTANCE = new SyncDataNodeClientPool(); 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 1e247ad599e..ce21fa0c8db 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 @@ -133,7 +133,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..15590dcd2ba 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,16 @@ 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) { + public IoTConsensusRPCService( + TEndPoint thisNode, + IoTConsensusConfig config, + TrustedChannelFailureHandler trustedChannelFailureHandler) { this.thisNode = thisNode; this.config = config; + this.trustedChannelFailureHandler = trustedChannelFailureHandler; } @Override @@ -83,7 +90,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 cf1d4056398..b98b7e5f3f1 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 @@ -107,7 +107,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..22667813076 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,16 @@ 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) { + public IoTConsensusV2RPCService( + TEndPoint thisNode, + IoTConsensusV2Config config, + TrustedChannelFailureHandler trustedChannelFailureHandler) { this.thisNode = thisNode; this.config = config; + this.trustedChannelFailureHandler = trustedChannelFailureHandler; } @Override @@ -71,7 +78,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/audit/DNAuditLogger.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java index 90026e37ba0..1ba8309feeb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java @@ -19,11 +19,14 @@ package org.apache.iotdb.db.audit; +import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.audit.AbstractAuditLogger; import org.apache.iotdb.commons.audit.AuditLogFields; import org.apache.iotdb.commons.audit.IAuditEntity; +import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.queryengine.plan.Coordinator; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; @@ -58,6 +61,17 @@ public class DNAuditLogger extends AbstractAuditLogger { @Override public synchronized void log(IAuditEntity auditLogFields, Supplier<String> log) {} + public void recordTrustedChannelFailureAuditLogIfNecessary(Throwable failure, TEndPoint target) { + if (CommonDescriptor.getInstance().getConfig().isEnableInternalSSL()) { + recordTrustedChannelFailureAuditLogIfNecessary( + failure, + new TEndPoint( + IoTDBDescriptor.getInstance().getConfig().getInternalAddress(), + IoTDBDescriptor.getInstance().getConfig().getInternalPort()), + target); + } + } + public void logFromCN(AuditLogFields auditLogFields, String log, int nodeId) throws IllegalPathException {} 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/protocol/client/ConfigNodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java index a9fe5a60cd4..572cd65ca53 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 @@ -190,6 +190,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TTestOperation; import org.apache.iotdb.confignode.rpc.thrift.TThrottleQuotaResp; import org.apache.iotdb.confignode.rpc.thrift.TUnsetSchemaTemplateReq; import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq; +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; @@ -325,6 +326,7 @@ public class ConfigNodeClient implements IConfigNodeRPCService.Iface, ThriftClie return; } catch (TException e) { logger.warn(DataNodeMiscMessages.NODE_LEADER_MAY_DOWN_TRY_NEXT, configLeader); + recordTrustedChannelFailureAuditLogIfNecessary(e, configLeader); configLeader = null; exception = e; } @@ -347,6 +349,7 @@ public class ConfigNodeClient implements IConfigNodeRPCService.Iface, ThriftClie return; } catch (TException e) { logger.warn(DataNodeMiscMessages.NODE_MAY_DOWN_TRY_NEXT, tryEndpoint); + recordTrustedChannelFailureAuditLogIfNecessary(e, tryEndpoint); exception = e; } } @@ -461,6 +464,7 @@ public class ConfigNodeClient implements IConfigNodeRPCService.Iface, ThriftClie Thread.currentThread().getStackTrace()[2].getMethodName()); logger.warn(message, e); configLeader = null; + recordTrustedChannelFailureAuditLogIfNecessary(e, configNode); if (e.getCause() != null && e.getCause() instanceof SSLHandshakeException) { throw e; } @@ -485,6 +489,12 @@ public class ConfigNodeClient implements IConfigNodeRPCService.Iface, ThriftClie throw new TException(MSG_RECONNECTION_FAIL); } + private void recordTrustedChannelFailureAuditLogIfNecessary(Throwable failure, TEndPoint target) { + if (commonConfig.isEnableInternalSSL()) { + DNAuditLogger.getInstance().recordTrustedChannelFailureAuditLogIfNecessary(failure, target); + } + } + @FunctionalInterface private interface Operation<T> { T execute() throws TException; 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 8815acc036e..72f1886310b 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 @@ -50,6 +50,7 @@ import org.apache.iotdb.commons.conf.CommonConfig; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.consensus.ConfigRegionId; import org.apache.iotdb.confignode.rpc.thrift.TGetAINodeLocationResp; +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; @@ -177,6 +178,9 @@ public class AINodeClient implements IAINodeRPCService.Iface, AutoCloseable, Thr IOTDB_CONFIG.getAddressAndPort(), Thread.currentThread().getStackTrace()[2].getMethodName()); LOGGER.warn(message, e); + final TAINodeLocation currentLocation = CURRENT_LOCATION.get(); + recordTrustedChannelFailureAuditLogIfNecessary( + e, currentLocation == null ? null : currentLocation.getInternalEndPoint()); CURRENT_LOCATION.set(null); if (e.getCause() != null && e.getCause() instanceof SSLHandshakeException) { throw e; @@ -202,6 +206,7 @@ public class AINodeClient implements IAINodeRPCService.Iface, AutoCloseable, Thr return; } catch (TException e) { LOGGER.warn(DataNodeMiscMessages.AINODE_MAY_DOWN, endpoint, e); + recordTrustedChannelFailureAuditLogIfNecessary(e, endpoint); CURRENT_LOCATION.set(null); } } else { @@ -233,6 +238,12 @@ public class AINodeClient implements IAINodeRPCService.Iface, AutoCloseable, Thr client = new IAINodeRPCService.Client(property.getProtocolFactory().getProtocol(transport)); } + private void recordTrustedChannelFailureAuditLogIfNecessary(Throwable failure, TEndPoint target) { + if (COMMON_CONFIG.isEnableInternalSSL()) { + DNAuditLogger.getInstance().recordTrustedChannelFailureAuditLogIfNecessary(failure, target); + } + } + public TEndPoint getCurrentEndpoint() { TAINodeLocation loc = CURRENT_LOCATION.get(); if (loc == null) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/dn/AsyncTSStatusRPCHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/dn/AsyncTSStatusRPCHandler.java index c69f6ea4e40..fefb4983dd9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/dn/AsyncTSStatusRPCHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/dn/AsyncTSStatusRPCHandler.java @@ -21,6 +21,7 @@ package org.apache.iotdb.db.protocol.client.dn; import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.db.audit.DNAuditLogger; import org.apache.iotdb.db.i18n.DataNodeMiscMessages; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -77,6 +78,8 @@ public class AsyncTSStatusRPCHandler extends DataNodeAsyncRequestRPCHandler<TSSt @Override public void onError(Exception e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, targetNode.getInternalEndPoint()); String errorMsg = "Failed to " + requestType 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/queryengine/execution/exchange/sink/SinkChannel.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java index ec6c9c680a1..dc460466fc4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/sink/SinkChannel.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.sync.SyncDataNodeMPPDataExchangeServiceClient; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.db.audit.DNAuditLogger; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.FragmentInstanceId; @@ -579,6 +580,8 @@ public class SinkChannel implements ISinkChannel { client.onNewDataBlockEvent(newDataBlockEvent); break; } catch (Exception e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, remoteEndpoint); LOGGER.warn( DataNodeQueryMessages.FAILED_TO_SEND_NEW_DATA_BLOCK_EVENT_ATTEMPT, attempt, e); if (attempt == MAX_ATTEMPT_TIMES) { @@ -627,6 +630,8 @@ public class SinkChannel implements ISinkChannel { client.onEndOfDataBlockEvent(endOfDataBlockEvent); break; } catch (Exception e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, remoteEndpoint); LOGGER.warn(DataNodeQueryMessages.FAILED_TO_SEND_END_OF_DATA_BLOCK_EVENT, attempt, e); if (attempt == MAX_ATTEMPT_TIMES) { LOGGER.warn(DataNodeQueryMessages.FAILED_TO_SEND_END_OF_DATA_BLOCK_EVENT_2, e); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java index 4f90db0caed..66a2c70e551 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/exchange/source/SourceHandle.java @@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.client.IClientManager; import org.apache.iotdb.commons.client.sync.SyncDataNodeMPPDataExchangeServiceClient; import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.db.audit.DNAuditLogger; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; import org.apache.iotdb.db.queryengine.common.FragmentInstanceId; @@ -663,6 +664,8 @@ public class SourceHandle implements ISourceHandle { } break; } catch (Throwable e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, remoteEndpoint); LOGGER.warn( DataNodeQueryMessages.FAILED_TO_GET_DATA_BLOCK, @@ -744,6 +747,8 @@ public class SourceHandle implements ISourceHandle { client.onAcknowledgeDataBlockEvent(acknowledgeDataBlockEvent); break; } catch (Throwable e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, remoteEndpoint); LOGGER.warn( DataNodeQueryMessages.FAILED_TO_SEND_ACK_DATA_BLOCK_EVENT, startSequenceId, @@ -795,6 +800,8 @@ public class SourceHandle implements ISourceHandle { client.onCloseSinkChannelEvent(closeSinkChannelEvent); break; } catch (Throwable e) { + DNAuditLogger.getInstance() + .recordTrustedChannelFailureAuditLogIfNecessary(e, remoteEndpoint); LOGGER.warn( DataNodeQueryMessages.SEND_CLOSE_SINK_CHANNEL_EVENT_FAILED, remoteFragmentInstanceId, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncPlanNodeSender.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncPlanNodeSender.java index da5a2dfc889..00611993e12 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncPlanNodeSender.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncPlanNodeSender.java @@ -91,7 +91,8 @@ public class AsyncPlanNodeSender { pendingNumber, instanceId2RespMap, needRetryInstanceIndex, - startSendTime); + startSendTime, + entry.getKey()); try { AsyncDataNodeInternalServiceClient client = asyncInternalServiceClientManager.borrowClient(entry.getKey()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncSendPlanNodeHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncSendPlanNodeHandler.java index 23f99da3484..e73e4f6e1cd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncSendPlanNodeHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/AsyncSendPlanNodeHandler.java @@ -19,8 +19,10 @@ package org.apache.iotdb.db.queryengine.plan.scheduler; +import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics; import org.apache.iotdb.commons.utils.StatusUtils; +import org.apache.iotdb.db.audit.DNAuditLogger; import org.apache.iotdb.mpp.rpc.thrift.TSendBatchPlanNodeResp; import org.apache.iotdb.mpp.rpc.thrift.TSendSinglePlanNodeResp; import org.apache.iotdb.rpc.RpcUtils; @@ -43,6 +45,7 @@ public class AsyncSendPlanNodeHandler implements AsyncMethodCallback<TSendBatchP private final Map<Integer, TSendSinglePlanNodeResp> instanceId2RespMap; private final List<Integer> needRetryInstanceIndex; private final long sendTime; + private final TEndPoint targetEndPoint; private static final PerformanceOverviewMetrics PERFORMANCE_OVERVIEW_METRICS = PerformanceOverviewMetrics.getInstance(); @@ -51,12 +54,14 @@ public class AsyncSendPlanNodeHandler implements AsyncMethodCallback<TSendBatchP AtomicLong pendingNumber, Map<Integer, TSendSinglePlanNodeResp> instanceId2RespMap, List<Integer> needRetryInstanceIndex, - long sendTime) { + long sendTime, + TEndPoint targetEndPoint) { this.instanceIds = instanceIds; this.pendingNumber = pendingNumber; this.instanceId2RespMap = instanceId2RespMap; this.needRetryInstanceIndex = needRetryInstanceIndex; this.sendTime = sendTime; + this.targetEndPoint = targetEndPoint; } @Override @@ -78,6 +83,7 @@ public class AsyncSendPlanNodeHandler implements AsyncMethodCallback<TSendBatchP @Override public void onError(Exception e) { + DNAuditLogger.getInstance().recordTrustedChannelFailureAuditLogIfNecessary(e, targetEndPoint); if (needRetry(e)) { needRetryInstanceIndex.addAll(instanceIds); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java index df719a724fe..c76912ced24 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/scheduler/FragmentInstanceDispatcherImpl.java @@ -35,6 +35,7 @@ import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics; import org.apache.iotdb.commons.utils.RetryUtils; import org.apache.iotdb.consensus.exception.ConsensusGroupNotExistException; import org.apache.iotdb.consensus.exception.RatisReadUnavailableException; +import org.apache.iotdb.db.audit.DNAuditLogger; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.mpp.FragmentInstanceDispatchException; import org.apache.iotdb.db.exception.query.QueryTimeoutRuntimeException; @@ -587,6 +588,7 @@ public class FragmentInstanceDispatcherImpl implements IFragInstanceDispatcher { try { dispatchRemoteHelper(instance, endPoint); } catch (ClientManagerException | TException | RatisReadUnavailableException e) { + DNAuditLogger.getInstance().recordTrustedChannelFailureAuditLogIfNecessary(e, endPoint); LOGGER.warn( DataNodeQueryMessages .CAN_T_EXECUTE_REQUEST_ON_NODE_ARG_ERROR_MSG_IS_ARG_AND_WE_TRY_TO_RECONNECT_THIS_NODE, @@ -613,6 +615,7 @@ public class FragmentInstanceDispatcherImpl implements IFragInstanceDispatcher { | TException | RatisReadUnavailableException | ConsensusGroupNotExistException e1) { + DNAuditLogger.getInstance().recordTrustedChannelFailureAuditLogIfNecessary(e1, endPoint); dispatchRemoteFailed(endPoint, e1); } } catch (ConsensusGroupNotExistException e) { 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..f0b84bbc279 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 = + "可信信道功能失效:initiator=%s,target=%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..bdc17c49686 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,64 @@ 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 (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.AUDIT), + () -> initiatorIdentifier, + () -> targetIdentifier); + } 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 510912a302f..2c99e436676 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 @@ -44,7 +44,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 56% 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 510912a302f..e342cc8d231 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,38 +19,12 @@ 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, - 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) -> {}; + + void onFailure(Throwable failure, TEndPoint initiator, TEndPoint target); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java index b13d1ab0637..4332ebfa65f 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/client/request/AsyncRequestManager.java @@ -210,6 +210,7 @@ public abstract class AsyncRequestManager<RequestType, NodeLocation, Client> { endPoint, e.getMessage(), retryCount); + onRequestFailure(e, endPoint); if (handler != null) { try { handler.onError(e); @@ -237,6 +238,10 @@ public abstract class AsyncRequestManager<RequestType, NodeLocation, Client> { // In default, no need to do this } + protected void onRequestFailure(Exception failure, TEndPoint targetEndPoint) { + // In default, no need to do this + } + protected abstract TEndPoint nodeLocationToEndPoint(NodeLocation location); protected abstract AsyncRequestRPCHandler<?, RequestType, NodeLocation> buildHandler( 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..4c9679ee5eb --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandler.java @@ -0,0 +1,135 @@ +/* + * 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.SSLException; +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); + return delegate.createContext(input, output); + } + + @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) { + if (e instanceof SSLException) { + notifyFailure(e, socket.getRemoteSocketAddress()); + } + try { + socket.close(); + } catch (IOException closeFailure) { + e.addSuppressed(closeFailure); + } + throw new UncheckedIOException(e); + } + } + + private void notifyFailure(Throwable failure, SocketAddress remoteAddress) { + TEndPoint initiator = toEndPoint(remoteAddress); + if (initiator == null) { + return; + } + try { + failureHandler.onFailure(failure, initiator, target); + } catch (RuntimeException auditFailure) { + failure.addSuppressed(auditFailure); + } + } + + private static Socket getSocket(TProtocol protocol) { + if (protocol == null || !(protocol.getTransport() instanceof TElasticFramedTransport)) { + return null; + } + TTransport socketTransport = ((TElasticFramedTransport) protocol.getTransport()).getSocket(); + return socketTransport instanceof TSocket ? ((TSocket) socketTransport).getSocket() : null; + } + + private static TEndPoint toEndPoint(SocketAddress socketAddress) { + if (!(socketAddress instanceof InetSocketAddress)) { + return null; + } + InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; + 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..ccb8c937720 --- /dev/null +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/audit/AbstractAuditLoggerTest.java @@ -0,0 +1,114 @@ +/* + * 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.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( + "Trusted channel function failed: [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( + "Trusted channel function failed: initiator=10.0.0.1:10730, target=10.0.0.2:10730", + auditLogger.auditLog.get()); + } + + 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; + } + } +} 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..091f4b0d123 --- /dev/null +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/service/TrustedChannelAuditServerEventHandlerTest.java @@ -0,0 +1,121 @@ +/* + * 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.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()); + } + + 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 7dc7e38602b..7da51d6e3c2 100644 --- a/pom.xml +++ b/pom.xml @@ -571,6 +571,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>
