SOP:OpenClash 订阅配置(dell-R620 旁路由 · OpenWrt 24.10)¶
变更类型:C 类(生产变更) 目标主机:
openwrt(192.168.31.2,宿主 dell-R620 上的 KVM 虚拟机) 关联文档:docs/SOP.md(旁路由改造)、recovery-plan.md(断网恢复预案) 本次范围:应用机场订阅并让代理真正生效
1. 背景¶
旁路由底座已就绪(宿主 br0 + OpenWrt 单网卡 192.168.31.2 + OpenClash 0.47.156 + Mihomo 内核),
但应用订阅时代理一直不生效。排查确认是四个叠加问题,本 SOP 逐条固化:
| # | 现象 | 根因 |
|---|---|---|
| 1 | 节点域名被解析成 198.18.x.x |
fake-ip 白名单未包含节点域名,clash 拿到自己的虚拟地址去连节点 |
| 2 | 节点域名解析"无答案" | clash 走了 fallback 的国外 DoH(dns.twnic.tw / dns.cloudflare.com 超时、doh.dns.sb 证书过期) |
| 3 | 改了 custom 配置却不生效 | OpenClash 的「快速启动」优化跳过了配置生成(日志:Quick Start Mode, Skip Modify The Config File) |
| 4 | 节点全部 Timeout | 策略组默认选中机场塞的广告占位节点(server: 0.0.0.0) |
2. 目标¶
- 订阅成功拉取并设为当前配置;
- 节点域名走真实解析(不再返回 fake-ip);
- 节点延迟测试有返回值(非 Timeout);
- 端到端:经代理访问
github.com返回 200; - 服务开机自启。
3. 前置条件¶
| 项 | 要求 |
|---|---|
| 访问方式 | 可 SSH 登录 192.168.31.2(root,密码 admin) |
| 服务状态 | OpenClash 已安装(见 docs/SOP.md) |
| 订阅地址 | 机场给的 clash 订阅 URL |
| 控制面板 | clash API http://127.0.0.1:9090,密钥 123456 |
4. 影响范围¶
| 维度 | 说明 |
|---|---|
| 主机 | 仅 openwrt 虚拟机 |
| 是否停服 | 操作过程中会 stop OpenClash(恢复 DNS),约 1 分钟;不影响 Windows 上网(Win 走小米路由,不经旁路由) |
| VM 影响 | 尚未创建 VM,无影响 |
| 网络影响 | 宿主网络不变 |
| 窗口期 | 约 5 分钟 |
5. 分步操作¶
以下命令与本次实际执行一致;关键点已用注释标注。字段值按你的环境替换。
# ---------- 1. 停服务:恢复 DNS,便于下载订阅 ----------
# openclash 运行时会劫持 53 端口,导致它自己下载订阅也可能失败
/etc/init.d/openclash stop
sleep 6
# ---------- 2. 备份并清理旧配置 ----------
mkdir -p /etc/openclash/config/_backup
mv -f /etc/openclash/config/*.yaml /etc/openclash/config/_backup/ 2>/dev/null
ls -la /etc/openclash/config/
# ---------- 3. 拉取订阅 ----------
# ⚠️ 订阅地址含 token,属敏感信息,不要外传;UA 按机场要求可调整
curl -sSL -m 120 -A 'clash-verge/v1.0' \
-o /etc/openclash/config/smallstrawberry.yaml \
'https://sub3.smallstrawberry.com/api/v1/client/subscribe?token=<TOKEN>'
# 校验:应是标准 clash 配置,能看到 proxies: / proxy-groups: / rules:
head -5 /etc/openclash/config/smallstrawberry.yaml
grep -c 'server:' /etc/openclash/config/smallstrawberry.yaml
# ---------- 4. 指定当前配置文件 ----------
uci set openclash.config.config_path='/etc/openclash/config/smallstrawberry.yaml'
uci commit openclash
# ---------- 5.【关键】fake-ip 白名单:节点域名必须真实解析 ----------
# 不加这条 → 节点域名被解析成 198.18.x.x → clash 拿虚拟地址连节点 → 必然失败
printf '+.the-best-airport.com\n' > /etc/openclash/custom/openclash_custom_fake_filter.list
uci set openclash.config.custom_fakeip_filter='1'
uci set openclash.config.custom_fakeip_filter_mode='blacklist' # blacklist=列表内域名不走 fake-ip
# ---------- 6.【关键】节点域名的 DNS 策略 ----------
# 默认 fallback 的国外 DoH 在国内不可达(超时/证书过期),会让节点域名解析失败。
# 这里显式指定用国内可用的 DoH 解析节点域名,绕开失效的 fallback。
cat > /etc/openclash/custom/openclash_custom_domain_dns_policy.list <<'EOF'
'+.the-best-airport.com': ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query', '223.5.5.5', '119.29.29.29']
EOF
uci set openclash.config.custom_name_policy='1'
uci commit openclash
# ---------- 7.【关键】强制完整启动 ----------
# OpenClash 有「快速启动」优化:若它判定文件未变,会跳过配置生成(日志可见
# "Quick Start Mode, Skip Modify The Config File"),第 5、6 步就不会生效。
# touch /etc/config/openclash 是官方判据里的触发条件。
touch /etc/config/openclash
# ---------- 8. 打开开关并启动 ----------
uci set openclash.config.enable='1'
uci commit openclash
/etc/init.d/openclash start
sleep 25
/etc/init.d/openclash status # 预期 running
# ---------- 9. 选择节点:避开机场的广告占位节点 ----------
# 订阅里常混入 server 为 0.0.0.0 的广告节点(名字如「请立即到官网下载新客户端!」),
# 「自动选择」组可能选中它 → 全部超时。手工切到真实节点。
curl -s -X PUT -H 'Authorization: Bearer 123456' -H 'Content-Type: application/json' \
-d '{"name":"🇯🇵日本 09 | 高级专线"}' \
'http://127.0.0.1:9090/proxies/%E4%B8%80%E5%85%83%E6%9C%BA%E5%9C%BA'
# ---------- 10. 节点自动选优 + 剔除广告节点 ----------
# 【设计要点】剔除逻辑必须放在「覆写脚本」而不是直接改配置文件:
# 订阅更新会覆盖 config/*.yaml,但覆写脚本在每次启动时都会重新执行,
# 所以订阅怎么覆盖都不怕。执行链:
# 订阅下载 → 生成配置(Step3) → 覆写脚本(Step3.5) → 运行时配置
#
# 10.1 写剔除脚本(v2:server 剔除 + 名字模式剔除)
cat > /etc/openclash/custom/clean_nodes.rb <<'RUBY'
require 'yaml'
cfg = ARGV[0]
unless cfg && File.exist?(cfg)
warn "配置文件不存在: #{cfg}"
exit 1
end
data = YAML.load_file(cfg)
INFO_NAME = /(剩余|重置|到期|官网|流量|expire|traffic)/i
removed = []
if data['proxies'].is_a?(Array)
removed = data['proxies']
.select { |p| p.is_a?(Hash) && (p['server'].to_s == '0.0.0.0' || p['name'].to_s =~ INFO_NAME) }
.map { |p| p['name'] }
data['proxies'].reject! { |p| p.is_a?(Hash) && (p['server'].to_s == '0.0.0.0' || p['name'].to_s =~ INFO_NAME) }
end
cleaned = 0
if data['proxy-groups'].is_a?(Array)
data['proxy-groups'].each do |g|
next unless g.is_a?(Hash)
if g['proxies'].is_a?(Array) && !removed.empty?
before = g['proxies'].length
g['proxies'] -= removed
cleaned += before - g['proxies'].length
end
# 每 5 分钟全组重测重选(节点劣化响应);容差 50ms 抑制无意义跳变
if %w[url-test load-balance].include?(g['type'])
g['interval'] = 300
g['tolerance'] = 50 if g['type'] == 'url-test'
end
end
end
File.open(cfg, 'w') { |f| f.write(YAML.dump(data)) }
RUBY
chmod 755 /etc/openclash/custom/clean_nodes.rb
# ⚠️ v2 版剔除必须含名字模式:fsc 类机场的信息占位节点(剩余流量/套餐到期等)
# server 是真实地址(与真实节点相同),无法按 server 区分;留在组里会被
# url-test 选中(端口通、延迟低)导致代理全断。
# 10.2 挂到覆写链(在 openclash_custom_overwrite.sh 的 exit 0 之前插入;
# ⚠️ 文件名是 openclash_custom_overwrite.sh,勿与本文其它处简写混淆)
ruby /etc/openclash/custom/clean_nodes.rb "$CONFIG_FILE" >> $LOG_FILE 2>&1
# 10.3 自动选优 / 故障切换由策略组类型提供,无需额外开关:
# url-test → 按延迟自动选最优(interval=3600,即每小时重选)
# fallback → 当前节点不可用时自动切下一个
#
# ⚠️ 10.4 千万不要开 auto_smart_switch
# 它会把组类型改成 type: smart,而标准 mihomo 内核不认,直接 fatal 起不来:
# level=fatal msg="Parse config error: proxy group[1]: unsupported type: smart"
# 该功能需要 OpenClash 的定制内核;改用标准 url-test 即可满足「定时选优 + 故障切换」。
6. 验证¶
# 1) 服务状态与自启
/etc/init.d/openclash status # 预期 running
/etc/init.d/openclash enabled && echo 已设自启
# 2) 运行时 fake-ip-filter 是否含节点域名(关键)
grep -A6 'fake-ip-filter' /etc/openclash/smallstrawberry.yaml | head -9
# 预期:fake-ip-filter-mode: blacklist
# fake-ip-filter: [ "+.the-best-airport.com", rule-set:oc-cn-domain ]
# 3) 运行时 nameserver-policy(关键)
grep -A6 'nameserver-policy' /etc/openclash/smallstrawberry.yaml | head -9
# 预期:+.the-best-airport.com 指向 doh.pub / alidns / 223.5.5.5
# 4) 节点延迟(应返回数字,非 Timeout)
curl -s -H 'Authorization: Bearer 123456' \
'http://127.0.0.1:9090/proxies/🇯🇵日本%2009%20%7C%20高级专线/delay?url=http://www.gstatic.com/generate_204&timeout=5000'
# 预期:{"delay":4xx}
# 5) 端到端代理实测
curl -sS -m 20 -x http://Clash:TuTvVtkS@127.0.0.1:7890 -o /dev/null -w '%{http_code}\n' https://github.com
# 预期:200
curl -sS -m 20 -x http://Clash:TuTvVtkS@127.0.0.1:7890 -o /dev/null -w '%{http_code}\n' https://www.google.com
# 预期:302
# 6) 当前选中的节点
curl -s -H 'Authorization: Bearer 123456' 'http://127.0.0.1:9090/proxies/一元机场' | head -c 200
实测结果(本次):日本节点 404–495ms 最优,美国 983–1126ms,香港 1439–1877ms; 代理下载速度约 1.57 MB/s;github / google / youtube 均正常。
7. 回滚¶
7.1 备份¶
| 对象 | 位置 |
|---|---|
| 旧订阅配置 | /etc/openclash/config/_backup/ |
| fake-ip 白名单 | /etc/openclash/custom/openclash_custom_fake_filter.list.bak.<时间戳> |
| uhttpd 配置 | /etc/config/uhttpd.bak.<时间戳> |
7.2 回滚触发判据¶
github.com经代理返回非 200/302;- 节点延迟测试全部 Timeout;
- OpenClash 反复重启或
status非 running。
7.3 回滚步骤¶
# ① 恢复旧订阅配置
mv -f /etc/openclash/config/_backup/*.yaml /etc/openclash/config/
uci set openclash.config.config_path='/etc/openclash/config/<旧配置>.yaml'
uci commit openclash
# ② 关掉本次新增的自定义项
uci set openclash.config.custom_fakeip_filter='0'
uci set openclash.config.custom_name_policy='0'
uci commit openclash
# ③ 重启
touch /etc/config/openclash && /etc/init.d/openclash restart
# ④ 彻底停用(回到无代理状态)
uci set openclash.config.enable='0'; uci commit openclash
/etc/init.d/openclash stop
7.4 回滚后验证¶
/etc/init.d/openclash status # 预期 inactive(若已停用)
nslookup www.baidu.com 127.0.0.1 # DNS 应恢复正常
8. 风险说明¶
| 风险点 | 说明 | 缓解 |
|---|---|---|
| 新增/更换机场需同步白名单 | fake-ip 白名单与 DNS 策略按节点域名命中,新机场的域名不在名单里就会"节点全部超时"。现名单已含双机场域名(the-best-airport.com / dnscloudcloud.top),在两者间切换无需改动;新增机场时按 §8「新增机场速查」追加一行 |
见下方「新增机场速查」 |
| 广告占位节点 | 机场会在订阅里塞 server: 0.0.0.0 的推广节点,URLTest 可能选中它 |
手工固定到真实节点(第 9 步) |
| 快速启动跳过配置 | 改完 custom 目录若忘记 touch /etc/config/openclash,改动不生效且无报错 |
把它作为固定动作 |
| DNS fallback 失效 | 配置默认 fallback 的国外 DoH 在国内不可达/证书过期 | 已用 nameserver-policy 绕开;也可在 LuCI 的 DNS 设置里替换 fallback |
| 节点速度波动 | 实测机场带宽有限(约 1.5 MB/s) | 用延迟测试挑最快节点 |
| 凭据泄露 | 订阅 token、代理认证 Clash:TuTvVtkS、API 密钥 123456 均属敏感 |
不要外传;必要时在 LuCI 中改密 |
| 旁路由无 wan 区域 | 日志持续告警 Verify The Firewall's WAN Zone Name is wan |
旁路由模式下属预期,不影响代理;若需"仅允许内网"等功能再考虑补一个名义 wan 区域 |
auto_smart_switch 需定制内核 |
开启后会把组类型改成 type: smart,标准 mihomo 内核不支持 → 直接 fatal 起不来(unsupported type: smart) |
保持关闭;标准 url-test + interval:3600 已满足「定时选优 + 故障切换」 |
openclash stop 会置 enable=0 |
停服后再 restart 会被拒(日志:OpenClash Now Disabled, Need Start From Luci Page),表现为"改了配置却不生效" |
停服后要重启,先 uci set openclash.config.enable='1'; uci commit openclash |
| 广告节点随订阅回归 | 订阅更新会带回 server: 0.0.0.0 的推广节点 |
已用覆写脚本每次启动自动剔除(第 5 章 10.1/10.2),订阅怎么覆盖都不怕 |
| 订阅自动更新"看似开启实则失效" | auto_update=1 但 config_auto_update_mode / auto_update_time / config_update_week_time 未设置时,init.d 写 cron 的判断因空值失败,订阅永远不会自动更新,且无任何报错 |
三参数显式设置,并确认 cron 出现 openclash.sh 任务(第 10.1 节) |
| 本机 curl 假警报 | 订阅自带 SRC-IP-CIDR,192.168.31.2/32,DIRECT 防回环规则:本机透明流量一律直连,clash 内部 DNS 失效时 curl 报 TLS EOF / 000,易误判为"代理坏了" |
验证代理走 7893 端口带认证(127.0.0.1 不命中防回环规则),或从其它设备测(第 10.2 节) |
新增机场速查(白名单为追加式,双机场切换无需改动)¶
# 1) 从新订阅配置里 grep server: 观察,取节点域名公共父域(如 +.newairport.com)
# 2) 追加进 fake-ip 白名单(不要覆盖已有行)
echo '+.newairport.com' >> /etc/openclash/custom/openclash_custom_fake_filter.list
# 3) 追加进节点域名 DNS 策略(同样追加行,勿覆盖)
echo "'+.newairport.com': ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query', '223.5.5.5']" \
>> /etc/openclash/custom/openclash_custom_domain_dns_policy.list
# 4) 最后必须触发完整重启
touch /etc/config/openclash && /etc/init.d/openclash stop && uci set openclash.config.enable='1' && uci commit openclash && /etc/init.d/openclash start
9. 附:本次同时修掉的原镜像问题¶
| 问题 | 现象 | 修复 |
|---|---|---|
| LuCI 界面不可用 | /cgi-bin/luci/ 一律 403 |
uhttpd 的 lua_prefix(21.02 时代路径)改为 ucode_prefix=/cgi-bin/luci=/usr/share/ucode/luci/uhttpd.uc |
| 内核过旧 | clash_meta 为 2023-02 编译,不支持新协议 |
换成 Mihomo Meta v1.19.31(选 compatible 版,适配 Sandy Bridge 无 AVX2) |
| 缺 TProxy 模块 | 日志 【nft_tproxy】module not found |
安装 kmod-nf-tproxy + kmod-nft-tproxy |
| LuCI 菜单不显示 OpenClash | 服务菜单里找不到 | 需存在 /etc/config/openclash;并清 LuCI 缓存后强制刷新浏览器 |
10. 补遗:订阅自动更新与"本机验证假警报"(2026-09-15 复核)¶
10.1 让订阅自动更新真正生效¶
auto_update=1 只是必要条件。init.d 写 cron 任务的判断要求三个参数齐全,
未设置(空值)时条件判断失败 → cron 里永远不会有订阅更新任务,且无任何报错。
相关逻辑摘要(/etc/init.d/openclash):
# [ auto_update = 1 ] 且 [ config_auto_update_mode != 1 ] 时写入:
# 0 <auto_update_time> * * <config_update_week_time> /usr/share/openclash/openclash.sh
# 三个值任一为空,该判断即不成立
修复(已执行,幂等):
# 高危操作标记:修改 uci 配置并重启服务,先备份
cp /etc/config/openclash /etc/config/openclash.bak.$(date +%Y%m%d-%H%M%S)
uci set openclash.config.config_auto_update_mode='0' # 0=定时更新(缺省空值会导致判断失败)
uci set openclash.config.auto_update_time='4' # 凌晨 4 点
uci set openclash.config.config_update_week_time='0' # 0=周日(7=每天,可按需改)
uci commit openclash
# ⚠️ 重启用 stop → 设 enable=1 → start 的顺序;不要用 restart:
# stop 会把 enable 置 0(见风险表),restart 一旦踩中会拒启且无显式报错
/etc/init.d/openclash stop
uci set openclash.config.enable='1'
uci commit openclash
/etc/init.d/openclash start
# 验证:cron 必须出现这一行
crontab -l | grep openclash.sh
# 预期:0 4 * * 0 /usr/share/openclash/openclash.sh #openclash-cron-task
订阅每周日 04:00 自动更新;更新会触发完整启动链,覆写脚本再次剔除广告节点 (第 5 章 10.1/10.2),策略组周期也被重置为 3600s——整条自动化链路闭环。
回滚:删除三个参数并重启即可恢复原状(uci delete openclash.config.<参数名> × 3,然后按上方 stop → start 顺序重启)。
终验记录(2026-09-15 会话接续复核,状态一致零变更):
enable=1、auto_update=1、三参数齐全,cron 命中 0 4 * * 0 openclash.sh(另含 chnroute 任务);
openclash_custom_overwrite.sh 第 126 行挂载 clean_nodes.rb(exit 0 之前);
当前配置 smallstrawberry.yaml 中 server: 0.0.0.0 节点为 0;
经 7893 代理 github=200(5.9s)/ google=302(5.6s);策略组 alive=true 且节点列表无广告项。
10.2 "本机 curl 不通"≠代理故障(防回环规则)¶
订阅规则自带本机防回环(运行时 yaml 中可见):
SRC-IP-CIDR,192.168.31.2/32,DIRECT
含义:OpenWrt 本机发起的透明代理流量一律 DIRECT 直连(防止代理回环,属机场正常设计)。
因此在本机上直接 curl https://github.com(透明代理路径)时:
- clash 按规则走 DIRECT,需要 clash 自己解析域名;
- clash 内部 fallback 的国外 DoH 全部失效(证书过期/超时)时解析超时,日志出现
dns resolve failed: context deadline exceeded,curl 侧表现为ssl_handshake ... EOF/000—— 这是本机直连失败,不是代理故障。
验证代理链路的正确姿势(二选一):
# 方式一:走显式代理端口(mixed 7893,带认证;源 127.0.0.1 不命中防回环规则)
curl -m 20 -x http://Clash:TuTvVtkS@127.0.0.1:7893 \
-o /dev/null -w '%{http_code}\n' https://github.com # 预期 200
# 方式二:从其它设备(Win / VM,网关指向 192.168.31.2)直接访问 github
实测记录(2026-09-15 复核):
- 节点 14 个全部健康,日本 457–482ms 最优(触发全组测速后 url-test 自动重选);
- 经 7893 代理:github=200 (5.9s) / google=302 (5.5s);
- 覆写链幂等:连续两次重启日志均输出
剔除广告节点 2 个;清理策略组引用 6 处;url-test 周期置 3600s。
11. 双订阅实测择优与应用 fsc(2026-09-15)¶
11.1 需求与结论¶
- 新增第二订阅
fsc(https://103.14.76.98/sub/fsc/...,IP 直连 HTTPS,证书可信)。 - OpenClash 单次只能加载一个配置文件,两订阅无法直接同时生效;合并方案需自维护合并脚本且随订阅更新过期,弃用。
- 按「同口径实测、择优应用」执行,fsc 胜出并已应用。
11.2 同口径实测(基准脚本:切配置→完整重启→逐节点延迟(gstatic 204/5s)→切最快节点测 cloudflare 10MB)¶
| 指标 | smallstrawberry(原) | fsc(新) |
|---|---|---|
| 真实节点数 | 18 | 46 |
| 可用数 | 14/18 | 30/46 |
| 最优延迟 | 408ms(日本09) | 208ms(美国-HY2-29) |
| 中位延迟 | 456ms | 648ms |
| 最快节点带宽 | 1.60 MB/s | 5.11 MB/s |
fsc 的 HY2(Hysteria2/UDP)节点池大、最优延迟与带宽显著更优,择优机制下表现占优。
11.3 应用动作(均已执行并验证)¶
- 订阅登记对齐(修复自动更新文件名漂移):
openclash.sh按订阅记录name存文件(/etc/openclash/config/<name>.yaml)并改写config_path; 原记录名ond_doller与在用文件smallstrawberry.yaml不一致,周日 cron 更新会产生"更新件与在用件漂移"。- 已改:记录[0]
name=smallstrawberry;新增记录[1]name=fsc(两条均enabled=1,UAclash-verge/v2.4.5)。 - cron 双任务保留:订阅更新 + chnroute 更新。
- 剔除脚本升级 v2(
clean_nodes.rb):新增名字模式剔除(剩余|重置|到期|官网|流量|expire|traffic)。 原因:fsc 的信息占位节点(剩余流量:100 GB等 3 个)server 是真实地址,无法按server=0.0.0.0识别, 留在组里会被 url-test 选中导致全断。首轮剔除3 个占位节点 / 9 处组引用。 - 白名单改为双机场追加式:
openclash_custom_fake_filter.list与openclash_custom_domain_dns_policy.list同时含+.the-best-airport.com与+.dnscloudcloud.top,两订阅间切换无需再改白名单(见 §8 新增机场速查)。 - config_path →
/etc/openclash/config/fsc.yaml,主组「便宜机场」切至美国-HY2-29。
11.4 实测提升与幂等验证¶
| 项 | smallstrawberry | fsc 应用后 |
|---|---|---|
| github 经 7893 | 200 / 5.9s | 200 / 0.78–0.94s |
| google 经 7893 | 302 / 5.5s | 200 / 0.58s |
| 带宽(10MB) | 1.60 MB/s | 6.30 MB/s |
幂等复跑:第二次完整重启覆写日志与首次完全一致(剔除 3 个/9 处/3600s)——覆写链每次启动从订阅原稿重建,
输入相同输出恒定,订阅怎么覆盖都不怕。/etc/init.d/openclash status=running。
11.5 本次顺带项¶
- 时区:
zonename=Asia/Shanghai、timezone=CST-8,date输出 CST(+0800)。 - 旧订阅清理:
/etc/openclash/config/_backup/one.yaml已删除(用户确认;smallstrawberry 的旧备份hk-self-build.yaml保留)。 - 远程基准脚本与中间文件已清理(
/tmp/bench.sh等)。
11.6 回滚(切回 smallstrawberry)¶
uci set openclash.config.config_path='/etc/openclash/config/smallstrawberry.yaml'; uci commit openclash
touch /etc/config/openclash
/etc/init.d/openclash stop; uci set openclash.config.enable='1'; uci commit openclash; /etc/init.d/openclash start
# 白名单为双机场共用,无需回改;如需恢复 v1 剔除脚本:/etc/openclash/custom/clean_nodes.rb.bak.20260915-034500
11.7 自动选优周期调优 v3(2026-09-15)¶
背景:1 小时重选对"节点劣化"响应太慢(节点真挂会即时切换,但"变慢了"要等满周期); 且主组此前手动固定单节点,完全不参与自动重选,劣化无保护。
调整(clean_nodes.rb v3,已生效):
- url-test 组
interval: 3600 → 300(5 分钟全组重测),tolerance: 50(抑制 <50ms 的无意义跳变); - 主组「便宜机场」由固定节点改选「自动选择」子组,自动选优全程接管;
- 实证:切换后自动选择立即选中
美国-HY2-31(非此前人工固定的 HY2-29),重选机制工作正常; - 不取更小值(如 60s)的原因:46 节点全组测速过频 + 节点跳变频繁导致连接重建卡顿; tolerance 是跳变与劣化响应之间的平衡点。
注:节点真故障的切换不受 interval 影响(请求失败即时触发健康检查),300s 只优化"劣化"场景。
11.8 DNS fallback 修复 v4(2026-09-15)¶
问题:fallback 原为裸 UDP 1.1.1.1 / 8.8.8.8,污染过滤基本失效——冷门国外域名(不在 479 条
域名清单内)走 GEOIP,CN 判断时依赖这份解析,污染结果被判为国内 IP → 误判直连 → 打不开。
实测:本网络对国外 IP 的 443/5053 直连全阻断(1.1.1.1 / 8.8.8.8 / 9.9.9.9 的 IP 式 DoH 均 超时),单纯换加密协议不可行。
解法(clean_nodes.rb v4,覆写链内实现):
data['dns']['fallback'] = [
"https://1.1.1.1/dns-query##{主组名}", # mihomo 的 #代理组 语法:DoH 查询流量本身走代理发出
"https://8.8.8.8/dns-query##{主组名}"
]
data['dns']['default-nameserver'] -= ['1.1.1.1', '8.8.8.8'] # 纯冗余,清理
要点:
- IP 直连式 DoH:fallback 服务器自身零解析依赖,无"鸡生蛋"问题;
#代理组:DoH 查询流量经代理发出,加密 + 必达 + 不受直连阻断影响;主组名动态取第一个type: select组(注意配置文件里是小写select,与 API 返回的Selector大小写不同);- 降级安全:代理不可用时 fallback 失效,退化为国内解析,行为同旧版,不会更糟。
验证:twitter.com → 172.66.0.227(Cloudflare 真实段)、google → 142.251.x(真实段)、 github → 172.182.x;均非污染特征 IP。幂等复跑覆写输出一致。
回滚:恢复 clean_nodes.rb.bak.20260915-040724(v3)并完整重启即可。
12. 运维加固(2026-09-15 体检后执行)¶
体检结论(未动项):内存 2G 占用 76M、负载 0.2——性能类优化无必要;lan 无 IPv6 分配、无全局 v6——无泄漏风险; mihomo v1.19.31(2026-09-14 编译)为最新版;订阅实际为每天 04:00 自动更新(cron 实测,比 §10.1 记录的周更更勤)。
已执行项:
| # | 项 | 变更 | 验证 |
|---|---|---|---|
| 1 | 🔴 Clash API 弱密钥 | dashboard_password 123456 → 强随机(见下);面板登录同步使用 |
新密钥 200 / 旧密钥 401 |
| 2 | SSH 弱密码 | Dropbear PasswordAuth/RootPasswordAuth 关闭,仅密钥登录 |
当前密钥会话未中断 |
| 3 | opkg 官方源不稳 | distfeeds.conf 全量切清华镜像(备份 .bak.*) |
opkg update 签名校验通过 |
| 4 | NTP 国外源 | server 改 ntp.aliyun.com / tencent / cn.pool(uci system.ntp) | sysntpd 运行 |
| 5 | GeoIP 数据不更新 | 启用自带 geo_auto_update,每周一 05:00(cron 实证) |
cron 含 openclash_geo.sh ipdb |
| 6 | 系统日志仅内存 | log_file=/root/syslog.log,4MB 循环 |
logger 写入实测命中 |
🔴 新 API 密钥(敏感,勿外传):面板/external-controller 登录使用
9adad5b8a7c5f06eec08c30dde7a059c
面板地址:http://192.168.31.2:9090/ui
未动项(说明理由):
- 代理端口密码
TuTvVtkS:无设备使用显式代理,更换会破坏 SOP 中既有命令引用;需要时说一声再换; - VM 镜像备份:用户暂缓,待后续单独执行(宿主侧
qemu-img+ cron); - 防火墙限制 9090 来源:密钥已强,旁路由管理便利性优先,暂不加规则。
回滚:openclash/dropbear/system/opkg 均有 .bak 备份或 uci 默认值;日志落盘关闭 = 删除 log_file 项重启。
12.1 Web"无订阅信息"排查与订阅记录重建(2026-09-15)¶
机制:LuCI「配置订阅」页的订阅信息(流量/到期)不是 uci 存储,而是前端每 15 分钟
XHR 请求 sub_info_get 接口(controller/openclash.lua:861 → fetch_sub_info:614),
实时 curl 订阅 URL(-m 5 --retry 2,无代理)解析 subscription-userinfo 响应头;
结果缓存在浏览器 localStorage。
根因:/etc/config/openclash 的 config_subscribe 记录损坏——smallstrawberry 整条消失、
fsc 只剩 name/sub_ua(address/enabled/sub_convert 全丢),LuCI 无地址可请求 → 显示"无订阅信息";
同时 cron 的 openclash.sh 静默跳过(sub_info_get 对空 address 直接 return)→ 自动更新失效。
修复(已执行):备份后重建两条记录(uci add openclash config_subscribe + 完整字段,
显式 sub_convert='0'),重建后手动触发 openclash.sh 一次性验证:fsc 下载成功并自动重启;
sub_info_get API 两订阅均返回正常(fsc 剩余 100.0GB / smallstrawberry 剩余 498.9GB)。
提示:
- Web 页信息有 15 分钟 localStorage 缓存,订阅页点 Refresh 按钮可强制刷新;
- 记录损坏诱因不明(登记后某时刻发生),若 Web 再现"无订阅信息",先查
grep -A8 config_subscribe /etc/config/openclash,字段缺失照本节重建即可。
13. 自动选优改造:亚太区域 + 带宽与延迟并重(2026-10-01,v5)¶
变更类型:C 类 | 目标主机:
openwrt(192.168.31.2) 日常使用与故障处置见openclash-apac-20261001/RUNBOOK.md;备份台账见openclash-apac-20261001/backup/README.md实现体:/etc/openclash/custom/clean_nodes.rb(覆写脚本 v5)、/etc/openclash/custom/speed_select.rb(选优脚本) 源码留档:openclash-apac-20261001/(与线上文件一致)
13.1 背景¶
原自动选优为纯延迟(url-test + http://www.gstatic.com/generate_204)。实测确认该机制只测首包延迟、不读响应体:对 10MB 端点做 delay 测试仅耗时 0.65s(非下载耗时),故"低延迟但低带宽"的节点会被长期选中。
2026-10-01 同一轮测速内的实测对比(脚本实测):
| 节点 | 延迟 | 带宽 | url-test 会选它吗 |
|---|---|---|---|
| 日本-HY2-47 | 161–611 ms | 228–895 KB/s | 会(延迟更低) |
| 泰国-HY2-48 | 170–180 ms | 1.58–4.28 MB/s | 视延迟波动 |
用户诉求:① 节点只选亚太(排除香港,ChatGPT 受限);② 带宽与延迟并重。
13.2 目标¶
- 延迟选优与故障切换只在亚太 8 节点池内进行,不再落到美国 / 德国;
- 补上带宽维度:当前出口带宽低于门槛时自动换到带宽达标的节点;
- 亚太整体不可用时回退全量池,保证不断网;
- 改动全部落在覆写脚本,订阅每周更新冲不掉。
13.3 前置条件¶
| 项 | 要求 |
|---|---|
| 访问 | 可 SSH 登录 192.168.31.2(root,密钥) |
| 服务 | OpenClash 运行中;uci get openclash.config.config_path = /etc/openclash/config/fsc.yaml |
| 运行时依赖 | ruby 3.3.10、curl(系统自带)。无 python3 / jq;ruby 的 json、net/http 库在 OpenWrt 上被拆包不可用 → 脚本用 YAML.load 解析 JSON(JSON 是 YAML 子集),HTTP 走 curl |
| 磁盘 | / 剩余 ≥ 100 MB(实测 20.5 G) |
13.4 影响范围¶
| 维度 | 说明 |
|---|---|
| 主机 | 仅 openwrt 虚拟机;宿主与网络拓扑不变 |
| 停服 | 部署时 stop/start OpenClash,代理中断约 1 分钟(Win 走小米路由,不受影响) |
| 流量开销 | 常态约 0.5–0.7 GB/天(完整测速每 30 分钟一轮,每轮下载 3×5 MB) |
| 窗口期 | 约 5 分钟 |
13.5 分步操作¶
步骤 1:备份(高危前置)
TS=$(date +%Y%m%d-%H%M%S)
cp -a /etc/openclash/custom/clean_nodes.rb /etc/openclash/custom/clean_nodes.rb.bak.$TS
crontab -l > /root/crontab.bak.$TS
预期:两个 .bak.$TS 生成。回滚靠它们,台账见 openclash-apac-20261001/backup/README.md。
步骤 2:部署覆写脚本 v5 与选优脚本
把 openclash-apac-20261001/{clean_nodes.rb,speed_select.rb} 放到 /etc/openclash/custom/ 并置可执行:
scp openclash-apac-20261001/clean_nodes.rb openclash-apac-20261001/speed_select.rb root@192.168.31.2:/etc/openclash/custom/
ssh root@192.168.31.2 'chmod 755 /etc/openclash/custom/clean_nodes.rb /etc/openclash/custom/speed_select.rb'
clean_nodes.rb v5 相对 v4 新增四件事(对订阅原稿的每次启动重建,幂等):
- 新增
亚太优选(url-test,成员 = 亚太节点,interval:300/tolerance:50)与亚太测速(select,仅服务测速脚本)两个组,插在主组之后(主组必须仍是"第一个 select 组",DNS fallback 依赖它); - 主组
proxies首位插入亚太优选; rules首条插入DOMAIN-SUFFIX,speed.cloudflare.com,亚太测速—— 让测速流量走独立组,不干扰主链路,且该位置在订阅自带的SRC-IP-CIDR,192.168.31.2/32,DIRECT之前;- 亚太池取自
proxies名字:日本|东京|大阪|泰国|曼谷|新加坡|狮城|台湾|台北|中国|韩国|首尔|越南|马来|菲律宾,再排除香港|Hong Kong|HK。
坑:亚太池为空时不新增任何组与规则,行为退回 v4(安全降级);中国-HY2-69 的 server 是 hinet.huhu.icu(台湾中华电信),不是香港。
步骤 3:写 cron(幂等)
(crontab -l 2>/dev/null | grep -v "speed_select.rb"; \
echo "*/5 * * * * /usr/bin/ruby /etc/openclash/custom/speed_select.rb >/dev/null 2>&1 #speed_select") | crontab -
crontab -l | grep speed_select # 预期命中一行
坑:cron 的 PATH 不含 /sbin,而 uci 在 /sbin —— 脚本已在启动处把标准 PATH 前置;不要删掉那一行,否则最小环境下预检查必然失败。
步骤 4:完整重启
touch /etc/config/openclash
/etc/init.d/openclash stop; sleep 3
uci set openclash.config.enable=1; uci commit openclash
/etc/init.d/openclash start; sleep 40
/etc/init.d/openclash status # 预期 running
grep -a "亚太池" /tmp/openclash.log | tail -1
坑:不要用 restart —— stop 会把 enable 置 0,restart 踩中会拒启且无显式报错。
步骤 5:主组指向「亚太优选」(一次性)
S=$(uci -q get openclash.config.dashboard_password)
curl -s -X PUT -H "Authorization: Bearer $S" -H 'Content-Type: application/json' \
-d '{"name":"亚太优选"}' 'http://127.0.0.1:9090/proxies/%E4%BE%BF%E5%AE%9C%E6%9C%BA%E5%9C%BA'
原因:select 组的当前选择被 mihomo 持久化在 cache,改成员顺序不会自动改选中项,必须显式设置一次。
步骤 6:跑一次选优脚本
ruby /etc/openclash/custom/speed_select.rb --force # 完整测速
ruby /etc/openclash/custom/speed_select.rb --dry-run # 只预览、不切主组
13.6 验证¶
S=$(uci -q get openclash.config.dashboard_password)
# 1) 组结构:亚太优选=URLTest/8 成员,亚太测速=Selector/8 成员
curl -s -H "Authorization: Bearer $S" http://127.0.0.1:9090/proxies | \
ruby -ryaml -e 'd=YAML.load(STDIN.read)["proxies"]; %w[便宜机场 亚太优选 亚太测速].each{|k| v=d[k]; puts "#{k} type=#{v["type"]} now=#{v["now"]} n=#{v["all"].size}"}'
# 预期:便宜机场 now=亚太优选;亚太优选与亚太测速成员均为 8
# 2) 出口归属亚太(loc 应为 TH/SG/JP/TW,绝不能是 HK/US/DE)
curl -s -m 20 -x "http://<proxy-auth>@192.168.31.2:7893" https://chatgpt.com/cdn-cgi/trace | grep -E '^(ip|loc|colo)='
# 3) 端到端与带宽
curl -s -m 20 -x "http://<proxy-auth>@192.168.31.2:7893" -o /dev/null -w 'github=%{http_code}\n' https://github.com # 预期 200
curl -s -m 45 -x "http://<proxy-auth>@192.168.31.2:7893" -o /dev/null -w 'speed=%{speed_download}\n' 'https://speed.cloudflare.com/__down?bytes=5242880'
# 4) 脚本可运行性与幂等:连续两次,第二次应为「无需切换」
ruby /etc/openclash/custom/speed_select.rb --force | tail -1
ruby /etc/openclash/custom/speed_select.rb --force | tail -1
# 5) cron 兼容性(最小环境,验证 PATH 修复)
env -i /usr/bin/ruby /etc/openclash/custom/speed_select.rb; echo $? # 预期 0
# 6) cron 是否真在触发(健康检查轮不写业务日志,靠心跳时间判断)
cat /tmp/speed_select.state # last_run 应每 5 分钟刷新一次
实测(2026-10-01):出口 loc=TH(44.x/BKK)、github=200、google=302、带宽 4.97–8.31 MB/s;
连续两次 --force 均输出「目标已是 亚太优选,无需切换」;env -i 下退出码 0;
cron 于 12:40:01 实际触发(last_run=1790829601,心跳按 5 分钟周期刷新)。
13.7 回滚¶
# ① 恢复 v4 覆写脚本
cp -a /etc/openclash/custom/clean_nodes.rb.bak.<TS> /etc/openclash/custom/clean_nodes.rb
# ② 停用选优脚本
(crontab -l | grep -v "speed_select.rb") | crontab -
rm -f /etc/openclash/custom/speed_select.rb
# ③ 完整重启(touch + stop → enable=1 → start)
touch /etc/config/openclash && /etc/init.d/openclash stop && sleep 3 && \
uci set openclash.config.enable=1 && uci commit openclash && /etc/init.d/openclash start
# ④ 验证:组结构回到 3 个(便宜机场 / 故障转移 / 自动选择),github 经代理 200
备份位置与台账:openclash-apac-20261001/backup/(含 README.md);
远端就地备份 /etc/openclash/custom/clean_nodes.rb.bak.20261001-122808 与 /root/crontab.bak.20261001-122808。
回滚触发判据:github 经代理非 200/302;出口 loc 落在 HK/US/DE;组结构缺失「亚太优选」;OpenClash status 非 running。任一命中即回滚。
13.8 风险说明¶
| 风险点 | 说明 | 缓解 |
|---|---|---|
| 带宽门槛抖动导致误切欧美 | 机场节点带宽波动大(同一节点实测 1.58–4.28 MB/s),门槛若贴近实测值会来回切 | 门槛取 1 MB/s(只拦"明显不可用");且"可达但整体偏慢"时取最快者留在亚太,仅"全部测速失败"才回退全量池 |
| 固定节点期间的切换空窗 | 完整测速 30 分钟一轮 | cron 每 5 分钟一轮做零流量健康检查:固定节点不可达则立即切回「亚太优选」并把 full 置 0,使下一轮立刻重新完整测速 |
| 【v2 修正】主组不得指回 url-test 组 | v1 曾把"url-test 选中项达标"当作无需干预,主组交回「亚太优选」,结果 url-test 在两次完整测速之间按延迟切到 日本-HY2-47(延迟 161ms 最低、带宽仅 714 KB/s),把 Harbor 镜像拉取从 4.28 MB/s 拖到 ~0.2 MB/s(2026-10-01 实测回归,见 13.9) |
v2 起主组总是固定到具体节点,带宽维度真正主导;故障切换由 5 分钟健康检查承担 |
| 下游设备出网受本链路影响 | Harbor 主机(192.168.31.31)默认路由即 192.168.31.2,其全部出网走本 Clash;节点选择的变化会直接改变 Harbor 拉取速度 |
调整选优逻辑后须回归下游(见 13.9 的验证方式) |
| 测速消耗机场流量 | 每轮 3×5 MB,约 0.5–0.7 GB/天 | 参数集中在 speed_select.rb 顶部(TOPN / SPEED_BYTES / FULL_INTERVAL)可下调 |
cron 环境缺 /sbin |
最小环境下 uci 找不到,脚本预检查失败 |
脚本启动即前置标准 PATH;已用 env -i 实测通过 |
| 覆写脚本写坏导致配置不可用 | 组名冲突、悬空引用 | 上线前在配置副本上演练并校验悬空引用(本次为空);亚太池为空时自动退回 v4 行为 |
| 脚本异常 | 最坏结果是退回"纯延迟选优" | 脚本不改配置文件、只调 API;预检查不通过即退出 |
| 香港节点混入 | 将来订阅新增"中国香港"命名 | 亚太正则之外另设 HK_NAME 显式排除 |
【实测边界】「亚太候选全部测速失败 → 回退全量池」与「固定节点不可达 → 切回亚太优选」两条分支仅在逻辑上验证过,未制造真实故障复现(本地无合适手段,且制造故障会中断用户)。其余分支均已在实机跑通。
13.9 回归与修正 v2(2026-10-01 同日发现)¶
现象:下游 Harbor 镜像拉取从 1.0–2.4 MB/s 掉到 ~0.2 MB/s(4 并发下 225 KB/s)。
定位过程(三步,全部可复现):
- 确认下游是否受本链路影响:
ssh root@192.168.31.31 'ip route show default'→default via 192.168.31.2, 即 Harbor 全部出网走本 Clash;在 Harbor 上发起请求,Clash/connections中可见其连接 → 链路确认。 - 抓链路快照:
curl -s -H "Authorization: Bearer $S" http://127.0.0.1:9090/connections按metadata.sourceIP+chains归并。连接链在建立时固定,因此旧连接与新连接并存,可当"改动前后对比":
旧连接(改动前): 泰国-HY2-48 > 自动选择 > 便宜机场 ← 4.28 MB/s
新连接(改动后): 日本-HY2-47 > 亚太优选 > 便宜机场 ← 714 KB/s
- 根因:主组由「自动选择」(46 节点)改为「亚太优选」(8 节点)后,url-test 在两个池子里
各选"延迟最低",结果从
泰国-HY2-48(180 ms / 4.28 MB/s)变成日本-HY2-47(161 ms / 714 KB/s) —— 延迟只差 19 ms,带宽差 6 倍。
修正(v2,已部署验证):speed_select.rb 去掉"交回 url-test"分支,主组总是固定到带宽达标的
综合分最高节点;健康检查轮在节点不可达时额外把状态文件 full 置 0,触发下一轮立即重选。
修正后验证:--force 两次幂等输出「目标已是 泰国-HY2-48,无需切换」;
/connections 中新连接链变为 泰国-HY2-48 > 便宜机场(两层,直接固定)。
遗留判断:本次回归的触发条件是"区域池变更导致 url-test 换了个选出结果"。它同时暴露一条更普适的 结论——只要主组还指向 url-test 组,带宽维度就随时可能被延迟维度覆盖。故 v2 起不再让 url-test 参与主链路。
13.10 v3:放开区域限制 + 迟滞(2026-10-01)¶
背景:v5 把候选池限定为亚太 8 节点,是应"慢的时候切的是美国 IP"这一观察而设。但实测数据显示该观察不成立—— 美国 HY2 类节点的带宽优于部分亚太节点(美国-HY2-26/28 实测 1.83–1.86 MB/s,而日本-HY2-47 仅 688 KB/s)。 限制区域等于把好节点排除在外。经用户确认,放开区域限制。
同时查清的一件事(重要,避免再次误判):Harbor 镜像拉取慢不是本链路造成的。
- Harbor(
192.168.31.31)默认路由为192.168.31.2,但其镜像拉取走本机 socks5 隧道 (harbor-ssh监听1080,本体是到129.226.138.204:22的 ssh 长连接); - Clash
/connections中看不到这条 22 端口连接 —— TProxy 不劫持 22,隧道直连香港; - 同链路对比实测:经隧道 21 KB/s vs 经 Clash 主组 1.09 MB/s(差 50 倍)。
结论:Harbor 拉取问题在 Harbor → 香港跳板 → 上游 这条独立链路(见 frp_openvpn_tunnel/),
与 Clash 节点选择无关。
v3 变更内容:
| 项 | v5 / v2 | v3 |
|---|---|---|
| 候选池 | 亚太 8 节点 | 全部 46 节点(仅排除香港 HK_NAME) |
| 组名 | 亚太优选 / 亚太测速 |
优选 / 测速 |
| 延迟测试 | 逐节点串行调用 | 组测速接口并发(/group/{组}/delay) |
| 候选数 / 测速量 | TOPN 2 / 5 MB | TOPN 5 + 当前节点 / 3 MB(约 0.86 GB/天) |
| 切换判据 | 综合分最高即切 | 加 15% 迟滞:当前节点仍达标且差距未超阈值则保持 |
迟滞的必要性(实测):放开区域后,相邻两轮完整测速给出 美国-HY2-26(1.86 MB/s)与
泰国-HY2-48(2.04 MB/s)互有胜负 —— 差异在测量噪声量级,无迟滞会每 30 分钟来回切、反复中断连接。
加迟滞后连续 3 轮均为「保持」,抖动消除。
验证:组结构正确(优选/测速 各 46 成员、旧组名已清除);--force 连跑 3 次均「无需切换」;
出口 loc=TH、github=200、带宽 4.26 MB/s;完整测速耗时 17 s(组测速并发,v2 为 39 s)。
来源:
/home/rain/Documents/kvm/openwrt_bypass/docs/SOP-openclash.md| 最后更新:2026-10-01 13:17(由build/sync.py同步,请勿在站点侧修改)