Surge 规则系统

内容纲要

查看【Surge】专题可浏览更多内容

先从一个最简单的空白配置开始了解 Surge 配置文件的构成:

[General]

[Rule]
FINAL,DIRECT

第一个块 [General] 对应 Surge 图形界面中的「更多设置」的各种通用设置。

当然现在这里是空的,也就是没有配置任何功能。

第二个区块的 [Rule] 对应图形界面中「出站模式」下的「代理规则」,现在只有一条规则:FINAL,DIRECT

规则的组成

规则由四个部分组成:

<规则类型>,<内容条件>,<策略>,[参数]

Surge 使用规则系统来对选择每个连接的出口策略。规则的匹配方式为自上而下,逐一测试。最末尾规则一定是 FINAL 规则,当所有规则都不匹配时使用该规则。

关于策略,默认有几种内置策略,一开始可以只了解:

  • DIRECT 表示直连;
  • REJECT 表示阻止请求;

标准规则

DOMAIN 类型规则

首先需要弄明白,URL 中域名是哪些?以一条 URL 为例:

http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument

其中 www.example.com 为域名,或者说是第二个 / 之后以及第三个 / 之间的内容(不包括冒号和端口)。

💡 在 Surge 3 之前的早期版本,DOMAIN 类型规则有一个用于代理策略的参数: force-remote-dns,使用该选项时的主机名返回 Fake IP,以此解决 DNS 污染问题,由于该选项经常对用户产生困扰,现在版本已取消了这个选项,对所有主机名都会返回 Fake IP 地址。
配置 [General] 中的 always-real-ip 选项用于覆盖该行为,对于出现在该选项中的主机名,Surge 不会返回 Fake IP,会将该 DNS 查询转发给 DNS 服务器获得真实 IP 地址。

[Rule]
# DOAMIN 类型规则
# 当请求的域名完全匹配时执行:
DOMAIN,www.baidu.com,REJECT // 屏蔽百度首页
DOMAIN,baike.baidu.com,REJECT // 屏蔽百度百科
DOMAIN,1.1.1.1,REJECT

# DOMAIN-SUFFIX 类型规则
# 当请求的域名的后缀匹配时执行:
DOMAIN-SUFFIX,doubleclick.net,REJECT // 屏蔽 doubleclick.net 及其子域名

# DOMAIN-KEYWORD 类型规则
# 当请求的域名包含关键词时执行:
DOMAIN-KEYWORD,adservice,REJECT

# DOMAIN-WILDCARD
# 通配符 * 与 ? 匹配
DOMAIN-WILDCARD,ad*.example.com,REJECT

刚接触 Surge 的朋友可能会有点迷茫于 DOMAIN-SUFFIX 类型规则到底和 DOMAIN 类型规则有什么不同,在如上示例中的 DOMAIN-SUFFIX,doubleclick.net,REJECT 相比使用 hosts 需要一条条的阻止域名,如:

127.0.0.1 ad.doubleclick.net
127.0.0.1 adx.g.doubleclick.net
127.0.0.1 pubads.g.doubleclick.net

Surge 仅使用一条 DOMAIN-SUFFIX 类型规则,就可以对 doubleclick.net 及其子域名都做出了阻止。

💡 再看几个关于 DOMAIN-SUFFIX 的例子:

  • DOMAIN-SUFFIX,googleadsserving.cn 对于 googleadsserving.cnwww.googleadsserving.cn 都会生效,但 google-adsserving.cn 就不会(注意 -);
  • ads.example.com 本身及其子域名是广告,而 example.com 本身及其他子域名是正常内容就可以使用: DOMAIN-SUFFIX,ads.example.com,REJECT,所以并不是只能将一级域名用于 DOMAIN-SUFFIX;
  • 可以对域名后缀使用,如想对所有 .cn 后缀的域名都采取直连策略,那么可以使用:DOMAIN-SUFFIX,cn,DIRECT

遇到过 3 个类似 Surge 的应用都出现过对 DOMAIN-SUFFIX 设计的理解错误

另外,域名其实是主机名的一种形式,Surge 内部对域名和主机名并没有区分,所有文档所提到的域名和主机名所对应的处理逻辑都是一样的。比如,DOMAIN,1.2.3.4 规则其实也可以用于匹配目标主机为 IP 地址 1.2.3.4 的连接。DOMAIN,MacBook.local 也可用于匹配 Bonjour 主机名。

可选参数 extended-matching

开启该参数后,该规则将同时尝试匹配 SNI 和 HTTP Host Header (或 :authority)中的字段。

DOMAIN-SUFFIX,example.com,DIRECT,extended-matching

如果想仅对 SNI 生效,可使用逻辑规则 AND 配合 PROTOCOL,HTTPS 规则。

IP 类型规则

当目标主机名是一个域名或主机名时,IP 类型规则会触发本地 DNS 解析。根据解析得到的 IP 地址进行判断。当解析失败时:如果最终的 FINAL 规则带有 dns-failed 标记,那么将直接匹配 FINAL 规则。如果 FINAL 规则不带有 dns-failed 标记,该请求将直接失败

IP 类型规则有一个专有的参数 no-resolve,如果一个 IP 规则带有该参数,那么:

  1. 如果目标主机名是一个域名,那么将跳过该规则,不触发 DNS 解析。
  2. 如果目标主机名是 IP 地址,按规则进行判断。
  3. 如果目标主机名是一个域名,且先前出现的 IP 规则已经触发了 DNS 解析获得了 IP 地址,那么使用该 IP 地址进行判断。

由于 DNS 查询有时间开销,所以在配置规则时,最优的方式是尽量先不触发 DNS 解析,将所有会触发 DNS 解析的规则放在底部。这样应使用代理策略的请求就完全避免了在本地进行 DNS 解析。

但是也不用刻意的去完全避免解析,因为一旦决定使用 DIRECT 策略,那么最终还是要进行解析。Surge 有完备的 DNS 缓存系统,不必在意短时间内的重复解析。

不过需要注意,如果某目标主机在本地 DNS 不可被解析,那么一定需要加入对应的规则,在触发 DNS 前就决定策略终止匹配。或者对 FINAL 规则加上 dns-failed 标记并使用代理策略。

[Rule]
# IP-CIDR 与 IP-CIDR6 类型规则
# 当请求的目标 IP 属于指定段时执行:
IP-CIDR,47.119.139.56/32,REJECT
IP-CIDR6,2606:4700:4700::1001/64,CELLULAR

# GEOIP 类型规则
# 当 IP 归属地地区符合时执行:
GEOIP,CN,DIRECT

# IP-ASN 类型规则
# 当 IP 属于 ASN 号时执行:
IP-ASN,13335,CELLULAR
  • 如果不清楚 IP-CIDR 的 /32/64,需要自行了解「CIDR」与「子网掩码」,可以搜索「子网掩码计算器」获得帮助;
  • IP 类型规则需要注意使用参数 no-resolve,在不执行 DNS 解析可以加上,如:
    IP-CIDR,1.2.3.4/32,CELLULAR,no-resolve

    在有 DOMAIN 类型规则之前使用 IP 类型规则时最好要加上 no-resolve 或将其移动到 DOMAIN 类型之后;

  • Surge 作者有提到不建议也没有必要使用 IP-ASN 类型规则进行地区分流;

进程名类型规则

[Rule]
# PROCESS-NAME 类型规则
# 当请求的应用进程名符合时匹配,可匹配可执行文件的文件名或绝对路径,支持 * 和 ? 通配符:
PROCESS-NAME,/Applications/QQ.app/Contents/MacOS/QQ,DIRECT
PROCESS-NAME,QQ,DIRECT

该类型规则仅 macOS 版 Surge 可用,iOS 版 Surge 会忽略。

HTTP 类型规则

[Rule]
# USER-AGENT 类型规则
# 当请求的 User Agent 符合时执行:
USER-AGENT,TIM*,DIRECT

# URL-REGEX 类型规则
# 当请求的 URL 匹配正则表达式时执行:
URL-REGEX,^http://example.com/ads,REJECT

关于 USER-AGENT 类型规则:

User Agent 可在 iOS 版 Surge 的「工具」>「最近请求」内的请求列表中找到想要的并进入某个请求,在「请求」选项卡下的「REQUEST HEADER」中可看到,如腾讯 TIM 的 User Agent 为:User-Agent: TIM/3.3.5.545 CFNetwork/1220.1 Darwin/20.3.0,可以使用通配符省去名字后面的内容,也就是:TIM*

使用该规则时请注意:

  • 该规则仅对 HTTP/HTTPS 请求有效。如果是一个 raw TCP 请求中提取的 HTTP header,规则无法生效,需配置 force-http-engine 参数;
  • 对于 HTTPS 请求,存在发给 HTTP 代理的 CONNECT 请求 User-Agent 和真实的 HTTP 请求 User-Agent ,两者的内容可能相同也可能不同。前者的内容通常是由系统生成,不可被 app 调整。在未开启 MITM 的情况下,匹配时仅对前者生效。开启 MITM 后仅对后者生效;
  • 在 iOS 15 系统后,系统出于隐私保护考虑,不再于 CONNECT 请求中提供 User-Agent,这意味着对于所有 HTTPS 请求,在未开启 MITM 时,User-Agent 均不可见且规则无法生效。

关于 URL-REGEX 类型规则:

来看一个例子:

URL-REGEX,^http://quietbrightlushmagic.neverssl.com/online/,REJECT

然后打开浏览器访问改地址,如果开启了 show-error-page-for-reject 就能看到这样的 Surge 错误页面:
Surge 规则系统

❓ 通过 URL-REGEX 阻止与使用 DOMAIN 阻止有什么不同呢?
DOMAIN 类型规则只能匹配域名部分,但如果想要处理一条 URL 中的资源路径、参数等内容,就可以使用 URL-REGEX 和正则表达式进行匹配。

另外,show-error-page-for-reject 可以修改配置文件来开启 :

[General]
show-error-page-for-reject = true

或是在图形界面开启:

  • iOS:在「首页」底部的「更多设置」中打开「Show Reject Error Page」
  • macOS:「设置」>「通用」>「错误页」

⚠️ 注意:到上述例子中都是 HTTP 链接,如果你测试的是 HTTPS 链接需要开启 MitM 功能,关于 MitM 后续文章会提到,此处先略过。

端口号类型规则

[Rule]
# DEST-PORT(仅 macOS 版 Surge)
# 当目标主机端口一致时执行:
DEST-PORT,80,DIRECT
DEST-PORT,443,DIRECT

# IN-PORT(仅 macOS 版 Surge)
# 当监听端口号一致时执行:
IN-PORT,6153,DIRECT

# SRC-IP(仅 macOS 版 Surge)
# 可匹配连接来源 IP 地址,接管其他设备连接时可使用。
# 比如在使用 Surge Mac 的网关模式时可以使用该规则让某台设备全局代理或直连。
# 当请求的来源 IP 一致时执行:
SRC-IP,192.168.20.100,DIRECT

# SRC-PORT
# 匹配客户端端口号
SRC-PORT,50000,DIRECT

支持三种写法:

  • 直接撰写端口号,如 IN-PORT,6153
  • 端口号闭区间:如 DEST-PORT,10000-20000
  • 使用>、<、<=、>=操作符,如 SRC-PORT,>=50000

其他类型规则

[Rule]
# PROTOCOL
# 当请求的协议符合时执行:
# 可选项:HTTP / HTTPS / TCP / UDP / QUIC
PROTOCOL,HTTP,DIRECT

# SUBNET
# 最初 SUBNET 类型规则适用示例:
# 匹配 SSID
SUBNET,Apple Store,DIRECT
# 匹配 BSSID
SUBNET,44:f9:71:4d:ef:ef,DIRECT
# 匹配 Router Address
SUBNET,192.168.1.1,DIRECT
# 最新 SUBNET 类型规则可使用子网表达式:
SUBNET,SSID:Apple Store,DIRECT // 连接到名为 Apple Store 的 Wi-Fi 时直连

关于 SUBNET 类型规则:
子网类型规则最早用于匹配 SSID/BSSID/路由 IP 地址,SSID 匹配支持通配符,从 Surge iOS 4.12.0 及 Surge Mac 4.0.5 开始支持子网表达式:

  • 匹配 Wi-Fi 的 SSID:SSID:value
  • 匹配 Wi-Fi 的 BSSID:BSSID:value
  • 匹配路由的 IP 地址:ROUTER:value
  • 匹配所有 Wi-Fi 网络:TYPE:WIFI
  • 匹配所有有线网络:TYPE:WIRED
  • 匹配所有蜂窝网络 (仅 iOS):TYPE:CELLULAR
  • 匹配指定蜂窝网络 (仅 iOS):MCCMNC:100-200

iOS 16.4 版本开始,系统不再允许访问数据网络 MCC/MNC,相关功能可能会失效。

如果没有使用前缀,为了兼容旧版本配置,会尝试匹配 SSID/BSSID/路由 IP。

举个例子,如果想要出门时使用 Surge 处理广告,但在家时(使用路由器上的 AdguardHome 处理广告),那么希望 Surge 在处理广告时使用直连处理:

[Rule]
# 反运营商劫持
RULE-SET,https://www.example.com/Hijacking.list,REJECT

# 在连接 SSID 为 Apple 的 Wi-Fi 时直连
SUBNET,SSID:Apple,DIRECT

# 反广告
RULE-SET,https://www.example.com/Advertising.list,REJECT

FINAL,DIRECT

RULE-SET 可以先不用急着理解它,你只需要知道这个示例的意思为在连接到 SSID 名为 Apple 的 Wi-Fi 时,反运营商劫持依然生效,但反广告为直连。

也就是说后续的反广告和 FINAL 规则不重要了,因为都在 SUBNET 规则的后面,而在遇到 SSID 名为 Apple 的 Wi-Fi 时就已经走直连了。

数据网络运营商类型规则

# CELLULAR-CARRIER
# 用于匹配数据网络运营商 MCC-MNC 代码:
CELLULAR-CARRIER,460-1,DIRECT

# CELLULAR-RADIO
# 用于匹配运营商的无线电接入技术(Radio Access Technology)
# 可选项:GPRS / Edge / WCDMA / HSDPA / HSUPA / CDMA1x / CDMAEVDORev0 / CDMAEVDORevA / CDMAEVDORevB / eHRPD / HRPD / LTE / NRNSA / NR
CELLULAR-RADIO,LTE,DIRECT

移动设备网络代码(Mobile Network Code,MNC)是与移动设备国家代码(Mobile Country Code,MCC)(也称为「MCC / MNC」)相结合,以用来表示唯一一个的移动设备的网络运营商。

运营商代码映射表、电信运营商标识等叫法也是表示 MCC-MNC。

你可以通过 Surge 的「主页」>「修改」>「脚本」的「编辑器」,使用代码 (输入以下代码后点击「执行」):

console.log($network)
$done()

来获取当前的网络信息以查看 MCC-MNC 代码:
Surge 规则系统

更多的国家或地区的 MCC-MNC 可查阅维基百科:Mobile country code

由于 iOS 16.4 的新隐私策略,现在第三方应用无法获取数据网络服务商信息,MCC/MNC 的相关规则无法生效

逻辑规则

AND

与规则。当所有子规则都满足时,则执行该规则:

AND,((#Rule1), (#Rule2), (#Rule3)...),Policy

例子

[Rule]
# 强制回退到 HTTP2/HTTP1.1
AND,((PROTOCOL,UDP),(DEST-PORT,443)),REJECT-NO-DROP

# 拦截 Chrome 发出的 UDP 数据包
AND,((PROCESS-NAME,Google Chrome),(PROTOCOL,UDP)),REJECT

这两个例子的作用是针对目前 HTTP3/QUIC 协议开始流行,但国内 ISP 和国际出口的 UDP 优先级都很低,导致上网体验下降,用以强制回退到 HTTP2/HTTP1.1。

关于这个问题也可以在浏览器内进行设置。

OR

或规则。当任意子规则满足时,则执行该规则:

OR,((#Rule1), (#Rule2), (#Rule3)...),Policy

例子

[Rule]
OR,((SRC-IP,192.168.1.110), (SRC-IP,192.168.1.111)),DIRECT

网关模式下的 192.168.1.110 或 192.168.1.111 设备的请求采取直连策略。

NOT

非规则。当任意子规则满足时,则执行该规则:

NOT,((#Rule1)),Policy

例子

[Rule]
AND,((NOT,((SRC-IP,192.168.1.110))),(DOMAIN, example.com)),DIRECT

这条规则稍微有点复杂,它将 AND 和 NOT 相结合,它表示网关模式下除了设备 192.168.1.110 以外的其他设备在请求域名 example.com 时做直连处理。

规则集

RULE-SET

RULE-SET 分为内置规则集和外部规则集,内部规则集目前是两种:SYSTEMLAN

内置规则集:SYSTEM

可匹配来自 macOS 和 iOS 自身的大多数系统请求,App Store 和 iTunes 等其他内容服务的请求不会被匹配。

它的内容是:

USER-AGENT,*com.apple.mobileme.fmip1
USER-AGENT,*WeatherFoundation*
USER-AGENT,%E5%9C%B0%E5%9B%BE*
USER-AGENT,%E8%AE%BE%E7%BD%AE*
USER-AGENT,com.apple.geod*
USER-AGENT,com.apple.Maps
USER-AGENT,FindMyFriends*
USER-AGENT,FindMyiPhone*
USER-AGENT,FMDClient*
USER-AGENT,FMFD*
USER-AGENT,fmflocatord*
USER-AGENT,geod*
USER-AGENT,locationd*
USER-AGENT,Maps*
DOMAIN,api.smoot.apple.com
DOMAIN,captive.apple.com
DOMAIN,configuration.apple.com
DOMAIN,guzzoni.apple.com
DOMAIN,smp-device-content.apple.com
DOMAIN,xp.apple.com
DOMAIN-SUFFIX,ess.apple.com
DOMAIN-SUFFIX,push-apple.com.akadns.net
DOMAIN-SUFFIX,push.apple.com
DOMAIN,aod.itunes.apple.com
DOMAIN,mesu.apple.com
DOMAIN,api.smoot.apple.cn
DOMAIN,gs-loc.apple.com
DOMAIN,mvod.itunes.apple.com
DOMAIN,streamingaudio.itunes.apple.com
DOMAIN-SUFFIX,lcdn-locator.apple.com
DOMAIN-SUFFIX,lcdn-registration.apple.com
DOMAIN-SUFFIX,ls.apple.com
PROCESS-NAME,trustd

使用方法:

[Rule]
RULE-SET,SYSTEM,DIRECT

内置规则集:LAN

可匹配局域网 IP 地址和 .local 后缀,请注意该规则集会触发 DNS 请求。

它的内容为:

DOMAIN-SUFFIX,local
IP-CIDR,192.168.0.0/16
IP-CIDR,10.0.0.0/8
IP-CIDR,172.16.0.0/12
IP-CIDR,127.0.0.0/8
IP-CIDR,100.64.0.0/10
IP-CIDR,224.0.0.0/4
IP-CIDR6,fe80::/10

注意这里的「请注意该规则集会触发 DNS 请求」,如果看过之前的文章应该就会明白这是和 no-resolve 选项有关,但有关局域网的部分不单是匹配 IP 本身,一些 IP 为本地网络的域名也应该包含在内,所以不应该使用 no-resolve 选项。

故而一般的配置都会将此放置于配置末尾(在 DOMAIN 相关类型规则之后就不会触发)。

使用方法:

[Rule]
RULE-SET,LAN,DIRECT

外部规则集

外部规则集就是从另一个文件或者 URL 引用规则集。

其实从上述两个内置规则集例子可以看出,所谓的规则集就是将多个子规则放在一个单独的文件中,便于分享和复用。但是规则集中的规则不可以指定策略,而是将整个规则集指向一个同一个策略。

可以将制作好的规则集文本文件放置在服务器上进行引用,如:

[Rule]
RULE-SET,https://www.example.com/direct.list,DIRECT

在配置里使用 RULE-SET 类型规则,内容就是规则集地址,然后加上策略即可。

DOMAIN-SET

DOMAIN-SET 实际属于「标准规则」,但是其功能类似 Ruleset 所以放在一起说了。RULE-SET 可包含所有类型的子规则,而 DOMAIN-SET 仅可使用 DOMAINDOMAIN-SUFFIX 两种形式的内容。

在旧版本的 Surge 中,DOMAIN-SET 有着比 RULE-SET 更好的性能,所以在旧版本的时候如果规则数量达到了千条以上应该使用 DOMAIN-SET,另外由于性能优化,DOMAIN-SET 规则不支持包含 eTLD 后缀。

例如 .github.io 不会匹配 example.github.io。完整的 eTLD 列表:https://publicsuffix.org/list/ 如果要使用 eTLD 作为后缀,请使用 RULE-SET。

从 Surge iOS 5.8.1/Mac 5.4.1 开始,RULE-SETDOMAIN-SET 的实现完全重写,现在 Surge 会在资源更新时自动对规则集进行预处理,建立索引数据结构,大幅提高匹配速度。并且 DOMAIN-SET 也不再限制使用 eTLD 后缀。

DOMAIN-SET 文件内容示例如下:

.doubleclick.net
static.googleadsserving.cn
1.1.1.1

文件中每行为一个域名或一个 IP 地址,如果某行以 . 开头则表示匹配所有子域名和该域名本身。

它的使用方法也类似 Ruleset:

[Rule]
DOMAIN-SET,https://www.example.com/reject.list,REJECT

注释

可以使用 #// 对规则进行注释说明:

[General]

[Rule]
# GeoIP China 使用直连
# GEOIP,CN,DIRECT

FINAL,DIRECT // FINAL 使用直连
  • 当说明为独立一行时,使用 # 表示对下列一条或多条内容进行说明;
  • 当一条规则前加上 # 时,表示禁用该条规则(如上示例中的 GEOIP,CN,DIRECT);
  • 当仅对某一条规则进行说明时可在其后以 // 为开头单独说明这条规则;

通知

使用 notification-text 参数可以在触发规则时让 Surge 弹出一条通知:

[General]

[Rule]
DOMAIN-SUFFIX,bilibili.com,DIRECT,notification-text="( ゜- ゜)つロ 乾杯~"

FINAL,DIRECT

如上示例,使用英文双引号把你要通知的文字包起来,在访问哔哩哔哩时就会得到一条通知,通知内容为「( ゜- ゜)つロ 乾杯~」。