1. 基本概念
规则引擎是一种软件系统,它使用预定义的逻辑规则来解析和执行决策过程。这些规则通常以如果-那么(if-then)的格式表示,使得系统能够自动进行复杂决策和流程的处理。在不同的应用场景中,以下是规则引擎的一些主要用途和功能:
- 自动化决策制定:规则引擎可以根据预设的逻辑规则自动做出决策。例如,在金融领域,规则引擎可用于信用评分或贷款批准过程。
- 业务流程管理:它可以自动化复杂的业务流程,确保业务操作符合组织的规则和政策。例如,在保险行业,规则引擎可以自动处理索赔审批流程。
- 数据验证和处理:规则引擎可用于验证数据的准确性和完整性,以及对数据进行处理和转换。例如,在电子商务网站上,它可以用于验证订单信息。
- 个性化内容和推荐:根据用户的行为和偏好,规则引擎可以提供个性化的内容和推荐。这在在线零售和数字营销中尤为常见。
- 合规性和风险管理:在需要遵守严格法规的行业(如金融服务或医疗保健)中,规则引擎可以确保操作符合法律和内部政策。
- 实时监控和响应:规则引擎可以实时监控数据和事件,一旦满足特定条件,立即作出响应。例如,它可以用于网络安全中的入侵检测系统。
- 智能客户服务:在客户服务领域,规则引擎可以用于驱动聊天机器人或自动响应系统,提供快速、一致的客户支持。
- 资源优化和调度:规则引擎可以用于优化资源分配和调度任务。例如,在物流和运输行业,它可以用于优化货物配送路线。
规则引擎之所以强大,是因为它们能够处理复杂的逻辑,并且可以轻松地调整和更新规则,以适应不断变化的业务需求和环境。
当然现在也有很多开源项目的规则引擎,但是这些规则引擎往往适用大型项目中(例如:URule、Drools)且具有资源开销大和性能低的缺陷,在很多小型项目想使用,却很难利用上。
2. 选择 LuaJIT 作为基础的优势
规则引擎的核心优势在于其将业务决策逻辑与程序代码分离,使得非技术人员也能理解和管理规则。这种分离确保了业务逻辑的灵活性和可维护性,同时减少了对代码的频繁修改需求。
LuaJIT 是 Lua 编程语言的一个高性能实现版本,它通过即时编译(JIT)技术提供了显著的执行速度优势。选择 LuaJIT 作为规则引擎的基础有以下几个理由:
- 高性能:LuaJIT 提供接近原生代码的执行速度,这对于需要快速处理大量数据和复杂规则的规则引擎至关重要。
- 轻量级和灵活性:LuaJIT 的轻量级特性意味着它可以轻松集成到各种系统中,无论是嵌入式系统还是大型服务器。此外,Lua 语言本身的灵活性使得开发复杂规则变得简单直观。
- 易于集成:LuaJIT 与 C/C++ 之间的互操作性极强,使其可以轻松集成到现有的系统中,尤其是在需要与本地代码或其他语言编写的模块交互时。
- 广泛的社区支持:Lua 和 LuaJIT 享有活跃的开发者社区,提供了大量的资源和库来支持复杂应用程序的开发,这对于构建一个健壮的规则引擎是非常有价值的。
综上所述,LuaJIT 的高性能、灵活性和易于集成的特点使其成为构建高效、可扩展规则引擎的理想选择。
3. 整体框架和设计逻辑
3.1 明确的设计原则:数据透明
规则引擎将导出函数直接交给Pattern处理,将Pattern扫描的结果返回给集成者,Pattern的改动对规则引擎透明,规则引擎无需改动。
3.2 其他优势
- 跨平台性:windows、linux
- 多线程支持:规则引擎基于Lua解释器执行脚本,通过规则引擎接口创建多个Lua解释器即可实现多线程。
- 高性能:Lua解释器增强版LuaJIT性能基本接近C语言。
- 可扩展性:规则引擎可以很方便添加新的功能性API用来支持pattern的新功能。
- 沙箱运行。
3.3 整体框架和应用场景
当应用程序,调用引擎对外的API,对引擎初始化,引擎此时将相关功能API导出给lua解释器,同时将pattern(lua编写)加载到lua解释器中,整个环境和资源都准备完成。
当应用程序,业务需要时,调用引擎对外的API,执行业务逻辑。
当应用程序,不需要引擎时,调用引擎对外的API,执行引擎资源销毁逻辑。
pattern中包括两个部分,执行策略和规则集,都在引擎加载pattern时,加载到lua解释器中,根据业务逻辑调取执行。
这样引擎的整体构成可以分为2个:一个是完整的引擎,一个是pattern。具体细节如下图:
4. 实例应用 - 系统漏洞扫描引擎
为什么举例系统漏洞扫描,因为我们研究系统漏洞的时候,也发现存在开源软件openscap支持系统扫描。我也应该理所应当基于openscap去扩展或二次开发,支持这个系统漏洞扫描的能力。
先看看OpenSCAP(开放源码安全内容自动化协议)是一个开源框架,用于实现安全内容自动化协议(SCAP)的标准。SCAP是由美国国家标准与技术研究院(NIST)开发的一组标准,旨在提供机制来帮助组织自动地管理系统漏洞和配置不当的风险,同时确保符合安全合规性。
4.1 OpenSCAP
4.1.1 主要特点
- 符合 SCAP 标准:OpenSCAP 实现了 SCAP 标准,包括漏洞管理、配置管理、补丁管理等多个方面。
- 自动化漏洞评估和修复:它能够自动识别系统中的安全漏洞和不当配置,并提供修复建议。
- 合规性评估:OpenSCAP 可以用来评估系统是否符合各种安全标准和合规要求,例如 PCI-DSS、HIPAA、NIST 等。
- 灵活的报告功能:它生成详细的报告,清晰地展示了评估结果和需要采取的行动。
- 扩展性:OpenSCAP 社区提供了大量的安全基线和配置文件,适用于不同的操作系统和环境。
- 集成和自动化:OpenSCAP 可以集成到其他安全管理工具中,实现自动化的安全监控和响应
对于系统漏洞的评估,每个操作系统发行商都提供OpenSCAP 用于评估的oval文件,例如:
ubuntu:https://ubuntu.com/security/oval
Redhat: https://access.redhat.com/security/data/oval/v2/
Suse: https://www.suse.com/support/security/oval
Windows: https://oval.cisecurity.org/repository/download/5.11.2/vulnerability/windows.xml
使用也比较简单:OpenSCAP 漏洞扫描程序使用 OVAL 进行评估
oscap oval eval --results results.xml --report report.html rhel-7.oval.xml
下面是在redhat7 /ubuntu20 输出的实例(天翼云CTyunOS也有自己的oval文件,暂时未放出,就不在下面展示结果)。
4.1.1.1 redhat7 评估信息
评估文件rhel-7.oval.xml
结果文件redhat7_vulnerability.html
4.1.1.2 ubuntu20 评估信息
评估文件com.ubuntu.focal.usn.oval.xml
评估结果ubuntu20_vulnerabilty.html
4.1.2 存在问题和限制
感觉OpenSCAP挺方便的,但是还是存在问题:
- OpenSCAP依赖太多。使用openscap中的主要评估程序oscap因为功能强大,依赖比较多,实质我们不需要那么功能。
sudo yum install \\
cmake dbus-devel GConf2-devel libacl-devel libblkid-devel libcap-devel libcurl-devel \\
libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel \\
pcre-devel perl-XML-Parser perl-XML-XPath perl-devel python-devel rpm-devel swig \\
bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel
---
On Fedora 24+, the command to install the build dependencies is:
sudo yum install \\
cmake dbus-devel GConf2-devel libacl-devel libblkid-devel libcap-devel libcurl-devel \\
libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel \\
pcre-devel perl-XML-Parser perl-XML-XPath perl-devel python3-devel rpm-devel swig \\
bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel
---
On RHEL 8 / CentOS 8, the command to install the build dependencies is:
sudo yum install \\
cmake dbus-devel libacl-devel libblkid-devel libcap-devel libcurl-devel \\
libgcrypt-devel libselinux-devel libxml2-devel libxslt-devel libattr-devel make openldap-devel \\
pcre-devel perl-XML-Parser perl-XML-XPath perl-devel python36-devel rpm-devel swig \\
bzip2-devel gcc-c++ libyaml-devel xmlsec1-devel xmlsec1-openssl-devel
---
On Ubuntu 16.04, Debian 8 or Debian 9, the command to install the build dependencies is:
sudo apt-get install -y cmake libdbus-1-dev libdbus-glib-1-dev libcurl4-openssl-dev \\
libgcrypt20-dev libselinux1-dev libxslt1-dev libgconf2-dev libacl1-dev libblkid-dev \\
libcap-dev libxml2-dev libldap2-dev libpcre3-dev python-dev swig libxml-parser-perl \\
libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libapt-pkg-dev libyaml-dev \\
libxmlsec1-dev libxmlsec1-openssl`
通过ldd展示图如下:
- windows上不能使用。
- 不能优雅的实现编译到处运行(不依赖平台,不依赖版本)。
4.2 基于luajit实现系统漏洞规则引擎
结合OVAL定义我们基于OVAL进行分析,可以发现,OVAL实质一堆匹配的规则,所以我们就基于luajit引擎进行了实现漏洞扫描逻辑完全可行。不同的发行商定义的逻辑不同,这里我们以redhat为例。
rhel-7.oval.xml
OVAL XML 文件按照以下几个关键部分组织,每个部分在OVAL模式中都扮演着特定的角色。以下是其结构以及每个部分的意义(不是本文章重点,仅概述):
- 生成器(Generator):这一部分提供了关于OVAL文件本身的元数据,例如OVAL模式版本和用于生成文件的工具。
- 定义(Definitions):这是一个关键部分,其中定义了单个安全通告或配置检查。每个“定义”包括关于特定漏洞、配置问题或其他与安全相关条件的详细信息,这些是OVAL文件旨在检查的内容。
- 测试(Tests):这一部分包含了在定义中引用的测试。这些测试是对系统执行的实际检查,用于确定是否适用某个特定的定义(例如漏洞或配置问题)。它包括检查内容的具体细节及如何进行检查。
- 对象(Objects):这些是测试检查的具体项目。例如,如果测试是检查软件包的版本,那么对象将是该软件包。
- 状态(States):这一部分描述了对象必须处于的状态,以便特定定义被认为是适用的。例如,状态可能指定软件版本必须低于某个数字才被视为易受攻击。
- 变量(Variables):变量用于在OVAL定义中提供灵活性和可重用性。它们允许对测试、对象和状态中的某些信息进行参数化。
- 结构:
1)Definitions
部分包含多个definition
元素。
2)每个definition
元素进一步包括metadata
和criteria
子元素。 - 每个定义(Definition)的组成:
1)Metadata(元数据):提供有关每个安全定义的基本信息,如标题、描述、参考链接等。
2)Criteria(标准):定义了确定是否符合该安全定义的条件。在我们检查的第一个定义中,criteria
元素的operator
属性设置为 "OR",表示它可能包含多个条件,其中任何一个条件满足即表示定义适用。 - 意义:
1)这些definition
元素是OVAL文件的核心,用于描述特定的安全漏洞、配置问题或其他与安全相关的条件。
2)Metadata
部分为每个定义提供了上下文和参考信息,使用户可以理解和识别每个安全问题。
3)Criteria
部分则详细描述了如何确定系统是否受到特定漏洞的影响或是否符合特定的配置要求。这包括一系列的测试、条件和逻辑操作,用于评估系统的安全状态。
- 结构:
1)Tests
部分包含多种类型的测试元素,包括rpminfo_test
、rpmverifyfile_test
、uname_test
和textfilecontent54_test
。
2)每个测试元素都有一系列属性,如check
、comment
、id
和version
。例如,一个rpminfo_test
元素可能包含注释 "nspr is earlier than 0:4.10.6-3.el7" 和一个唯一的测试ID。 - 设计逻辑:
1)测试类型:OVAL定义了不同类型的测试,每种测试针对特定的系统方面或配置。例如,rpminfo_test
检查RPM包的信息,uname_test
检查操作系统内核版本,等等。
2)属性:
check
:指定如何评估测试(例如,“至少一个”)。comment
:提供有关测试目的的描述性信息。id
:为每个测试提供一个唯一的标识符。version
:指明测试定义的版本。
3)测试与标准的关联:在Criteria
部分定义的criterion
元素通过其test_ref
属性引用这些测试,形成标准的评估逻辑。
- 意义:
1)Tests
部分是OVAL文件中的实际执行部分,它定义了用于检查系统是否符合特定定义条件的具体测试。
2)通过结合不同类型的测试,OVAL能够对系统的多种方面进行全面的安全评估。
4.2.1 引擎部分
4.2.1.1 对外API
基本一致和框架一致
Initialize:
返回值: 指向SCAN_CONTEXT的对象,也是Lua解释器
LoadPattern
参数:
SCAN_CONTEXTpScanContext, lua解释器指针
strPatternFilePath, Pattern路径
返回值:TRUE/FALSE
ExecuteScan
参数:
SCAN_CONTEXTpScanContext, lua解释器指针
SCAN_RESULT&pScanResult, 扫描结果的json格式字符串(参考附录-事件说明)以及长度 (输入时候pScanResult =NULL,并且需要调用者使用完后释放)
返回值: TRUE/FALSE, 是否执行成功。当执行成功时,返回True,否则返回false。返回false可能是在pattern中处理发生了错误。
FreeScanResult
参数: SCAN_RESULT&pScanResult
返回值:释放成功或者失败
GetPatternVersion
参数说明
pScanContext lua 解释器指针
OutVersion: 用作返回版本信息的字符串
OutSize: OutVersion 的最大长度,此长度必须大于等于64
返回值 True/false
UnInitialize
参数:VA_SCAN_CONTEXT* pScanContext, lua解释器指针
返回值: null
4.2.1.2 功能性API
漏扫引擎需要实现一些功能性API给pattern脚本调用,这些功能性API, 在Initialize()中通过luaL_register()注册到Lua解释器中,后续脚本执行的时候可以直接调用这些API。
FunGetLinuxBuild //获取Linux产品版本,如:“Red Hat Enterprise Linux”。
FunLinuxVersion //获取Linux版本,如:“7.1”。
FunGetFileVersion //获取文件版本,如:“3.10.0-693.21.1.el7.x86_64”。
FunGetKernelRuning //获取当前运行内核版本,如:“3.10.0-693.21.1.el7.x86_64”。
FunGetKernelBoot //获取设置的启动内核版本,如:“3.10.0-693.21.1.el7.x86_64”。
FunGetWindowsVersion //获取Windows版本,如:“21H1”。
FunGetWindowsBuild //获取Windows OS build版本的高位,如:“19043”。
FunGetWindowsRevisonNumber //获取Windows OS build版本的低位,如:“1237”。
FunGetInstallKB // 获取Windows已安装更新,如:“KB4422”。
FunGetWindowsOs //获取Windows系统版本,如:“Microsoft Windows 10”。
4.2.2 漏洞规则:承担变化的数据
根据OVAL 其本身就是对评估对象的规则表示。我们对其进行转化,将其承载的数据使用lua脚本来承载。
Pattern是一个Lua脚本,分为两部分,第一部分定义漏洞检测的规则,第二部分实现扫描函数ScanFun,遍历检查所有的漏洞规则,将满足的规则做为结果输出。
VulRule = {
PatchId = "", # 补丁id
PatchUrl = "https://access.redhat.com/errata/", # 补丁url相对路径
PatchFull = "", # 补丁url全路径
CVEId = "", # 漏洞id,多个用“;”分割
CVEUrl = "https://access.redhat.com/security/cve/", # CVE相对路径
CVEUrlFull = "", # CVE全路径
description = "", # 描述信息
CVEType = VulType.OS, # 漏洞类型
criteria = { # criteria:中的规则是“或”操作,如果有多条规则,满足了其中一条,就代表含有对应的漏洞。
# 操作系统类型
OS = nil,
# Logic:中的规则是“与”操作,如果有多条规则,必须所有的规则都满足,表示含有对应的漏洞
# Type:定义的漏洞匹配查找的对应的信息类型
# filename :查找对应的文件名称信息
# Version :版本号
# Oper :操作类型
logic = {Type = nil, filename = nil, Version = nil, Oper = nil}
}
}
# 漏洞类型
VulType = {
OS = 1,
App = 2,
Web = 3,
db = 4
}
# 操作系统类型
# redhat pattern 定义
BaseOS = {
Rhel5 = 1,
Rhel6 = 2,
Rhel7 = 3,
Rhel8 = 4,
Rhel9 = 5,
}
# windows pattern 定义
BaseOS = {
Windows_xp = 1000,
Windows_7 = 1001,
Windows_Server_2003 = 1002,
Windows_Vista = 1003,
Windows_Server_2008 = 1004,
Windows_Server_2008_R2 = 1005,
Windows_Server_2012 = 1006,
Windows_Server_2012_R2 = 1007,
Windows_8_1 = 1008,
Windows_10 = 1009,
Windows_Server_2016 = 1010,
Windows_Server_2019 = 1011
}
# Type:定义的漏洞匹配查找的对应的信息类型
BaseType = {
kb = 1,//Windows补丁信息
dll = 2,//dll的版本信息
exe = 3,//exe的版本信息
sys = 4,//sys的版本信息
Windows_version = 5,//windows 的version信息
Windows_build = 6,//Windows的build的高版本信息
Windows_revison_number = 7 //windows的resion的number
}
# Oper :操作类型
BaseOper = {
lessthan = 1,//版本号早于
equalto = 2,//版本号等于
greaterthan = 3,//版本号大于
notinstalled = 4//未安装
}
Windows pattern demo
local CVE_2020_0774 = CVE_2020_0774_Rule:new{CVEId = "CVE-2020-0774",
description = "An information disclosure vulnerability exists when the Windows GDI component improperly discloses the contents of its memory..",
criteria = {
{
OS = BaseOS.Windows_7,
logic = {
{
Type = BaseType.dll,
filename = "gdi32.dll",
Version = "6.1.7601.24549",
Oper = BaseOper.lessthan
}
}
}, {
OS = BaseOS.Windows_Server_2008_R2,
logic = {
{
Type = BaseType.dll,
filename = "gdi32.dll",
Version = "6.1.7601.24549",
Oper = BaseOper.lessthan
}
}
},
Linux pattern demo
local RHBA_2020_3527 = RHBA_2020_3527_Rule:new{
PatchId = "RHBA-2020:3527",
CVEId = "CVE-2019-5108",
description = "The kernel-rt packages provide the Real Time Linux Kernel, which enables fine-tuning for systems with extremely high determinism requirements",
criteria = {
{
OS = BaseOS.Rhel7,
logic = {
{
Type = BaseType.kernel_running,
filename = "kernel",
Version = "3.10.0-1127.19.1.rt56.1116.el7",
Oper = BaseOper.lessthan
}, {
Type = BaseType.kernel_boot,
filename = "kernel",
Version = "3.10.0-1127.19.1.rt56.1116.el7",
Oper = BaseOper.lessthan
}
}
}, {
OS = BaseOS.Rhel7,
logic = {
{
Type = BaseType.linux_kernel,
filename = "kernel-rt",
Version = "3.10.0-1127.19.1.rt56.1116.el7",
Oper = BaseOper.lessthan
}
4.2.3 匹配逻辑
function VulRule:Scan()
-- ... ...
for _, tab in ipairs(self.criteria) do -- 执行"或"操作
if tab.OS == GetOsType() then
-- ... ...
for _, value in pairs(tab.logic) do
-- 执行"与"操作
end
-- ... ...
end
end
return false
end
function VulRule:createScanResult() return self.PatchId,self.PatchUrl,self.PatchFull,self.CVEId,self.CVEUrl,self.CVEUrlFull,self.description,self.CVEType end
local allrule = GetALLModule()
local result = {}
function ScanFun()
local result = "{\"result\":["
-- ... ...
---扫描所有规则
for _, v in pairs(allrule) do
if v:Scan() then
local PatchId,PatchUrl,PatchFull,CVEId,CVEUrl,CVEUrlFull,description,CVEType = v:createScanResult()
result = table.concat{result, "{\"Patchid\":\"", PatchId, "\",\"PatchUrl\":\"",PatchUrl,"\",\"PatchFull\":\"",PatchFull,"\",\"CveId\":\"",CVEId,"\",\"CVEUrl\":\"",CVEUrl,"\",\"CVEUrlFull\":\"",CVEUrlFull,"\",\"Type\":\"",CVEType,"\",\"desc\":\"", description, "\"}"}
-- ... ...
end
end
result = table.concat{result, "]}"}
return result
end
4.2.4 事件扫描结果说明
{
"result":[
{
"Patchid":"RHSA-2020:0374", // 补丁id
"PatchUrl":"https://access.redhat.com/errata/", // 补丁的url相对路径
"PatchFull":"", // 补丁的url全路径
"CveId":"CVE-2019-14816;CVE-2019-14895;CVE-2019-14898;CVE-2019-14901;CVE-2019-17133", //漏洞id,多个用“;”分割
"CVEUrl":"https://access.redhat.com/security/cve/", // CVE的url的相对路径
"CVEUrlFull":"", // CVE的url的全路径
"Type":"1", // 漏洞类型
"desc":"" // 描述信息
}
]
}
// 漏洞类型
VulType = {
OS = 1, //系统漏洞
App = 2, //应用漏洞
Web = 3, //web漏洞
db = 4 //数据库漏洞
}
5. 后面思考:怎么做成通用的lua规则引擎?应用到基线核查、弱口令识别等等。
上面说了那么多,其实最后总结一下:其实上面所说的"引擎"名字感觉挺牛逼的,实质是lua解释器 + lua脚本的封装。将lua换成 c+luajit,将lua脚本按照规则引擎的逻辑进行了呈现,并加密。当然他也满足规则引擎的要求:使用预定义的逻辑规则来解析和执行决策过程。