ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ZeroTier 规则编译器(Rule Compiler)实战指南:从可读规则脚本到网络控制器规则

ZeroTier 规则编译器(Rule Compiler)实战指南:从可读规则脚本到网络控制器规则 ZeroTier 规则编译器Rule Compiler实战指南从可读规则脚本到网络控制器规则【免费下载链接】ZeroTierOneA Smart Ethernet Switch for Earth项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTierOne本篇指南围绕 ZeroTierOne 仓库中的 rule-compiler/README.md 展开系统讲解如何用rule-compiler将人类可读的规则脚本编译为可供 ZeroTier 网络控制器导入的低层规则 JSON。读完本文你将掌握node cli.js rules script的完整用法、规则脚本语法动作、匹配、宏、标签、能力、编译输出的 JSON 结构以及编译器的三遍解析原理和常见错误处理可以直接用仓库自带的示例编写并编译自己的网络准入规则。一、rule-compiler 是什么ZeroTier 的网络流量控制基于规则引擎rules engine实现控制器侧需要的是由ZT_NETWORK_RULE_*枚举组成的低层规则数组而人类直接手写这种数组既不直观也容易出错。rule-compiler正是为了解决这一矛盾而存在它把人类可读格式的规则脚本转换成适合导入 ZeroTier 网络控制器的规则它是 ZeroTier Central 网页控制台规则编辑器rules editor背后实际使用的编译脚本同时提供一个命令行接口可本地离线使用。在仓库中该模块位于 rule-compiler/ 目录核心文件包括文件作用rule-compiler/rule-compiler.js核心编译器实现导出compile()函数rule-compiler/cli.js命令行入口读取脚本文件并输出 JSONrule-compiler/examples/capabilities-and-tags.ztrules官方示例规则脚本rule-compiler/package.jsonnpm 包元信息名称zerotier-rule-compiler版本 1.2.2-2主入口为cli.js关于规则引擎本身与规则脚本语法的更完整定义官方文档指向 ZeroTier 手册manual本文则以仓库源码为准补充实现层面的细节。二、快速上手命令行用法根据 rule-compiler/README.md命令行调用方式为node cli.js rules script其中rules script是规则脚本文件的路径。例如编译仓库自带的示例node cli.js examples/capabilities-and-tags.ztrules2.1 成功时的输出从 rule-compiler/cli.js 的源码第 36-44 行可以看到成功时向标准输出打印一个带缩进的 JSON 对象包含三部分{ config: { rules: [...], capabilities: [...], tags: [...] }, capabilitiesByName: {...}, tagsByName: {...} }config.rules编译后的文档级规则数组每条是type如ACTION_DROP、MATCH_ETHERTYPE加相关字段的对象config.capabilities能力数组只含id与编译后的规则config.tags标签数组只含id与default值源码第 30-34 行在输出时剥离了enums/flags等编译期辅助信息capabilitiesByName/tagsByName以名字为键的完整映射tagsByName含enums、flags等完整定义便于控制器侧按名字查找。2.2 出错时的输出若脚本存在语法或语义错误cli.js 第 16-18 行会在标准错误输出错误信息并返回退出码 1ERROR parsing rules script line 行号 column 列号: 错误描述退出码为 1 时表示编译失败适合在 CI 或脚本中做断言。三、规则脚本语法基础编译器采用类似缩进块的语法。源码中的第一遍解析rule-compiler/rule-compiler.js 第 785 行起的compile()函数揭示了以下语法规则注释#开头直到行尾的内容被忽略块结构macro、tag、cap、drop、accept、tee、watch、redirect、break、priority是开启新块的块关键字见源码第 67-78 行的OPEN_BLOCK_KEYWORDS分号以;结束一个块块内容通过缩进嵌套标记name;这样单独一行加;的动作关键字表示无条件动作如accept;大小写关键字大小写不敏感解析时统一toLowerCase()名字中的标识符则需符合命名规则。3.1 命名规则与保留字_isValidName()源码第 208-216 行规定tag、capability、macro 的名字必须以非数字字符开头且只允许下划线与 Unicode 字母数字字符。源码第 81-135 行的RESERVED_WORDS列出全部保留字不可用作名字包括结构关键字macro、tag、cap、default动作关键字drop、accept、tee、watch、redirect、break、priority匹配关键字ztsrc、ztdest、vlan、vlanpcp、vlandei、ethertype、macsrc、macdest、ipsrc、ipdest、iptos、ipprotocol、icmp、sport、dport、chr、framesize、random、tand、tor、txor、tdiff、teq、tseq、treq其他语言关键字type、enum、class、define、import、include、log、not、xor、or、and、set、var、let等。3.2 数字与地址写法数字支持十进制与0x十六进制前缀_parseNum()源码第 226-236 行MAC 地址_cleanMac()第 238-254 行会把各种分隔形式统一为aa:bb:cc:dd:ee:ff的标准 17 字符格式否则报 Invalid MAC addressZeroTier 地址10 个十六进制字符如deadbeef01IP 地址ipsrc/ipdest必须带/bits前缀长度编译器用IPV6_REGEX/IPV4_REGEX自动区分 IPv4 与 IPv6并分别产出MATCH_IPV4_SOURCE/MATCH_IPV4_DEST或MATCH_IPV6_SOURCE/MATCH_IPV6_DEST类型的规则源码第 410-446 行。四、动作Actions动作关键字及其对应的低层规则类型见源码第 137-144 行的KEYWORD_TO_API_MAP脚本关键字低层规则类型说明dropACTION_DROP丢弃匹配的帧acceptACTION_ACCEPT放行匹配的帧teeACTION_TEE复制一份转发到指定节点带长度上限watchACTION_WATCH与 tee 类似用于观测redirectACTION_REDIRECT将帧重定向到指定节点breakACTION_BREAK停止后续规则评估priorityACTION_PRIORITY设置优先级4.1 无条件动作drop;、accept;、break;这种动作后直接分号的写法表示无条件动作_renderActions()第 671-681 行。注意规则按自上而下的顺序评估先匹配到的动作决定帧的命运。4.2 带目标地址的动作tee与watch需要两个参数最大转发长度0 表示全部与目标 ZeroTier 地址10 位十六进制例如tee 1500 deadbeef01;源码第 682-725 行要求长度在-1到0xffff之间目标地址必须恰好 10 个字符否则报错 Tee/watch max packet length to forward invalid or out of range 或 Missing or invalid ZeroTier address target for tee/watch。redirect只需要一个目标地址参数源码第 726-760 行redirect deadbeef01;五、匹配Matches匹配条件用于限定动作的作用范围。每个匹配关键字有固定参数个数定义在MATCH_ARG_COUNTS源码第 174-200 行整理如下匹配关键字参数个数匹配内容低层规则类型ztsrc/ztdest1源/目标 ZeroTier 地址MATCH_SOURCE_ZEROTIER_ADDRESS/MATCH_DEST_ZEROTIER_ADDRESSvlan1VLAN IDMATCH_VLAN_IDvlanpcp1VLAN PCP 优先级MATCH_VLAN_PCPvlandei1VLAN DEI 位MATCH_VLAN_DEIethertype1以太网类型MATCH_ETHERTYPEmacsrc/macdest1MAC 源/目的地址MATCH_MAC_SOURCE/MATCH_MAC_DESTipsrc/ipdest1IP 源/目的网段CIDRMATCH_IPV4_SOURCE/MATCH_IPV4_DEST或 IPv6 变体iptos2IP TOS 掩码与值/值范围MATCH_IP_TOSipprotocol1IP 协议号MATCH_IP_PROTOCOLicmp2ICMP 类型与代码MATCH_ICMPsport/dport1源/目的端口或端口范围MATCH_IP_SOURCE_PORT_RANGE/MATCH_IP_DEST_PORT_RANGEchr1帧特性位MATCH_CHARACTERISTICSframesize1帧大小或大小范围MATCH_FRAME_SIZE_RANGErandom1随机概率0.01.0MATCH_RANDOMtand/tor/txor2标签按位与/或/异或MATCH_TAGS_BITWISE_AND/_OR/_XORtdiff2标签差值MATCH_TAGS_DIFFERENCEteq2标签相等MATCH_TAGS_EQUALtseq/treq2发送方/接收方标签MATCH_TAG_SENDER/MATCH_TAG_RECEIVER5.1 组合逻辑not、or与默认 AND多个匹配条件写在同一动作块内时默认按AND组合not前缀取反or表示与后续条件为或关系见_renderMatches()第 266-278 行的处理逻辑。5.2 简写名称以太网类型简写ETHERTYPES源码第 33-43 行ipv40x0800、arp0x0806、wol0x0842、rarp0x8035、ipv60x86dd、atalk0x809b、aarp0x80f3、ipx_a0x8137、ipx_b0x8138也可直接写十六进制0x0800IP 协议简写IP_PROTOCOLS源码第 46-64 行icmp/icmp4/icmpv40x01、igmp0x02、ipip0x04、tcp0x06、egp0x08、igp0x09、udp0x11、rdp0x1b、esp0x32、ah0x33、icmp6/icmpv60x3a、l2tp0x73、sctp0x84、udplite0x88同样可直接写数字特性位名称CHARACTERISTIC_BITS源码第 12-30 行inbound63、multicast62、broadcast61、ipauth60、macauth59以及 TCP 标志位tcp_fin0、tcp_syn1、tcp_rst2、tcp_psh3、tcp_ack4、tcp_urg5、tcp_ece6、tcp_cwr7、tcp_ns8、tcp_rs29、tcp_rs110、tcp_rs011。chr可以写多个名称或位索引用逗号分隔。5.3 区间与概率sport/dport/framesize支持start-end区间写法如dport 1000-2000解析在源码第 470-499 行参数值必须在 00xffff 且区间起点不大于终点iptos的参数形式为iptos mask value其中值也可以写区间第 501-537 行random接受 0.01.0 的浮点概率越界会被钳制内部转换为 32 位整数概率第 381-393 行Math.floor(4294967295 * num)icmp的两个参数是类型0255与代码代码写-1表示不匹配代码第 448-468 行。5.4 标签类匹配标签匹配的第二个参数既可以是数字也可以是标签定义中的flag 或 enum 名字编译器会查表替换见第 578-611 行。例如teq department engineering等价于teq department 400假设engineering在示例中映射为 400。六、宏Macros与 include 指令宏用于复用规则片段可带参数。定义语法源码第 868-910 行macro name(param1,param2) 动作/匹配块 ;在规则块中使用include 宏名(参数列表);展开宏_renderActions()第 628-670 行。宏参数在脚本中通过$参数名引用编译时替换为实际传入的值_renderMatches()第 306-313 行处理$前缀的变量引用引用未定义变量会报 Undefined variable name.。宏缺少必选参数时报 Missing one or more required macro parameter.找不到宏时报 Macro name not found.。七、标签Tags标签在成员间传递数值/枚举/标志信息。定义语法源码第 911-1052 行tag name id 数字ID # 必填00xffffffff且不能与其他标签重复 default 值 # 可选可引用 enum/flag 名 flag 位索引或已有flag flag名 # 定义标志位 enum 数值 枚举名 # 定义枚举 ;约束要点id为必填数字00xffffffff重复定义标签名或缺少 ID 都会报错flag的位索引范围 031多个位用逗号分隔flag名不得重复enum的数值范围 00xffffffff枚举名不得重复default在输出时被归一化为 32 位无符号整数第 1031-1045 行。八、能力Capabilities能力是可授予的权限片段语法源码第 1053-1129 行cap name id 数字ID # 必填且全脚本内唯一 default # 可选标记为默认能力 动作/匹配块 ;约束要点每个能力必须有唯一数字id00xffffffff重复 ID 报 Duplicate capability ID.能力内规则在 Pass 3 阶段独立编译第 1137-1142 行与文档级规则互不干扰能力规则同样支持宏、匹配与动作的完整语法。九、完整示例逐段解读仓库提供了开箱即用的示例 rule-compiler/examples/capabilities-and-tags.ztrules其内容如下# 丢弃所有非 IPv4 / IPv6 的以太网帧类型 drop not ethertype 0x0800 # IPv4 not ethertype 0x0806 # IPv4 ARP not ethertype 0x86dd # IPv6 ; # 能力放行外发 SSH cap ssh id 1000 accept ipprotocol tcp dport 22 ; ; # 标签成员所属部门 tag department id 1000 enum 100 sales enum 200 marketing enum 300 accounting enum 400 engineering ; # 放行同部门成员之间的所有流量 accept tdiff department 0 ; # 规则集以兜底放行结束整体默认为宽松策略 accept;逐段要点首段drop块三个not ethertype条件按 AND 组合含义是以太网类型既不是 0x0800IPv4也不是 0x0806ARP也不是 0x86ddIPv6的帧一律丢弃等价于只允许 IPv4、ARP、IPv6 三种帧进入cap ssh定义 id1000 的能力其内容是TCP 且目的端口 22 则接受即被授予该能力的成员可发起 SSH 连接tag department定义 id1000 的标签四个枚举值把成员划分为销售、市场、财务、工程四个部门accept tdiff department 0tdiff department 0表示双方标签 department 的差值为 0即同部门成员间流量互相放行末行accept;兜底放行一切未命中规则配合第一段的 drop形成白名单式收口 末尾兜底的典型可读策略结构。示例注释也提醒规则集变大后建议复制一份做备份。可执行验证node cli.js examples/capabilities-and-tags.ztrules输出 JSON 中config.rules依次包含ACTION_DROP含三个MATCH_ETHERTYPE的取反条件、MATCH_TAGS_DIFFERENCEACTION_ACCEPT、以及末尾的ACTION_ACCEPTconfig.capabilities含 id1000 的 SSH 能力MATCH_IP_PROTOCOL、MATCH_IP_DEST_PORT_RANGEACTION_ACCEPTconfig.tags含 id1000 的 department 标签及默认值。十、编译输出的用途与导入方式编译产物config字段rules、capabilities、tags即控制器侧可识别的网络配置片段。从仓库的 C 侧实现可以确认这些结构的对应关系node/NetworkConfig.cpp 维护rules、capabilities、tags数组与ruleCount/capabilityCount/tagCount计数并负责序列化第 230、240、291-293 行capabilities与tags会按 id 排序第 492、501 行附近规则数量存在上限ZT_MAX_NETWORK_RULESnode/NetworkConfig.cpp 第 445 行在追加规则时会做越界保护node/Network.cpp 在流量处理中逐条评估规则如第 136 行处理ACTION_ACCEPT、第 222 行处理MATCH_DEST_ZEROTIER_ADDRESSnode/Capability.hpp 第 200、310 行附近同样实现了这些规则的匹配逻辑。因此node cli.js输出的 JSON 既可用于自动化流程管道交给其他脚本写入控制器 API也是理解 ZeroTier Central 规则编辑器所见即所得背后机制的关键编辑器中的可视化规则最终都经过同一套compile()转换为上述低层数组。十一、编译原理三遍解析源码级rule-compiler/rule-compiler.js 的compile(src, rules, caps, tags)第 773-1152 行分三个阶段完成转换这也解释了错误信息为何能精确到行与列Pass 1 —— 词法/结构解析第 778-859 行把源码逐字符扫描为树状结构每个元素是[字符串, 行号, 行内列号]三元组#注释、空白、换行与;分号在这里处理OPEN_BLOCK_KEYWORDS决定何时开启新的块块结构用blockStack栈维护Pass 2 —— 语义解析第 861-1133 行遍历解析树识别macro、tag、cap定义并填充对应表宏参数、标签的id/default/flag/enum、能力的id/default/规则其余元素进入文档级规则树baseRuleTree此阶段完成名字合法性、保留字、重复定义、ID 范围等校验Pass 3 —— 低层渲染第 1135-1145 行先对每个能力调用_renderActions()编译其内部规则再编译文档级规则_renderActions()展开宏、处理动作_renderMatches()校验参数个数并生成MATCH_*/ACTION_*类型的规则对象。十二、常见错误与排查编译器对错误的定位非常精确错误信息统一为[行号, 列号, 描述]典型错误包括错误信息片段触发场景Invalid ZeroTier address.ztsrc/ztdest参数不是 10 位十六进制Invalid MAC address.macsrc/macdest参数无法规范为 MAC 格式Missing /bits netmask length designation in IP.ipsrc/ipdest未带 CIDR 前缀Invalid IP address (not valid IPv4 or IPv6).IP 语法非法Invalid numeric range.端口/帧大小区间的起点大于终点或超界Undefined variable name.引用未定义的$变量Macro name not found./Missing one or more required macro parameter.include 的宏不存在或参数不足Unrecognized match type xxx.匹配关键字拼写错误Tag definition is missing a numeric ID./Capability definition is missing a numeric ID.定义缺少必填idDuplicate tag/capability ID.同一 ID 被重复使用Multiple definition of macro/tag/capability name.同名重复定义排查建议先确认每个动作块都以;正确收尾再确认名字未落在保留字列表最后核对匹配参数的个数与取值范围错误信息中的行号与列号可以直接定位到脚本的具体字符位置。十三、小结rule-compiler是 ZeroTier 规则体系的可读语言前端它以 rule-compiler/cli.js 提供node cli.js rules script的离线编译能力以 rule-compiler/rule-compiler.js 的三遍解析把宏、标签、能力、动作与匹配完整翻译成控制器可消费的规则数组其输出结构与 node/NetworkConfig.cpp、node/Network.cpp 中的运行时规则引擎一一对应。无论是通过 ZeroTier Central 的规则编辑器在线使用还是结合 rule-compiler/examples/capabilities-and-tags.ztrules 在本地编写、备份与自动化导入掌握本文介绍的语法与编译流程都能让你对 ZeroTier 网络的准入控制做到精确、可控、可审计。【免费下载链接】ZeroTierOneA Smart Ethernet Switch for Earth项目地址: https://gitcode.com/GitHub_Trending/ze/ZeroTierOne创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进