跳转至

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. 目标

  1. 订阅成功拉取并设为当前配置;
  2. 节点域名走真实解析(不再返回 fake-ip);
  3. 节点延迟测试有返回值(非 Timeout);
  4. 端到端:经代理访问 github.com 返回 200;
  5. 服务开机自启。

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 应用动作(均已执行并验证)

  1. 订阅登记对齐(修复自动更新文件名漂移):
  2. openclash.sh 按订阅记录 name 存文件(/etc/openclash/config/<name>.yaml)并改写 config_path; 原记录名 ond_doller 与在用文件 smallstrawberry.yaml 不一致,周日 cron 更新会产生"更新件与在用件漂移"。
  3. 已改:记录[0] name=smallstrawberry;新增记录[1] name=fsc(两条均 enabled=1,UA clash-verge/v2.4.5)。
  4. cron 双任务保留:订阅更新 + chnroute 更新。
  5. 剔除脚本升级 v2(clean_nodes.rb):新增名字模式剔除(剩余|重置|到期|官网|流量|expire|traffic)。 原因:fsc 的信息占位节点(剩余流量:100 GB 等 3 个)server 是真实地址,无法按 server=0.0.0.0 识别, 留在组里会被 url-test 选中导致全断。首轮剔除 3 个占位节点 / 9 处组引用。
  6. 白名单改为双机场追加式:openclash_custom_fake_filter.list 与 openclash_custom_domain_dns_policy.list 同时含 +.the-best-airport.com 与 +.dnscloudcloud.top,两订阅间切换无需再改白名单(见 §8 新增机场速查)。
  7. 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 目标

  1. 延迟选优与故障切换只在亚太 8 节点池内进行,不再落到美国 / 德国;
  2. 补上带宽维度:当前出口带宽低于门槛时自动换到带宽达标的节点;
  3. 亚太整体不可用时回退全量池,保证不断网;
  4. 改动全部落在覆写脚本,订阅每周更新冲不掉。

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 新增四件事(对订阅原稿的每次启动重建,幂等):

  1. 新增 亚太优选(url-test,成员 = 亚太节点,interval:300 / tolerance:50)与 亚太测速(select,仅服务测速脚本)两个组,插在主组之后(主组必须仍是"第一个 select 组",DNS fallback 依赖它);
  2. 主组 proxies 首位插入 亚太优选;
  3. rules 首条插入 DOMAIN-SUFFIX,speed.cloudflare.com,亚太测速 —— 让测速流量走独立组,不干扰主链路,且该位置在订阅自带的 SRC-IP-CIDR,192.168.31.2/32,DIRECT 之前;
  4. 亚太池取自 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)。

定位过程(三步,全部可复现):

  1. 确认下游是否受本链路影响:ssh root@192.168.31.31 'ip route show default' → default via 192.168.31.2, 即 Harbor 全部出网走本 Clash;在 Harbor 上发起请求,Clash /connections 中可见其连接 → 链路确认。
  2. 抓链路快照: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

  1. 根因:主组由「自动选择」(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 镜像拉取慢不是本链路造成的。

  1. Harbor(192.168.31.31)默认路由为 192.168.31.2,但其镜像拉取走本机 socks5 隧道 (harbor-ssh 监听 1080,本体是到 129.226.138.204:22 的 ssh 长连接);
  2. Clash /connections 中看不到这条 22 端口连接 —— TProxy 不劫持 22,隧道直连香港;
  3. 同链路对比实测:经隧道 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 同步,请勿在站点侧修改)