奇安信集团作为一家网络安全公司是如何基于 Flink 构建 CEP 引擎实时检测网络攻击?其中面临的挑战以及宝贵的实践经验有哪些?本文主要内容分为以下四个方面:
作者:韩鹏@奇安信
奇安信集团作为一家网络安全公司,专门为政府、企业,教育、金融等机构和组织提供企业级网络安全技术、产品和服务,奇安信的 NGSOC 产品的核心引擎是一个 CEP 引擎,用于实时检测网络攻击,其技术演进过程如下图所示。
奇安信产品具体的应用场景是企业系统的安全检测和数据分析,其自下而上分为四个业务处理流程,分别是数据的采集、解析、处理和展示结果,这其中最核心的是第三层数据处理。该产品的用户主要是安全规则团队,其可以使用规则编辑器来对安全规则进行添加、删除、编辑和查找操作,并可批量启动/停用多个规则,同时可以将处于启动状态的有效规则统一发送给产品。
在数据规模方面,产品解决的不是一个或几个大型数据集群的问题,而是数以百计的中小型数据集群的运维问题。在 B2B 领域,由于产品是直接部署到客户方,很多客户使用的是内部隔离网,无法连接外网,且没有专门人员负责集群的运维,这种情况下哪怕一个小升级都会耗费大量时间。因此,产品更多关注该领域下数据集群可运维性问题的解决。
奇安信在最初计划使用 Flink 作为技术方案并进行调研的过程中,发现了其一系列的痛点问题。由于企业级硬件资源环境受限,规则集数量及种类不确定,使得 Flink 程序运行难以控制,并且现有的库“Flink SQL”和“Flink CEP”均不能满足其业务性能需求。具体的痛点如下:
**1.不能进行语义优化、不便于动态更新规则。
**
网络安全事件井喷式发生的今天,安全需求迅速扩展。为了能够在有限时间内对特定语义的快速支持,关联引擎的整体架构必须异常灵活,才能适应未来安全分析场景的各种需求,而基于开源关联引擎实现的产品会在激烈的需求变化时遇到很多问题。
2.状态监控 & 高可用支持不足。
面向企业级的网络安全监测引擎具有一些特定需求,当前解决方案对此支持较差。
3.CEP 网络负载高、CPU 利用率低。
和互联网企业内部使用的大型集群相比,奇安信面向的企业级应用集群规模较小,硬件资源受限,且客户的定制需求较多,导致安全监测的规则要求更严格,引擎发布成本较高。但是,现有的 Flink 开源解决方案,或者需要根据业务需求进行改造,或者性能较差,均不能较好地解决上述问题。
其他的痛点问题还包括不支持空值窗口出发、以及聚合不保存原始数据等。
为了解决上述问题,奇安信在 Flink 的基础上推出了一种全新的 CEP 引擎, Sabre。其整体架构如下图所示,其中包含三大核心模块,左侧是配置端,中间是 Sabre-server,右侧是 Sabre 运行端。核心数据流存在两条主线,红线表示规则的提交、编译、发布和运行流程。绿线表示状态监控的生成、收集、统计和展示流程。如图所示,此架构与 Hive 极为相似,是一种通用的大数据 OLAP 系统架构。下面详细介绍三大核心模块和两大核心数据流。
Flink 有多种运行模式(例如 standalone Flink cluster、Flink cluster on YARN、Flink job on YARN 等),Sabre 采用了“Flink job on YARN”模式,在奇安信 NGSOC 应用的特定场景下,采用 YARN 可统一维护硬件资源,并且使用 Flink job on YARN 可与 Hadoop 平台进行无缝对接,以此很好的实现了任务间资源隔离。
在 Sabre 任务执行过程中,Kafka 数据源向引擎提供原始事件。引擎处理结果分为回注事件和告警事件两类。告警事件会输出到目的 Kafka,供下级应用消费。回注事件表示一条规则的处理结果可直接回注到下级规则,作为下级规则的数据源事件,由此可实现规则的相互引用。
绿线流程表示任务执行过程中会定时输出节点的运行监控消息到 Sabre-server 的监控消息缓存器,然后监控消息统计器再汇总各个规则实例的运行监控消息,统计为整条规则的运行监控状态,最后通过 Sabre-server 提供的 REST 接口推送给规则监控端。
Sabre 的组件依赖与版本兼容情况如下图所示。
如图所示,Deploy、Core、APIs、Libraries 四层是大家熟知的 Flink 基本的组件栈。Sabre 对 API 层的依赖降到了最低,只引用了 DataStream、KeyedStream 和 SplitStream 三种数据流 API。函数依赖只包括 DataStream 的 assignTimestamps、flatMap、union、keyBy、split、process、addSink 等函数,KeyedStream 最基础的 process 函数,以及 SplitStream 的 select 函数。由于依赖的 Flink API 较少,Sabre 可以很容易适配到各个 Flink 版本,从而具有良好的 Flink 版本兼容性。
在算子方面,Sabre 对 Flink 进行了一系列的重构,下图展示了这 Flink 和 Sabre 这二者之间的对比关系,其中主要包含三列,即 Flink 原生算子、Sabre 算子和两者之间的比较结果。比较结果主要有四种情况,相同(Same)、实现(Implement)、优化(优化)和新增(New)。Sabre 共有 13 种完全自研的核心算子,其中 Datasource、CustomKafkaSink 和 CustomDatabase 按照 Flink 接口要求做了具体实现,Filter、Key、Join 和 Aggregation 按照 Flink 原有算子的语义做了重新实现,CustomWindow 和 Sequence 在 Flink 原有算子语义的基础上做了优化实现。
下图展示了 Sabre 的规则与 EPL 设计。序列 Sequence、聚合 Aggregation、不发生 NotOccur、流式机器学习 StreamML 和连接 Join 均属于 Window 执行时间包含的计算性算子。蓝色虚线表示引用动态数据(Dynamic data),紫色虚线表示 Filter 无须经过 Window 可直连输出组件。
众所周知,Join 和 Aggregation 的时间范围由 Window 限定,而 Flink 原有 Window 算子不适合网络安全监测需求,为此 Sabre 设计了一种“自定义 Window 算子”,且重新实现了与“自定义 Window 算子”相匹配的 Join 和 Aggregation 算子。全新的 Window 具有以下六个主要特点:
Sabre 用 EPL 对 Flink CEP 实现的序列算子进行了重新设计,左边是 Flink CEP 官方代码展示,采用程序代码的方式拼凑“NFA 自动机”。右边是 Sabre 中 Sequence 算子的实现方式,其中包含了三个不同的 filter,通过正则表达式的使用来提升其表达的能力,并且,Sabre 将 filter 前置,无效事件不会传输到 window 算子,从而较少了不必要的网络负载。并且,只有较少的有效数据需要执行正则匹配,降低了 CPU 利用率(filter 可以并行)。
NotOccur 是 Sabre 在 Flink 基础上新增的一个算子,支持空事件触发。
Sabre 还实现了一种针对窗口的全局触发器 Trigger,Trigger 能够将多个子计算性算子组合为复杂表达式,并实现了具有 GroupBy/Distinct 功能的 Key 算子以适配此 Trigger 算子。
Dynamicdata 可以映射为数据库中的一个表,但是对这个表要进行特别的优化,具体来讲,如果一个事件的 IP 在威胁情报列表中,而这个威胁情报有可能比较长,比如十几万行甚至更长,这种情况下需要对该表数据结构进行优化以提升效率。Dynamic data 可以在其他算子中使用,如 Filter、Join 等。
机器学习在网络异常检测上已经越来越重要,为适应实时检测的需求,Sabre 没有使用 Flink MachineLearning,而是引入了自研的流式机器学习算子 StreamML。
Flink MachineLearning 是一种基于批模式 DataSetApi 实现的机器学习函数库,而 StreamML 是一种流式的机器学习算子,其目的是为了满足网络安全监测的特定需求。与阿里巴巴开源的 Alink 相比,StreamML 允许机器学习算法工程师通过配置规则的方式即可快速验证算法模型,无需编写任何程序代码。并且,流式机器学习算子 StreamML 实现了“模型训练/更新”与“模型使用”统一的理念。其核心功能是通过算法、技术及模型实现数据训练及对新数据检测。该流式机器学习算子 StreamML 引入的输入有三类,分别是:事件流、检测对象和对象属性;输出也包含三类,分别是:事件、告警和预警。
流式机器学习算子 StreamML 的组件栈包含三部分,从下往上依次为:机器学习方法、应用场景和产品业务。通过基本的机器学习算法(比如:统计学习算法、序列分析算法、聚类分析算法),流式机器学习算子 StreamML 可满足具体特定的安全监测应用场景(比如:行为特征异常检测、时间序列异常检测、群组聚类分析),进而为用户提供可理解的产品业务(比如:基线、用户及实体行为分析 UEBA)。
因为采用了 Flink 作为底层运行组件,所以 Sabre 具有与 Flink 等同的执行性能。并且,针对网络安全监测领域的特定需求,Sabre 还在以下方面进行了性能优化:
上图是 Sabre 流式状态机引擎的表示,其主要负责的功能是序列匹配。图中左边是标准的正则引擎,通常的流程可以从 Pattern 到语法树到 NFA 再到 DFA,也可以从 Paterrn 直接到 NFA;图左下侧是一个正则表达式的 NFA 表示,右侧是该正则表达式的 DFA 表示,使用该 DFA 的时候进行了改进(如图中绿色线)。其目的是为了在出现乱序的时候提升处理性能,在乱序发生在正则表达式后半段的时候,该改进对于性能提升的效果最好。
大规模正则引擎主要使用了两种互补的方法(图上半侧和下半侧)。在将 NFA 转向 DFA 的时候,很多情况下是不成功的,这种情况下往往会生成 DFA 的半成品,称为Unfinished-DFA,第一种方法属于混合状态自动机,包含 NFA 和 DFA,其适用于Pattern 量少于 1000 的情况。而第二种方法适用于 Pattern 量大于 1000 甚至上万的情况,该方法中首先需要寻找锚点,再做匹配,以降低整体的时间复杂度。这两种方法相结合能够较好地解决大规模正则匹配的问题。
多级规则是产品运维的一个显著特点。如下图所示,为满足复杂场景需求,一种规则的输出可直接作为另一种规则的输入。通过这种规则拆分的方式,能分层构造较为复杂的“多级规则”。如:图中的“暴力探测”规则结果可以直接回注到下面的“登陆成功 ”规则,而无须额外的通信组件,由此实现更为复杂的“暴力破解”规则。
产品采用微服务架构,使用多租户、多任务来满足多个规则引擎的使用场景,同时对资源进行了实时监控来保证系统的稳定运行。
规则级的状态和资源监控是非常重要的产品需求,产品采用分布式监控,提供三级分布式监控能力(用户、任务和规则),并支持吞吐量、EPS、CPU 和内存的监控。
整体系统保护主要涉及两方面,即流量控制和自我保护。
未来基于 Flink 构建的 Sabre 引擎会持续优化产品性能与功能,并将总结凝练项目中的优秀实践,及时回馈给 Apache Flink 社区。