• Elkeid 规则引擎——数据向后传递是亮点,支持单事件规则和统计类规则;如果向后传递支持的话,理论上AB先后事件的关联分析可以做;自定义plugin类似udf


    参考:https://elkeid.bytedance.com/docs/hub/handbook.html#6-elkeid-hub-ruleset

    检测规则在云端 hub里:如下图

    6 Elkeid HUB RuleSet

    RuleSet是HUB实现核心检测/响应动作的部分,需要根据具体业务需求来实现,下图是RuleSet在HUB中的简单工作流程:==》允许数据继续向后传递这个是亮点!!!

    4

    6.1 RuleSet

    HUB RuleSet是通过XML来描述的规则集

    RuleSet存在两种类型,rule 和 whitelist,如下:

     
    <root ruleset_id="test1" ruleset_name="test1" type="whitelist">
    ... ....
    </root>
    <root ruleset_id="test2" ruleset_name="test2" type="rule" undetected_discard="true">
    ... ...
    </root>
    

    其中RuleSet的是固定的模式,不可随意变更,其他属性:

    字段说明
    ruleset_id 不可重复,描述一个ruleset,只能由小写英文或"_/-"或数字组成
    ruleset_name 描述该ruleset的name,可以重复,可以使用中文
    type 为rule或者whitelist,其中rule代表着的检测到继续向后传递,whitelist则为检测到不向后传递向后传递的概念可以简单的理解为该ruleset的检出事件
    undetected_discard 仅在type为rule时有意义,意为未检测到是否丢弃,若为true,则未被该ruleset检测到则丢弃,若为false,则为未被该rulset检测到也继续向后传递

    6.2 Rule

    接下来我们来了解rule的具体语法,通常ruleset是由一个或多个rule组成的,需要注意的是多个rule之间的关系是'或'的关系,即如果一条数据可以命中其中的一条或多条rule。

     
    <root ruleset_id="test2" ruleset_name="test2" type="rule" undetected_discard="true">
        <rule rule_id="rule_xxx_1" author="xxx" type="Detection">
            ... ...
        </rule>
        <rule rule_id="rule_xxx_2" author="xxx" type="Frequency">
            ... ...
        </rule>
    </root>
    

    一个rule的基本属性有:

    字段说明
    rule_id 在同一个ruleset中不可重复,标识一个rule
    author 标识rule的作者
    type rule有两种类型,一种是Detection,是无状态的检测类型规则;另一种是Frequency,是在Detection的基础上对数据流进行频率的相关检测

    我们先来看两种不同类型规则的简单示例。

    我们先假设从Input传入到Ruleset的数据样例为:

     
    {
        "data_type":"59",
        "exe":"/usr/bin/nmap",
        "uid":"0"
    }
    
    6.2.1 Detection 简单例子
     
    <rule rule_id="detection_test_1" author="EBwill" type="Detection">
       <rule_name>detection_test_1</rule_name>
       <alert_data>True</alert_data>
       <harm_level>high</harm_level>
       <desc affected_target="test">这是一个Detection的测试</desc>
       <filter part="data_type">59</filter>
       <check_list>
           <check_node type="INCL" part="exe">nmap</check_node>
       </check_list>
    </rule>
    

    其中detection_test_1这个规则的意义是:当有数据的data_type为59,且exe中存在nmap的时候,数据继续向后传递。

    6.2.2 Frequency 简单例子
     
    <rule rule_id="frequency_test_1" author="EBwill" type="Frequency">
       <rule_name> frequency_test_1 </rule_name>
       <alert_data>True</alert_data>
       <harm_level>high</harm_level>
       <desc affected_target="test">这是一个Frequency的测试</desc>
       <filter part="data_type">59</filter>
       <check_list>
           <check_node type="INCL" part="exe">nmap</check_node>
       </check_list>
       <node_designate>
           <node part="uid"/>
       </node_designate>
       <threshold range="30" local_cache="true">10</threshold>
    </rule>
    

    其中frequency_test_1这个规则的意义是:当有数据的data_type为59,且exe中存在nmap的时候,进入到频率检测:当同一个uid,在30秒内出现了 >= 10以上行为则告警,且在这过程中使用当前HUB实例自身缓存。

    我们可以看到,实际上Frequency只是比Detection多了node_designate以及threshold字段,也就是说,无论什么规则,都会需有最基础的一些字段,我们接下来就先来了解这些通用的基础字段。

    6.2.3 通用字段描述
    字段说明
    rule_name 代表rule的名字,与rule_id不同的是可以使用中文或其他方式来更好的表达rule的含义,可以重复
    alert_data 为True或False,如果为True,则会将该rule的基础信息增加到当前的数据中向后传递;若为False,则不会将该rule的信息增加到当前的数据中
    harm_level 表达该rule的危险等级,可以为 info/low/medium/high/critical
    desc 用于提供这个rule本身的描述,其中affected_target是该rule针对的组件信息,用户自行填写,并无强制规定限制
    filter 对数据的第一层过滤,part表示对数据中的哪个字段进行过滤,具体内容为检测内容,义为part中是否存在 检测数据,如果RuleSet的类型为rule存在则继续向下执行rule的逻辑,如果不存在则不向下检测;RuleSet类型为whitelist时则相反,即存在则跳过检测,不存在则继续。 filter只能存在一个,且默认也仅支持“存在”的逻辑检测
    check_list check_list内可以存在0或多个check_node,一个rule只能存在一个check_list,其中check_node的逻辑为'且'即为'and',如果RuleSet的类型为rule则需要其中check_node全部通过才可以继续向下,如果为whitelist则相反,即其中check_node全部不通过才可以继续向下
    check_node check_node是一个具体的检测项
    6.2.4 alert_data

    我们依然以上面的Decetion的例子为例:

    待检测数据:

     
    {
        "data_type":"59",
        "exe":"/usr/bin/nmap",
        "uid":"0"
    }
    

    RuleSet:

     
    <root ruleset_id="test2" ruleset_name="test2" type="rule" undetected_discard="true">
    <rule rule_id="detection_test_1" author="EBwill" type="Detection">
       <rule_name>detection_test_1</rule_name>
       <alert_data>True</alert_data>
       <harm_level>high</harm_level>
       <desc affected_target="test">这是一个Detection的测试</desc>
       <filter part="data_type">59</filter>
       <check_list>
           <check_node type="INCL" part="exe">nmap</check_node>
       </check_list>
    </rule>
    </root>
    

    如果其alert_data为True,则该RuleSet会向后传递以下数据,会增加SMITH_ALERT_DATA字段,其中包括HIT_DATA用来描述命中规则的详情,以及RULE_INFO即规则本身的基本信息:==》这样是不是就可以做时序先后的关联了?

     
    {
        "SMITH_ALERT_DATA":{
            "HIT_DATA":[
                "test2 exe:[INCL]: nmap"
            ],
            "RULE_INFO":{
                "AffectedTarget":"all",
                "Author":"EBwill",
                "Desc":"这是一个Detection的测试",
                "DesignateNode":null,
                "FreqCountField":"",
                "FreqCountType":"",
                "FreqHitReset":false,
                "FreqRange":0,
                "HarmLevel":"high",
                "RuleID":"test2",
                "RuleName":"detection_test_1",
                "RuleType":"Detection",
                "Threshold":""
            }
        },
        "data_type":"59",
        "exe":"/usr/bin/nmap",
        "uid":"0"
    }
    

    alert_data为False,则会向后传递以下数据,即原始数据:

     
    {
        "data_type":"59",
        "exe":"/usr/bin/nmap",
        "uid":"0"
    }
    
    6.2.5 check_node

    check_node的基本结构如下:

     
    <check_node type="检测类型" part="待检测路径">
       检测内容
    </check_node>
    
    6.2.5.1 检测类型

    目前支持以下几种检测类型:

    类型说明
    END 待检测路径中的内容 以 检测内容 结尾
    START 待检测路径中的内容 以 检测内容 开头
    NEND 待检测路径中的内容 不以 检测内容 结尾
    NSTART 待检测路径中的内容 不以 检测内容 开头
    INCL 待检测路径中的内容 存在 检测内容
    NI 待检测路径中的内容 不存在 检测内容
    MT 待检测路径中的内容 大于 检测内容
    LT 待检测路径中的内容 小于 检测内容
    REGEX 对 待检测路径中的内容 进行 检测内容 的正则匹配
    ISNULL 待检测路径中的内容 为空
    NOTNULL 待检测路径中的内容 不为空
    EQU 待检测路径中的内容 等于 检测内容
    NEQ 待检测路径中的内容 不等于 检测内容
    CUSTOM 针对 待检测路径中的内容 进行 检测内容 指定的 自定义插件检测
    CUSTOM_ALLDATA 针对 待检测数据 进行 检测内容 指定的 自定义插件检测;该方式下part可以为空,因为不依赖该字段,是将整个数据传递到插件进行检测

    我们接下来说明下part的使用方法,该方法与filter的part使用方法一致。

    6.2.5.2 part

    假设待检测数据为:

     
    {
        "data":{
            "name":"EBwill",
            "number":100,
            "list":[
                "a1",
                "a2"
            ]
        },
        "class.id":"E-19"
    }
    

    对应的part描述方式如下:

     
    data               =       "{\"name\":\"EBwill\",\"number\":100,\"list\":[\"a1\",\"a2\"]
    data.name          =       "EBwill"
    data.number        =       "100"
    data.list.#_0      =       "a1"
    data.list.#_1      =       "a2"
    class\.id          =       "E-19"
    

    需要注意的是如果待检测key中存在"."需要用""转义

    6.2.5.3 高级用法之check_data_type

    假设待检测数据为

     
    {
        "stdin":"/dev/pts/1",
        "stdout":"/dev/pts/1"
    }
    

    假设我们需要检测 stdin 等于 stdout,即我们的检测内容来源于待检测数据,那么我们需要使用check_data_type="from_ori_data" 来重新定义检测内容的来源是来自于待检测数据而不是填写的内容,如下:

     
    <check_node type="EQU" part="stdin" check_data_type="from_ori_data">stdout</check_node>
    
    6.2.5.4 高级用法之logic_type

    假设待检测数据为

     
    {
        "data":"test1 test2 test3",
        "size": 96
    }
    

    当我们需要检测data中是否存在 test1 或 test2 的时候,我们可以写正则来实现,也可以通过定义logic_type来实现check_node支持"AND"或者"OR"逻辑,如下:

     
    <!-- data中存在test1 或 test2 -->
    <check_node type="INCL" part="data" logic_type="or" separator="|">
        <![CDATA[test1|test2]]>
    </check_node>
    <!-- data中存在test1 和 test2 -->
    <check_node type="INCL" part="data" logic_type="and" separator="|">
        <![CDATA[test1|test2]]>
    </check_node>
    

    其中logic_type用来描述逻辑类型,支持"and"和"or",separator用于自定义标识切割检测数据的方式

    6.2.5.5 高级用法之foreach

    当我们需要对数组有较复杂的检测时可能可以通过foreach来解决。

    假设待检测数据为

     
    {
        "data_type":"12",
        "data":[
            {
                "name":"a",
                "id":"14"
            },
            {
                "name":"b",
                "id":"98"
            },
            {
                "name":"c",
                "id":"176"
            },
            {
                "name":"d",
                "id":"172"
            }
        ]
    }
    

    我们想将id > 100且顶层的data_type等于12的数据的obj筛选出来那么可以先通过foreach进行遍历,然后再对遍历后的数据进行判断,如下:

     
    <check_list foreach="d in data"> ==》这不就是python web模板的写法嘛
        <check_node type="MT" part="d.id">100</check_node>
        <check_node type="EQU" part="data_type">12</check_node>
    </check_list>
    

    则会向后传递多条数据:

     
    {
        "data_type":"12",
        "data":[
            {
                "name":"c",
                "id":"176"
            }
        ]
    }
    
    以及
    
    {
        "data_type":"12",
        "data":[
            {
                "name":"d",
                "id":"172"
            }
        ]
    }
    

    我们通过下图来更好的理解foreach这个高级用法

    5

    假设待检测数据为

     
    {
        "data_type":"12",
        "data":[
            1,
            2,
            3,
            4,
            5,
            6,
            7
        ]
    }
    

    我们想筛选出data中小于5的数据,那么需要这样编写:

     
    <check_list foreach="d in data">
        <check_node type="LT" part="d">5</check_node>
    </check_list>
    
    6.2.6 Frequency 字段

    Frequency的逻辑在check_list之后,但数据通过了filter和check_list之后,如果当前rule的类型为Frequency则会进入到Frequency的特殊检测逻辑。Frequency存在两个字段,node_designatethreshold,如下:

     
    <node_designate>
        <node part="uid"/>
        <node part="pid"/>
    </node_designate>
    <threshold range="30">5</threshold>
    
    6.2.6.1 node_designate

    其中node_designate是代表group_by,上方样例的含义是对 uid 和 pid 这两个字段进行group_by。

    6.2.6.2 threshold

    threshold是描述频率检测的具体检测内容:多长时间内(range)出现多少次(threshold)。如上样例中即表达:同一uid与pid在30秒内出现5次即为检出,其中range的单位是秒。

    6

    如上图,由于仅仅在10s内就出现了5次,那么在剩下的20s内出现的全部pid=10且uid=1的数据都会告警,如下:

    7

    但是这个问题可能会导致告警数据过多,因此支持一个叫做:hit_reset的参数,使用方式如下:

     
    <node_designate>
        <node part="uid"/>
        <node part="pid"/>
    </node_designate>
    <threshold range="30" hit_reset="true" >5</threshold>
    

    当hit_reset为true时,每次满足threshold策略后,时间窗口将会重制,如下:

    8

    6.2.6.3 高级用法之count_type

    有些情况下我们计算频率不是想计算“出现了多少次”,而会有一些其他的需求,如出现了多少类,**出现的字段内数据和是多少。**我们先来看第一个需求,出现了多少类。

    假设待检测数据为

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22"
    }
    

    当我们想写一个检测扫描器的规则时,我们其实往往不关心某一个IP访问了多少次其他资产,而是访问了多少不同的其他资产,当这个数据较大时,可能存在网络扫描探测的可能性,假设我们规定,3600秒内,同一IP访问的不同IP数超过100种就记录下来,那么他的频率部分规则应该这样编写:

     
    <node_designate>
        <node part="sip"/>
    </node_designate>
    <threshold range="3600" count_type="classify" count_field="dip">100</threshold>
    

    这时候count_type需要为classifycount_field则为类型计算依赖的字段,即dip。

    第二个场景假设待检测数据为

     
    {
        "sip":"10.1.1.1",
        "qps":1
    }
    

    假设我们需要筛选出3600s内qps总和大于1000的数据,那么我们可以这样编写:

     
    <node_designate>
        <node part="sip"/>
    </node_designate>
    <threshold range="3600" count_type="sum" count_field="qps">1000</threshold>
    

    count_type为空时默认计算次数,当为classify时计算的是类型,为sum时计算的是求和

    6.2.7 append

    当我们想对数据进行一些增加信息的操作时,可以使用append来进行添加数据的操作,append的语法如下:

     
    <append type="append类型" rely_part="依赖字段" append_field_name="增加字段名称">增加内容</append>
    
    6.2.7.1 append之STATIC

    假设待检测数据为

     
    {
        "alert_data":"data"
    }
    

    假设该数据已经通过了filter/check_list/频率检测(若有),这时候我们想增加一些固定的数据到该数据中,如:data_type:10,那么我们可以通过以下方式增加:

     
    <append type="static" append_field_name="data_type">10</append>
    

    我们将会得到以下数据:

     
    {
        "alert_data":"data",
        "data_type":"10"
    }
    
    6.2.7.2 append之FIELD

    假设待检测数据为

     
    {
        "alert_data":"data",
        "data_type":"10"
    }
    

    如果我们想对该数据增加一个字段:data_type_copy:10(来源于数据中的data_type字段),那么我们可以按以下方式编写:

     
    <append type="field" rely_part="data_type" append_field_name="data_type_copy"></append>
    
    6.2.7.3 append之CUSTOM

    假设待检测数据为

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22"
    }
    

    如果我们想通过外部API查询sip的CMDB信息,那我们在这种场景下无法通过简单的规则来实现,需要借助Plugin来实现,Plugin的具体编写方式将在下文进行说明,在这里我们先介绍如果在RuleSet中调用自定义Plugin,如下:

     
    <append type="CUSTOM" rely_part="sip" append_field_name="cmdb_info">AddCMDBInfo</append>
    

    在这里我们将会把rely_part中字段的数据传递到AddCMDBInfo插件进行数据查询,并将插件返回数据append到cmdb_info数据中,如下:

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22",
        "cmdb_info": AddCMDBInfo(sip) --> cmdb_info中的数据为插件AddCMDBInfo(sip)的返回数据
    }
    
    6.2.7.4 append之CUSTOM_ALLORI

    假设待检测数据为

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22"
    }
    

    如果我们想通过内部权限系统的API查询sip与dip的权限关系,那此时也是需要通过插件来实现这一查询,但是我们的该插件的入参不唯一,我们需要将待检测数据完整的传入该插件,编写方式如下:

     
    <append type="CUSTOM_ALLORI" append_field_name="CONNECT_INFO">AddConnectInfo</append>
    

    我们可以得到:

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22",
        "CONNECT_INFO": AddConnectInfo({"sip":"10.1.1.1","sport":"6637","dip":"10.2.2.2","dport":"22"}) --> CONNECT_INFO中的数据为插件AddConnectInfo的返回数据
    }
    
    6.2.7.5 其他

    append可以在一条rule中存在多个,如下:

     
    <rule rule_id="rule_1" type="Detection" author="EBwill">
        ...
        <append type="CUSTOM_ALLORI" append_field_name="CONNECT_INFO">AddConnectInfo</append>
        <append type="field" rely_part="data_type"></append>
        <append type="static" append_field_name="data_type">10</append>
        ...
    </rule>
    
    6.2.8 del

    当我们需要对数据进行一些裁剪的时候,可以使用del字段进行操作。

    假设待检测数据为

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22",
        "CONNECT_INFO": "false"
    }
    

    假设我们需要将字段CONNECT_INFO移除,那我按如下方式编写即可:

     
    <del>CONNECT_INFO</del>
    

    可以得到如下数据:

     
    {
        "sip":"10.1.1.1",
        "sport":"6637",
        "dip":"10.2.2.2",
        "dport":"22"
    }
    

    del可以编写多个,需要用";"隔开,如下:

     
    <del>CONNECT_INFO;sport;dport</del>
    

    即可得到如下数据:

     
    {
        "sip":"10.1.1.1",
        "dip":"10.2.2.2"
    }
    
    6.2.9 modify

    当我们需要对数据进行复杂处理时,通过append和del无法满足需求,如对数据进行拍平操作,对数据的key进行变化等,这时候可以使用modify来进行操作,需注意modify仅支持插件,使用方式如下:

     
    <modify>插件名称</modify>
    

    流程如下图:

    9

    6.2.10 Action

    当我们需要做一些特殊操作,如联动其他系统,发送告警到钉钉/Lark/邮件,联动WAF封禁IP等操作的时候,我们可以通过Action来实现相关的操作,需注意仅支持插件,使用方式如下:

     
    <action>emailtosec</action>
    

    插件emailtosec的入参会是当前的数据,其他的操作可以按需求编写。

    action也是支持多个插件,使用方式如下:

     
    <action>emailtosec1</action>
    <action>emailtosec2</action>
    

    在上面的例子中emailtosec1与emailtosec2都会被触发运行。

    action插件也支持自定义入参,具体使用方法请参考7.2.1

    6.3 检测/执行顺序

    10

    需要注意的是数据在通过Rule的过程中是动态的,即如果通过了append那么接下来如果是del那么del接收到的数据是append生效后的数据。

    6.3.1 Rule之间的关系

    同一RuleSet中的Rule为"OR"的关系,假设RuleSet如下:

     
    <root ruleset_id="test2" ruleset_name="test2" type="rule" undetected_discard="true">
    <rule rule_id="detection_test_1" author="EBwill" type="Detection">
       <rule_name>detection_test_1</rule_name>
       <alert_data>True</alert_data>
       <harm_level>high</harm_level>
       <desc affected_target="test">这是一个Detection的测试1</desc>
       <filter part="data_type">59</filter>
       <check_list>
           <check_node type="INCL" part="exe">redis</check_node>
       </check_list>
    </rule>
    <rule rule_id="detection_test_2" author="EBwill" type="Detection">
       <rule_name>detection_test_2</rule_name>
       <alert_data>True</alert_data>
       <harm_level>high</harm_level>
       <desc affected_target="test">这是一个Detection的测试2</desc>
       <filter part="data_type">59</filter>
       <check_list>
           <check_node type="INCL" part="exe">mysql</check_node>
       </check_list>
    </rule>
    </root>
    

    假设数据的exe字段为 mysql-redis,那么会detection_test_1于detection_test_2都会被触发且会产生两条数据向后传递,分别隶属这两条规则

    6.4 更多的例子

     
    <rule rule_id="critical_reverse_shell_rlang_black" author="lez" type="Detection">
        <rule_name>critical_reverse_shell_rlang_black</rule_name>
        <alert_data>True</alert_data>
        <harm_level>high</harm_level>
        <desc kill_chain_id="critical" affected_target="host_process">可能存在创建 R 反弹shell的行为</desc>
        <filter part="data_type">42</filter>
        <check_list>
            <check_node type="INCL" part="exe">
                <![CDATA[exec/R]]>
            </check_node>
            <check_node type="REGEX" part="argv">
                <![CDATA[(?:\bsystem\b|\bshell\b|readLines.*pipe.*readLines|readLines.*writeLines)]]>
            </check_node>
        </check_list>
        <node_designate>
        </node_designate>
        <del />
        <action />
        <append append_field_name="" rely_part="" type="none" />
    </rule>
    <rule rule_id="init_attack_network_tools_freq_black" author="lez" type="Frequency">
        <rule_name>init_attack_network_tools_freq_black</rule_name>
        <freq_black_data>True</freq_black_data>
        <harm_level>medium</harm_level>
        <desc kill_chain_id="init_attack" affected_target="service">存在多次使用网络攻击工具的行为,可能存在中间人/网络欺骗</desc>
        <filter part="SMITH_ALERT_DATA.RULE_INFO.RuleID">init_attack_network</filter>
        <check_list>
        </check_list>
        <node_designate>
            <node part="agent_id" />
            <node part="pgid" />
        </node_designate>
        <threshold range="30" local_cache="true" count_type="classify" count_field="argv">3</threshold>
        <del />
        <action />
        <append append_field_name="" rely_part="" type="none" />
    </rule>
    <rule rule_id="tip_add_info_01" type="Detection" author="yaopengbo">
        <rule_name>tip_add_info_01</rule_name>
        <harm_level>info</harm_level>
        <threshold/>
        <node_designate/>
        <filter part="data_type">601</filter>
        <check_list>
            <check_node part="query" type="CUSTOM">NotLocalDomain</check_node>
        </check_list>
        <alert_data>False</alert_data>
        <append type="FIELD" rely_part="query" append_field_name="tip_data"></append>
        <append type="static" append_field_name="tip_type">3</append>
        <append type="CUSTOM_ALLORI" append_field_name="tip_info">AddTipInfo</append>
        <del/>
        <action/>
        <desc affected_target="tip">dns新增域名tip检测信息</desc>
    </rule>
    

    6.5 规则编写建议

    • filter的良好运用可以大大降低性能压力,filter的编写目标应该是让尽可能少的数据进入CheckList
    • 尽可能少的使用正则

    7 Elkeid HUB Plugin

    Elkeid HUB Plugin用于解除部分Ruleset在编写过程的限制,提高HUB使用的灵活性。通过编写plugin,可以实现部分编写Ruleset无法完成的操作。同时,如果需要和当前尚不支持的第三方组件进行交互,只能通过编写plugin来实现。Elkeid HUB目前仅支持Python Plugin。

    Python Plugin本质是在HUB运行过程中,通过另外一个进程执行Python 脚本,并将执行结果返回给Elkeid HUB。

    Plugin总计有6种类型,均在Ruleset编写文档中介绍过,以下会结合上文的例子对每种plugin展开介绍。 Plugin的类型命名与在Ruleset中的标签名并不一一对应,实际使用中请严格以文档为准。

    7.1 通用参数介绍

    7.1.1 格式

    每个Plugin都是一个Python Class,plugin加载时,HUB会实例化这个Class,并对该Class的name,type,log,redis四个变量进行赋值,每次plugin执行时,会调用该class的plugin_exec方法。

    Class 如下:

     
    class Plugin(object):
        def __init__(self):
            self.name = ''
            self.type = ''
            self.log = None
            self.redis = None
        
        def plugin_exec(self, arg, config):
            pass
    
    7.1.2 init

    __init__方法中包含以下四个变量:

    • name: Plugin Name
    • type: Plugin Type
    • log: logging
    • redis: redis client

    如果有自己的init逻辑,可以加在后面

    7.1.3 plugin_exec

    plugin_exec方面有两个参数,arg和config。

    • arg就是该plugin执行时接受的参数。根据plugin类型的不同,arg是string或dict()。

    针对Action,Modify,Origin,OriginAppend四种类型的plugin,arg是dict()。

    针对Append,Custom两种类型的plugin,arg是string。

    • config是plugin可以接受的额外参数,目前只有Action和Modify支持,如果在Ruleset中有指定,会通过config参数传递给plugin。

    例如:在ruleset中添加了extra标签,HUB会以dict的形式以config入参调用plugin_exec方法。

     
    <action extra="mail:xxx@bytedance.com">PushMsgToMatao</action>
    config = {"mail":"xxx@bytedance.com"}
    

    7.2 example

    7.2.1 Plugin之Action

    在rule中的作用见6.2.10。

    Action用于实现数据通过当前rule之后执行一些额外操作。

    一个Action plugin接收的是整个数据流的拷贝。返回的是action是否执行成功。action是否执行成功不会影响数据流是否继续向下走,只会在HUB的日志中体现。

    实现参考

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
            
        def plugin_exec(self, arg, config):
            # 例:请求某个回调地址
            requests.post(xxx)
            result = dict()
            result["done"] = True
            return result
    
    7.2.2 Plugin之Append

    在rule中的作用见6.2.7.3

    Append和OriginAppend类似,均是可以自定义Append操作,不同的是Append接受的数据流中确定的某个属性值,而OriginAppend接受的是整个数据流的拷贝。两者的返回值均会写入到数据流中指定属性中。

    实现参考

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
    
        def plugin_exec(self, arg, config):
            result = dict()
            result["flag"] = True
            # 在原arg后面加上__new__后缀
            result["msg"] = arg + "__new__"
            return result
    
    7.2.3 Plugin之Custom

    在rule中的作用见6.2.5.2中的Custom

    CUSTOM用于实现自定义CheckNode。CheckNode中虽然预定义了10余种常见的判断方式,但在实际rule编写过程中,必然无法完全覆盖,所以开放了plugin拥有书写更灵活的判断逻辑。

    该plugin接收的参数是数据流中指定的属性值,返回的是是否命中以及写入hit中部分。

    实现参考

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
    
        def plugin_exec(self, arg, config):
            result = dict()
            # 若arg长度为10
            if arg.length() == 10:
                result["flag"] = True
                result["msg"] = arg
            else:
                result["flag"] = True
                result["msg"] = arg
            return result
    
    7.2.4 Plugin之Modify

    在rule中的作用见6.2.9

    Modify是所有plugin中灵活度最高的plugin,当编写ruleset或其他plugin无法满足需求时,可以使用modify plugin,获得对数据流的完全操作能力。

    Modify插件的入参是当前数据流中的一条数据。返回分两种情况,可以返回单条数据,也可以返回多条数据。

    返回单条数据时,Flag为true,数据在Msg中,返回多条数据时,MultipleDataFlag为true,数据在数组MultipleData中。若Flag和MultipleDataFlag同时为true,则无意义。

    实现参考1:

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
    
        def plugin_exec(self, arg, config):
            result = dict()
            # 随意修改数据,例如加个字段
            arg["x.y"] = ["y.z"]
            result["flag"] = True
            result["msg"] = arg
            return result
    

    实现参考2:

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
    
        def plugin_exec(self, arg, config):
            result = dict()
            # 将该条数据复制成5分
            args = []
            args.append(arg)
            args.append(arg)
            args.append(arg)
            args.append(arg)
            args.append(arg)
            result["multiple_data_flag"] = True
            result["multiple_data"] = args
            return result
    
    7.2.5 Plugin之Origin

    在rule中的作用见6.2.5.2中的CUSTOM_ALLORI

    Custom插件的进阶版,不再是对数据流中的某个字段进行check,而是对数据流中的整条数据进行check。入参由单个字段变成了整个数据流。

    实现参考

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
    
        def plugin_exec(self, arg, config):
            result = dict()
            # 若arg["a"]和arg["b"]长度长度都为10
            if arg["a"].length() == 10 and arg["b"].length() == 10:
                result["flag"] = True
                result["msg"] = arg
            else:
                result["flag"] = True
                result["msg"] = arg
            return result
    
    7.2.6 Plugin之OriginAppend

    在rule中的作用见6.2.7.4

    Append插件的进阶版,不再是对数据流中的某个字段进行判断然后append,而是对数据流中的整条数据进行判断。入参由单个字段变成了整个数据流。

    实现参考

     
    class Plugin(object):
        def __init__(self):
            self.name = None
            self.type = None
            self.log = None
            self.redis = None
            
        def plugin_exec(self, arg, config):
            result = dict()
            result["flag"] = True
            # 合并两个字段
            result["msg"] = arg["a"] + "__" + arg["b"]
            return result
    

    7.3 Plugin 开发流程

    plugin的运行环境为pypy3.7-v7.3.5-linux64,如果希望python脚本正常运行,需要在此环境下进行测试。

    HUB自身引入了部分基础依赖,但远无法覆盖python常用package,当有需要时,需要用户通过如下方式自行安装。

    1. venv位于py/pypy下,可以通过以下命令切换到venv中,执行pip install进行安装。
     
    source py/pypy/bin/activate
    
    1. 在plugin的init方法中调用pip module,进行安装
    7.3.1 创建Plugin

    plugin存放在在运行配置(config目录)下的plugin文件夹中,一个plugin对应目录下的一个文件夹。我们提供了一些示例plugin,包含Action,Modify,Append操作,同时提供了一份空白模板。

    创建插件只需要复制一份空白模板并以你想要的plugin名命名,同时修改elkeid.txt文件

     
    [plugin]
    name = SendToLarkGroup              #插件名
    type = Action                       #插件类型
    description = use bot send lark     #插件描述  
    runtime = Python
    author = lichanghao_orange          #插件作者
    
    7.3.2 编写插件内容

    可以根据前面提供的示例以及内置的插件例子进行编写,实现自定义功能

    7.3.3 使用该插件

    在规则中对应的地方填写该插件的插件名即可使用,如果出现报错会显示在HUB的log中

    7.4 示例插件说明

    在社区版中我们提供了几个示例插件用于教学和简单实用,目前包含推送消息到飞书群插件、推送消息到钉钉群插件、根据IP反查域名插件、自定义Check插件。这些插件经过简单修改后均可以直接使用,如下是使用说明。

    7.4.1 推送消息到飞书群插件
    • 插件名:SendToLarkGroup
    • 使用方法:获取飞书机器人的app_id、app_secret修改对应位置即可(在注释中可以找到)。在调用时使用extra传参将群聊id传入即可使用。
     
      <action extra="id:qunid">SendToLark</action>
    
    7.4.2 推送消息到钉钉群插件
    • 插件名:SendToDingding
    • 使用方法:在钉钉群中添加机器人并获取机器人的token以及两步认证的secret(在群聊的机器人设置中可以找到)。
    7.4.3 推送消息到TG群插件
    • 插件名:SendToTelegram
    • 使用方法:在TG中创建好bot后,获取token,在bot设置中关闭Group Privacy。将bot添加到群中,将群id和token写进插件代码中即可。
    7.4.4 推送消息到企业微信插件
    • 插件名:SendToWeCom
    • 使用方法:在企业微信中添加机器人并获取机器人的webhook链接,将该链接填写在插件内即可。
    7.4.5 根据IP反查域名插件
    • 插件名:DNSptr
    • 使用方法:读取字段ip,如果可以找到对应的域名,会在新增的字段“ptr”中输出域名并在字段“dns”中输出dns服务器地址,如果没查到,会在新增的字段“ptr”中输出“null”。
  • 相关阅读:
    关于随机数生成
    全文搜索基本原理(倒排索引、搜索结果排序)
    Log-Structured Merge Tree (LSM Tree)
    Spring Cloud组件使用/配置小记
    容错框架之Hystrix小记
    (转)调试程序时设置断点的原理
    字符串匹配算法
    信息论小记
    Java 函数式编程(Lambda表达式)与Stream API
    (转)自动控制的故事
  • 原文地址:https://www.cnblogs.com/bonelee/p/16349330.html
Copyright © 2020-2023  润新知