拆解规则七段结构并读懂动作与匹配范围

约 25 分钟

能逐段解释一条 Suricata 规则的动作、协议、地址、端口与方向,并写出语法正确的规则头。

一条 Suricata 规则就是一行指令:告诉引擎看什么流量,命中后怎么办。先看官方快速上手文档里的一条规则:

text
alert ip any any -> any any (msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; classtype:bad-unknown; sid:2100498; rev:7;)

这条规则来自 ET Open 规则集,专门用于验证检测:任何 IP 流量,只要载荷里出现 uid=0(root) 字样,就产生告警。先别急着读括号里的内容,只盯括号前面的部分。

括号前是规则头,由七段组成:动作、协议、源地址、源端口、方向、目的地址、目的端口。括号内是规则体,存放更细的匹配条件与元数据。

这一节只完成一件事:逐段读懂规则头,并写出语法正确的规则头。规则体里的关键字留给后续课程逐个展开。

核心概念

规则头七段:一封收件人齐全的明信片

把规则想成一张明信片。邮局要投递,必须先知道:寄件人是谁(源地址)、从哪个门牌寄出(源端口)、往哪个方向送(方向)、收件人是谁(目的地址)、送到哪个门牌(目的端口);还要知道这封信用什么语言写(协议),以及邮局见到它该怎么处理(动作)。七项信息缺一不可,否则信就投不出去。

技术定义:规则头按固定顺序排列七段——动作、协议、源地址、源端口、方向、目的地址、目的端口,其后紧跟括号括起的规则体。官方文档给出的规则格式即:

text
action protocol source_address source_port direction destination_address destination_port (options)

对照示例 alert ip any any -> any any:alert 是动作,ip 是协议,any any 依次是源地址与源端口,-> 是方向,后两个 any 是目的地址与目的端口。规则头决定匹配哪类连接,规则体决定在连接里找什么。也有资料把规则拆成动作、头部、选项三部分,本质是同一回事,记住七段顺序即可。

核心概念

动作:门卫如何处理可疑包裹

动作像门卫收到可疑包裹时的处理预案。同一个包裹,预案是“记下来”、是“拦下来”还是“放行”,结果完全不同。先想清楚你要引擎干什么,再决定动作。

技术定义:动作是规则头的第一个字段,决定流量命中规则后 Suricata 的处理方式:

  • alert:记录事件并产生告警,是检测场景最常用的动作,示例规则与规则集用的都是它。
  • drop:丢弃匹配的流量,用于需要阻断的部署。
  • pass:放行匹配的流量、不产生告警,常用于豁免。

文档把动作概括为记录、告警或阻断流量。只做检测用 alert,要阻断才用 drop,要豁免才用 pass。

核心概念

协议:只用一种语言读流量

同一句话,你只会中文,对方发来英文,你读不懂。协议段就是告诉 Suricata“只用这一种语言解读流量”,其它协议的流量直接跳过。

技术定义:协议是规则头的第二个字段,限定规则只作用于指定协议,如 tcp、udp、ip、icmp。示例 alert ip any any -> any any 中的 ip 表示匹配所有 IP 承载的流量。协议段决定引擎检查的范围:范围越窄,需要比对的流量越少。协议写错不会报错,但规则永远不会命中。

核心概念

地址与端口:圈定街区与门牌

地址决定信投到哪个街区,端口决定街区里的哪扇门。两者都写对,信才送到正确的人手里。

技术定义:源地址与目的地址支持四种写法:单个 IP(如 10.200.0.0)、CIDR 网段(如 192.168.0.0/16)、地址列表 [192.168.0.0/16,10.0.0.0/8] 和关键字 any。注意列表内不允许出现空格。端口支持 any、单个端口与区间写法,0:1024 表示 0 到 1024。

两个常用变量值得记住:HOME_NET 与 EXTERNAL_NET,它们在 suricata.yaml 中定义。默认 HOME_NET 覆盖 192.168.0.0/16、10.0.0.0/8、172.16.0.0/12 这些内网网段,EXTERNAL_NET 是这些网段之外的任何 IP。部署时应把被监控网卡的地址与所有本地网段写入 HOME_NET。规则里用变量代替具体 IP,换环境只改配置,规则不用动。

核心概念

方向:单行道还是双向通道

-> 是单行道,只许从左到右;<> 是双向通道,两边都能走。方向符永远只出现在源与目的之间。

技术定义:方向符控制匹配方向,只有两种写法:

  • ->:只匹配从左到右的连接。想匹配反方向,把箭头两侧的内容对调即可——不存在 <- 运算符。
  • <>:方向无关,两侧任一方向的流量命中都算匹配,适合无法确定谁先发起连接的情形。

示例 alert tcp $HOME_NET any -> $EXTERNAL_NET 80 只匹配内网发往外网 80 端口的 TCP 流量;若要同时覆盖外网回连内网的流量,把箭头改成 <>,或把两侧字段对调。

规则头圈定“哪类连接”,规则体回答“在连接里找什么、怎么处理”。规则体写在括号内,由若干选项组成,选项之间以分号结尾,例如 msg:"GPL ATTACK_RESPONSE id check returned root"; content:"uid=0|28|root|29|"; sid:2100498;。

本课不展开规则体。下一课会用 msg、sid、reference 建立可溯源的告警元数据;再往后,content 与 pcre 负责载荷匹配,flow 与 flowbits 负责连接状态关联。现在只需记住两点:括号内以分号分隔选项;先写出讲得清七段的规则头,再逐步添加选项,是更稳妥的顺序。

手写三条规则头并逐段自检

  1. 1

    新建一个文本文件,按七段顺序写下第一条规则头:动作 alert,协议 tcp,源地址与源端口 any any,方向 ->,目的地址与目的端口 $HOME_NET any,括号留空。

  2. 2

    逐段核对第一条:alert(动作)、tcp(协议)、any(源地址)、any(源端口)、->(方向)、$HOME_NET(目的地址)、any(目的端口)。

  3. 3

    写第二条:匹配任意地址、任意端口、任意协议,方向为双向。提示:协议段用 ip,方向用 <>。

  4. 4

    写第三条:只匹配“外网发往内网”的 TCP 流量,方向仍用单向箭头。

  5. 5

    合上笔记,默写七段顺序,再逐段说出每一段的作用。

你应该看到

得到三条规则头:alert tcp any any -> $HOME_NET any、alert ip any any <> any any、alert tcp $EXTERNAL_NET any -> $HOME_NET any。括号留空,下一课补上 msg 与 sid 后即可加载。每一条都能逐段解释七段含义。

遇到问题:写完规则后,Suricata 加载时提示规则解析错误;或者规则加载成功,但始终不告警。

可能原因

  • 括号、分号不配对,规则体残缺,引擎解析规则时直接报语法错误。
  • 地址列表内误加了空格,例如写成 [192.168.0.0/16, 10.0.0.0/8],列表写法不被接受。
  • 方向符写错,或写成了不存在的 <-。
  • 规则用到了 suricata.yaml 中未定义的变量;或配置引用的文件路径与实际不符,引擎按日志提示找不到文件。

排查步骤

  1. 对照官方 Rules Format 按顺序核对七段:动作、协议、源地址、源端口、方向、目的地址、目的端口。
  2. 检查地址列表:列表内不允许有空格。
  3. 方向只有 -> 与 <> 两种;要匹配反方向时对调两侧字段。
  4. 打开 suricata.log 看具体报错路径,逐一核实 suricata.yaml 中每个文件路径;论坛中的同类报错正是配置文件路径与实际文件位置不符。
  5. 先只用一条最简单的规则跑通加载,再逐步增加选项,缩小问题范围。
自检:用规则头语法写出一条规则:任意源地址与任意源端口发往 $HOME_NET 任意端口的 TCP 流量。再改写成同时匹配“内网发往外网”和“外网发回内网”两个方向的版本,并说明改动了哪一段、为什么。

单向版本:alert tcp any any -> $HOME_NET any,七段依次是动作 alert、协议 tcp、源地址 any、源端口 any、方向 ->、目的地址 $HOME_NET、目的端口 any。双向版本:alert tcp any any <> $HOME_NET any,只把方向段从 -> 换成 <>。<> 表示方向无关,两侧任一方向命中都算匹配;而 -> 只匹配从左到右。若坚持用 -> 匹配反向流量,则把两侧字段对调成 alert tcp $HOME_NET any -> any any。