博客

  • Q3000 + R4S 分流进阶:方案 B 的 Mihomo 全权 DNS 实战

    Q3000 + R4S 分流进阶:方案 B 的 Mihomo 全权 DNS 实战

    本文是《Q3000 + R4S 分流终极指南:同一套大陆 IP 分流下的两种 DNS 方案》的续篇。上一篇文章对比了方案 A 与方案 B;本文只讲后来正式投入使用的方案 B:客户端网关仍为 Q3000,DNS 仍指向 R4S,但所有 DNS 查询统一交给 Mihomo 分类。本文记录最终结构、完整请求路径、关键 YAML、境外 DNS 通道、切换步骤、验收方法、故障边界和重装恢复。

    一、为什么单独写这篇续篇

    上一篇文章完成时,方案 A 已经稳定运行,方案 B 还只是经过配置验证、尚未正式切换。因此上一篇文章给出的阶段性建议是“生产继续使用方案 A”。

    后续实际测试发生了变化:

    方案 B 已正式切换并持续运行
    淘宝、微信等国内应用正常
    Google、YouTube、TikTok 等国外服务正常
    ssh.cloud.google.com 长连接正常
    Google Play 可以正常下载并完成安装
    services.googleapis.cn 特例已由 YAML 统一处理
    境外 DNS 已增加跨地区专用通道
    旧的四千多条 dnsmasq 代理域名和自定义插件已移除
    

    所以这篇文章不是推翻上一篇,而是把上一篇中的“候选方案 B”补全为一套已经落地、可以备份和重装恢复的正式方案。

    二、先给出最终结论

    当前网络的固定结构如下:

    客户端地址:192.168.10.100-192.168.10.200
    客户端默认网关:192.168.10.1(Q3000)
    客户端 DNS:192.168.10.3(R4S dnsmasq)
    
    Q3000:
    大陆目标 IP → WAN1 直接出网
    其他目标 IP / Fake-IP → 下一跳 192.168.10.3
    
    R4S:
    dnsmasq 的唯一上游 → 127.0.0.1#1053
    
    Mihomo:
    国内域名 → 国内 DNS → 返回真实 IP
    国外域名 → 境外 DNS → 返回 Fake-IP
    特殊域名 → 按 YAML 明确指定 Fake-IP 或真实 IP
    未知域名 → 返回真实 IP,再由 Q3000 按 IP 归属兜底
    

    这套结构可以用一句话概括:

    Mihomo 负责给域名分类和发放地址,Q3000 负责根据最终目标 IP 选择道路,R4S/Nikki/Mihomo 只承载真正需要代理的业务流量。

    方案 B 并不是把国内业务流量全部交给 R4S。国内域名的 DNS 查询经过 Mihomo,但拿到大陆真实 IP 后,真正的淘宝、微信、银行 App 等业务连接仍由 Q3000 直接从 WAN1 出口。

    三、五个组件分别负责什么

    Q3000(主路由)
    ├─ WAN 拨号、NAT、DHCP、VLAN、Wi-Fi 和客户端管理
    ├─ 保存大陆 IPv4 地址库 CN4-A01~CN4-A08
    ├─ 命中大陆 IP:WAN1 直连
    └─ 未命中大陆 IP:下一跳交给 R4S
    
    R4S(硬件设备)
    └─ ImmortalWrt / OpenWrt(操作系统)
       ├─ dnsmasq(监听 192.168.10.3:53,接收全网 DNS 查询)
       ├─ Tailscale(独立的远程组网服务)
       └─ Nikki(管理和启动代理配置的插件)
          └─ Mihomo(实际执行 DNS、Fake-IP、规则和代理连接的内核)
    

    再用最通俗的比喻说明:

    • dnsmasq 是对外营业的 DNS 前台,客户端只认识它。
    • Mihomo DNS 是后台判断员,决定返回真实 IP 还是 Fake-IP。
    • Q3000 是总路口,看到最终目标 IP 后决定走 WAN1 还是 R4S。
    • Nikki 是 Mihomo 的管理系统,不负责亲自转发每一个数据包。
    • Zashboard 是观察和选择策略的控制面板,也不是转发内核。

    四、方案 B 与旧方案 A 到底差在哪里

    方案 A:dnsmasq 先用四千多个域名分类

    客户端 DNS 查询
      ↓
    R4S dnsmasq
      ├─ 命中约四千多个代理域名 → Mihomo DNS
      ├─ 命中自定义插件域名 → Mihomo DNS
      └─ 其余域名 → 国内普通 DNS
    

    这种方式的优点是普通国内 DNS 不依赖 Mihomo;缺点是分类逻辑分散在自动域名表、更新脚本、自定义插件和 YAML 多处。

    方案 B:dnsmasq 不再分类,全部交给 Mihomo

    客户端 DNS 查询
      ↓
    R4S dnsmasq
      ↓ 所有域名
    127.0.0.1:1053
      ↓
    Mihomo DNS 按 YAML 统一分类
    

    因此方案 B 中已经不再需要:

    /etc/dnsmasq.d/gwf-gfwlist.conf
    /etc/dnsmasq.d/gwf-ui.conf
    /usr/local/sbin/update-gfw-dnsmasq
    每天更新代理域名的 cron 任务
    “域名分流”LuCI 插件
    

    这些组件必须先备份、验证方案 B 后再移除,不能一上来直接删除。

    五、Q3000 的 IP 分流仍然是核心

    方案 B 只替换 DNS 分类方法,Q3000 的两条核心规则没有改变。

    规则 1:大陆 IP 直接出网

    名称:CodexCNDirect
    来源:192.168.10.100-192.168.10.200
    目标:CN4-A01~CN4-A08
    出口:WAN1
    优先级:高于下一条兜底规则
    

    规则 2:其他地址交给 R4S

    名称:CodexR4S
    来源:192.168.10.100-192.168.10.200
    目标:任意
    下一跳:192.168.10.3
    优先级:低于 CodexCNDirect
    

    八个 CN4 分组只是大陆 IPv4 数据库。真正触发分流的是 CodexCNDirect 对这些分组的引用,而不是导入 IP 数据后自动发生魔法。

    “被引用次数 1”表示有一条规则正在使用该分组,不表示访问次数。访问淘宝一万次,它仍然是 1。

    这里还有两个不能省略的前提:

    1. Q3000 的 DHCP 固定地址和动态地址池必须落在这两条规则声明的来源范围内;超出 192.168.10.100-192.168.10.200 的客户端不会自动套用这套分流,需要同步扩大来源范围或增加规则。
    2. R4S 自身地址 192.168.10.3 不能包含在 CodexR4S 的来源范围内。Mihomo 建立到代理节点的连接时,来源是 R4S;排除它以后,Q3000 才会把该连接直接从 WAN1 发出,而不会再次送回 R4S 形成循环。

    六、方案 B 的核心 DNS 配置

    下面是当前实测配置中与方案 B 有关的部分。节点、订阅、控制密钥等私密内容不在本文展示。

    dns:
      enable: true
      cache-algorithm: arc
      listen: 0.0.0.0:1053
      ipv6: false
      respect-rules: true
      enhanced-mode: fake-ip
      fake-ip-range: 198.18.0.1/16
      fake-ip-filter-mode: rule
    
      default-nameserver:
        - 223.5.5.5
        - 119.29.29.29
    
      proxy-server-nameserver:
        - https://dns.alidns.com/dns-query
        - https://doh.pub/dns-query
    
      direct-nameserver:
        - https://dns.alidns.com/dns-query
        - https://doh.pub/dns-query
    
      direct-nameserver-follow-policy: true
    
      nameserver-policy:
        "rule-set:cn_domain":
          - https://dns.alidns.com/dns-query
          - https://doh.pub/dns-query
        "rule-set:geolocation-!cn":
          - https://8.8.8.8/dns-query#境外DNS通道
          - https://1.1.1.1/dns-query#境外DNS通道
    
      nameserver:
        - https://8.8.8.8/dns-query#境外DNS通道
        - https://1.1.1.1/dns-query#境外DNS通道
    
      fake-ip-filter:
        - DOMAIN,services.googleapis.cn,fake-ip
        - RULE-SET,fakeipfilter_domain,real-ip
        - RULE-SET,cn_domain,real-ip
        - RULE-SET,geolocation-!cn,fake-ip
        - MATCH,real-ip
    

    注意:#境外DNS通道 不是 YAML 注释。它是 Mihomo 在 DNS 上游地址后使用的策略组标记,表示访问这个 DoH 上游时必须经过名为“境外DNS通道”的代理组。

    七、五类 DNS 配置是什么关系

    这些名称容易让人误以为存在简单的上下级关系。实际上,它们是 Mihomo 在不同场景调用的不同 DNS 角色。

    1. default-nameserver

    default-nameserver:
      - 223.5.5.5
      - 119.29.29.29
    

    它负责最基础的启动解析,例如先解析其他 DoH 服务器自身的域名。这里使用可在大陆直连的纯 IP DNS,避免 Mihomo 刚启动时需要“先解析 DNS 服务器,才能使用 DNS 服务器”的循环。

    2. proxy-server-nameserver

    proxy-server-nameserver:
      - https://dns.alidns.com/dns-query
      - https://doh.pub/dns-query
    

    它专门解析代理节点服务器的域名。

    假设订阅中的节点地址是:

    sg-node.example.com
    

    Mihomo 必须先知道这个节点域名对应的真实 IP,才能连上节点。这里使用阿里和腾讯的国内 DoH,即使全部代理节点当前都不可用,Mihomo 仍然可以查到各节点服务器的 IP,并在节点恢复后重新连接。

    3. direct-nameserver

    它用于直连流量需要重新解析域名时的国内 DNS。国内服务不需要绕到 Google DNS 或 Cloudflare DNS 查询。

    4. nameserver-policy

    它按域名规则集选择上游:

    命中 cn_domain → 阿里 / 腾讯 DNS
    命中 geolocation-!cn → Google / Cloudflare DNS,并经过境外DNS通道
    

    5. nameserver

    它是没有被更具体策略选中的默认业务 DNS。在本方案中使用 Google 和 Cloudflare DoH,并明确绑定“境外DNS通道”。

    所以它们不是“一个覆盖另一个”的单一优先级列表,而是:

    启动解析 → default-nameserver
    代理节点域名 → proxy-server-nameserver
    直连重解析 → direct-nameserver
    已知域名分类 → nameserver-policy
    其余普通查询 → nameserver
    

    八、fake-ip-filter-mode: rule 为什么关键

    旧配置常见的是:

    fake-ip-filter-mode: blacklist
    

    在 blacklist 模式下,fake-ip-filter 更像“不使用 Fake-IP 的例外名单”。这不适合本方案,因为我们需要明确表达四种结果:国内真实 IP、国外 Fake-IP、兼容性域名真实 IP、特殊国外服务强制 Fake-IP。

    规则模式会从上到下判断,第一条命中即停止:

    - DOMAIN,services.googleapis.cn,fake-ip
    

    Google Play 特例优先返回 Fake-IP。

    - RULE-SET,fakeipfilter_domain,real-ip
    

    局域网发现、时间同步和部分不兼容 Fake-IP 的域名返回真实 IP。

    - RULE-SET,cn_domain,real-ip
    

    已知国内域名返回真实 IP,让 Q3000 可以命中大陆 IP 库。

    - RULE-SET,geolocation-!cn,fake-ip
    

    已知国外域名返回 Fake-IP,使 Q3000 稳定地把后续业务连接交给 R4S。

    - MATCH,real-ip
    

    无法确定的新域名保守地返回真实 IP,最后由 Q3000 的大陆 IP 库判断走 WAN1 还是 R4S。

    必须再次强调:

    fake-ip-filter 只决定 DNS 回答是真实 IP 还是 Fake-IP,不直接等于 DIRECT 或 PROXY。真正的业务连接到达 R4S 后,还要由 YAML 的正式 rules: 再判断一次。

    九、国内服务的完整闭环

    下面以手机打开淘宝为例,把 DNS 阶段和真实流量阶段完整分开。

    第一阶段:查询淘宝的 IP

    淘宝 App(准备访问 taobao.com)
      ↓
    手机系统 DNS 缓存(检查是否已有未过期答案)
      ↓ 缓存没有可用答案
    手机网络系统(生成查询 taobao.com 的 DNS 数据包)
      ↓
    手机 Wi-Fi 网卡(把 DNS 数据包发给 192.168.10.3)
      ↓
    无线 AP / K2P(只负责局域网二层转发)
      ↓
    R4S 网卡(收到目标端口为 53 的 DNS 查询)
      ↓
    R4S Linux 网络系统(把 53 端口数据交给 dnsmasq)
      ↓
    dnsmasq(检查本地缓存;没有答案时把查询全部转发到 127.0.0.1:1053)
      ↓
    Mihomo DNS(检查自己的 DNS 缓存和 fake-ip-filter 规则)
      ↓
    RULE-SET,cn_domain,real-ip(确认 taobao.com 属于国内域名)
      ↓
    nameserver-policy(选择阿里 DNS 或腾讯 DNS)
      ↓
    国内上游 DNS(返回 taobao.com 的真实大陆 IP)
      ↓
    Mihomo DNS(按 real-ip 要求保留真实 IP,并缓存结果)
      ↓
    dnsmasq(缓存答案并返回手机)
      ↓
    无线 AP / K2P(把 DNS 回应转交手机)
      ↓
    手机系统 DNS 缓存(保存淘宝真实 IP)
      ↓
    淘宝 App(取得真实大陆 IP)
    

    第二阶段:真正访问淘宝

    淘宝 App(向刚取得的大陆真实 IP 发起 HTTPS / QUIC 连接)
      ↓
    手机 Wi-Fi 网卡(把业务数据交给默认网关 192.168.10.1)
      ↓
    无线 AP / K2P(把数据转交 Q3000)
      ↓
    Q3000(读取来源 IP 和目标 IP)
      ↓
    CodexCNDirect(目标 IP 命中 CN4-A01~CN4-A08)
      ↓
    Q3000 WAN1 与 NAT(把客户端私网地址转换为宽带出口地址)
      ↓
    淘宝服务器(接收请求并返回内容)
      ↓
    Q3000 连接跟踪与 NAT 还原(确认回应属于原手机连接)
      ↓
    无线 AP / K2P(把回应转交手机)
      ↓
    淘宝 App(收到页面、图片和视频数据)
    

    结论:Mihomo 参加了淘宝的 DNS 判断,但淘宝真正的大流量没有经过代理节点。

    十、国外服务的完整闭环

    下面以手机访问 YouTube 为例。

    第一阶段:查询 YouTube 的 IP

    YouTube App(准备访问 youtube.com)
      ↓
    手机系统 DNS 缓存(没有可用答案)
      ↓
    手机网络系统(把 DNS 查询发给 192.168.10.3)
      ↓
    无线 AP / K2P(转交 R4S)
      ↓
    R4S dnsmasq(把查询转给 127.0.0.1:1053)
      ↓
    Mihomo DNS(检查 fake-ip-filter)
      ↓
    RULE-SET,geolocation-!cn,fake-ip(确认是非国内域名)
      ↓
    Mihomo Fake-IP 映射表(分配并记录 198.18.x.x = youtube.com)
      ↓
    Mihomo DNS(把 Fake-IP 返回 dnsmasq)
      ↓
    dnsmasq(把 Fake-IP 返回手机)
      ↓
    手机系统 DNS 缓存(保存 198.18.x.x)
      ↓
    YouTube App(取得 Fake-IP)
    

    这里返回 Fake-IP 时,Mihomo 不需要先把 YouTube 的真实 IP 交给手机。它记录了 Fake-IP 与域名的映射,稍后再根据域名规则建立代理连接。

    第二阶段:真正播放 YouTube

    YouTube App(向 198.18.x.x 的 443 端口发起 HTTPS / QUIC 连接)
      ↓
    手机 Wi-Fi 网卡(把业务数据交给默认网关 192.168.10.1)
      ↓
    Q3000(发现 Fake-IP 不属于大陆 IPv4 库)
      ↓
    CodexR4S(把连接下一跳交给 192.168.10.3)
      ↓
    R4S Linux 防火墙 / Nikki 透明代理规则(截获应该由 Mihomo 处理的连接)
      ↓
    Mihomo 内核(从 Fake-IP 映射表还原 youtube.com)
      ↓
    Mihomo rules(匹配 youtube_domain)
      ↓
    YouTube 策略组(选择当前节点或地区组)
      ↓
    Mihomo(通过代理节点建立到 YouTube / googlevideo CDN 的真实连接)
      ↓
    R4S 默认路由(把代理节点连接发给 Q3000)
      ↓
    Q3000(来源是 R4S,不再折返给 R4S,直接从 WAN1 出网)
      ↓
    代理节点 → YouTube / googlevideo CDN
      ↓
    视频数据沿代理隧道返回 Mihomo
      ↓
    Mihomo → R4S 局域网接口 → Q3000 / K2P 交换路径 → 手机
      ↓
    YouTube App(解码并播放视频)
    

    十一、Google Play 为什么需要单独一条规则

    services.googleapis.cn 虽然以 .cn 结尾,但它属于 Google Play 的服务链。若按普通国内域名处理,它可能返回大陆真实 IP,Q3000 随即让它直连;而账号、控制接口和其他下载 CDN 又可能走代理,最终造成同一次下载出口不一致,表现为一直等待、卡在 1% 或完全无法下载。

    因此它必须放在 cn_domain 之前:

    fake-ip-filter:
      - DOMAIN,services.googleapis.cn,fake-ip
      - RULE-SET,fakeipfilter_domain,real-ip
      - RULE-SET,cn_domain,real-ip
    

    完整闭环如下:

    Google Play(查询 services.googleapis.cn)
      ↓
    dnsmasq → Mihomo DNS
      ↓
    第一条精确规则强制返回 Fake-IP
      ↓
    手机向 Fake-IP 建立连接
      ↓
    Q3000 不命中大陆 IP 库 → CodexR4S
      ↓
    R4S / Nikki / Mihomo 还原域名
      ↓
    正式 rules 命中 Google 策略组
      ↓
    Google Play 控制连接和下载连接保持一致的代理出口
      ↓
    下载进度超过 1% 并完成安装
    

    这也是方案 B 移除旧“域名分流”插件后,仍能正常使用 Google Play 的关键。

    十二、为什么增加“境外DNS通道”

    国外域名会使用:

    https://8.8.8.8/dns-query
    https://1.1.1.1/dns-query
    

    在大陆网络中,不能假设它们始终可以稳定直连。若它们跟随普通“默认代理”,DNS 能否工作就会依赖默认策略当时选中的单一地区或节点。

    因此单独建立一个只服务于境外 DNS 的跨地区组:

    proxy-groups:
      - name: 境外DNS通道
        type: fallback
        url: https://www.gstatic.com/generate_204
        interval: 60
        lazy: false
        proxies:
          - 新加坡-故转
          - 香港-故转
          - 日本-故转
          - 台湾-故转
          - 美国-故转
    

    然后把境外 DoH 明确绑定到它:

    - https://8.8.8.8/dns-query#境外DNS通道
    - https://1.1.1.1/dns-query#境外DNS通道
    

    它的运行逻辑是:

    每 60 秒主动检测候选通道
      ↓
    优先使用列表中第一个健康地区
      ↓
    当前地区失效
      ↓
    自动切换到下一个健康地区
    

    健康检查使用 Google 的 generate_204,因为这个组的任务就是访问境外 DNS。国内连通性测试只能证明节点“能联网”,不能充分证明它可以稳定访问 Google 或 Cloudflare DNS。

    这个组不放 DIRECT 作为最后候选。原因是大陆直连 8.8.8.8 或 1.1.1.1 并不是可靠兜底,反而会让故障表现变得随机。

    但也要理解它的边界:

    • 一个地区故障:自动换其他地区。
    • 一个节点故障:对应地区组可以继续选择其他健康节点。
    • 所有代理地区全部故障:境外 DNS 仍会失败。
    • Mihomo 进程停止:国内外 DNS 都会失败,因为 dnsmasq 的唯一上游就是 Mihomo。

    “境外DNS通道”提高的是代理节点局部故障时的可用性,不是让完全不可用的节点恢复工作。

    十三、为什么不会形成 DNS 死循环

    常见担心是:

    访问 Google DNS 需要代理
    代理节点域名又需要 DNS
    DNS 还没工作,节点就无法连接
    

    本方案用不同 DNS 角色拆开了这个循环:

    default-nameserver(国内纯 IP DNS)
      ↓ 先解析阿里、腾讯等 DoH 服务器自身域名
    proxy-server-nameserver(国内可直连 DoH)
      ↓ 解析各代理节点服务器域名
    代理节点取得真实 IP并建立连接
      ↓
    境外DNS通道变为可用
      ↓
    8.8.8.8 / 1.1.1.1 DoH 开始解析国外业务域名
    

    即使所有代理节点暂时故障,阿里和腾讯 DNS 仍可以继续解析节点服务器域名。节点一旦恢复,Mihomo 知道应该连接哪个真实 IP,不会因为境外 DNS 不通而永远无法恢复。

    十四、实际切换步骤

    以下步骤适用于已经运行方案 A、准备切换方案 B 的环境。全新安装可以跳过“清理方案 A 组件”,但仍应先备份。

    1. 完整备份

    至少保存:

    /etc/config/dhcp
    /etc/config/nikki
    /etc/nikki/profiles/当前配置.yaml
    /etc/nikki/run/config.yaml
    /etc/crontabs/root
    /etc/dnsmasq.d/gwf-gfwlist.conf
    /etc/dnsmasq.d/gwf-ui.conf
    /usr/local/sbin/update-gfw-dnsmasq
    域名分流插件文件
    Q3000 的 DHCP、IP 分组和两条分流规则
    

    备份必须放到 R4S 之外再保存一份,否则重刷存储卡时设备内备份也会一起消失。

    2. 修改持久 YAML

    应修改 Nikki 的持久配置,例如:

    /etc/nikki/profiles/nikki_optimized_config.yaml
    

    不要只修改:

    /etc/nikki/run/config.yaml
    

    运行配置由 Nikki 生成,重启或重新应用后可能被覆盖。

    把本文第六节的 DNS 片段合并到现有唯一的 dns: 段中。不能在文件底部再增加第二个 dns:。

    同时确保规则集存在:

    rule-providers:
      fakeipfilter_domain:
        type: http
        behavior: domain
        format: mrs
        interval: 86400
        url: "你的可信规则源/fakeip-filter.mrs"
    
      cn_domain:
        type: http
        behavior: domain
        format: mrs
        interval: 86400
        url: "你的可信规则源/cn.mrs"
    
      geolocation-!cn:
        type: http
        behavior: domain
        format: mrs
        interval: 86400
        url: "你的可信规则源/geolocation-!cn.mrs"
    

    3. 先做内核语法校验

    不要直接重启碰运气。使用设备当前 Mihomo 内核检查候选文件:

    /usr/bin/mihomo -t -d /etc/nikki/run -f /tmp/scheme-b-candidate.yaml
    

    看到类似下面的结果才可以继续:

    configuration file ... test is successful
    

    4. 应用 YAML 并重启 Nikki

    建议先准备 5~10 分钟自动回滚,再原子替换持久 YAML,最后只重启 Nikki。

    确认:

    /etc/init.d/nikki status
    

    应返回:

    running
    

    5. 让 dnsmasq 全部转给 Mihomo

    LuCI 路径:

    网络 → DHCP/DNS → 转发 → DNS 转发
    

    只保留:

    127.0.0.1#1053
    

    对应的 UCI 操作为:

    uci -q delete dhcp.@dnsmasq[0].server
    uci add_list dhcp.@dnsmasq[0].server='127.0.0.1#1053'
    uci set dhcp.@dnsmasq[0].noresolv='1'
    uci commit dhcp
    /etc/init.d/dnsmasq restart
    

    截图中看到 127.0.0.1#1053,就表示客户端查询先到 dnsmasq,再统一转给 Mihomo 的 1053 端口。

    6. 验证后再移除方案 A 组件

    先确认淘宝、Google、Google Play 等全部正常,再删除旧四千域名文件、更新脚本、定时任务和自定义插件。删除前必须保留可恢复副本。

    十五、完整验收方法

    1. DNS 返回类型

    在 R4S 上执行:

    nslookup taobao.com 127.0.0.1
    nslookup google.com 127.0.0.1
    nslookup services.googleapis.cn 127.0.0.1
    

    预期结果:

    taobao.com → 真实大陆 IP
    google.com → 198.18.x.x
    services.googleapis.cn → 198.18.x.x
    

    建议再测试:

    baidu.com → 真实大陆 IP
    youtube.com → 198.18.x.x
    

    2. 境外 DNS 通道

    在 Zashboard 的“代理”页面检查:

    境外DNS通道
    类型:Fallback
    状态:Alive
    当前:某个健康地区组
    

    在“连接”页面检查 8.8.8.8 和 1.1.1.1,代理链应包含:

    当前节点 → 地区手动/自动组 → 地区故转组 → 境外DNS通道
    

    3. 应用验收

    □ 淘宝首页、搜索、图片和视频正常
    □ 微信消息、朋友圈图片和小程序正常
    □ 国内银行及生活服务 App 正常
    □ Google 搜索正常
    □ YouTube 首页、缩略图和视频播放正常
    □ TikTok 正常
    □ ssh.cloud.google.com 能完成长连接
    □ Google Play 下载超过 1% 并完成安装
    □ Tailscale 子网访问和出口节点正常
    

    Google Play 不能只测试打开商店首页,必须实际下载并完成一个应用。

    十六、常见现象与排查

    1. Zashboard 中来源为什么都是 192.168.10.1

    Q3000 与 R4S 当前位于同一 LAN。Q3000 把非大陆流量折返给同网段 R4S 时会做连接转换,因此 Mihomo 看到的来源通常是 Q3000,而不是真实手机或电脑 IP。

    这不影响分流和速度,只影响按客户端观察。若一定要保留真实来源,需要为 Q3000 与 R4S 建立独立传输网段,使用第二根网线或 VLAN;那是另一项架构改造,不是方案 B 本身的问题。

    2. 电脑访问 YouTube,为什么 R4S 看不到 YouTube 代理链

    先检查电脑是否开启了 Clash Verge 等本机代理。

    浏览器 → PC Clash → 代理节点
    

    这种情况下,YouTube 已被电脑本机接管,没有把原始 YouTube 连接交给 R4S。R4S 面板只能看到 PC Clash 连接其节点的加密连接,不会显示 YouTube → 节点。

    3. 手机与安卓模拟器表现为什么可能不同

    它们可能具有不同 DNS 缓存、QUIC 连接、Google 服务框架状态和既有长连接。修改 DNS 后应关闭并重新打开目标 App;必要时断开 Wi-Fi 后重新连接。不要把一次旧连接的结果当成新配置的最终判断。

    4. 为什么页面出现 iosapps.itunes.apple.com

    这是 Apple App Store 或系统内容下载域名,不是 YouTube 视频域名。判断大流量属于谁,必须同时看主机名、规则、代理链和实际设备状态,不能只凭下载速度猜测。

    5. 专用应用策略为什么偶尔显示“默认代理”

    Mihomo 的正式 rules: 从上到下匹配,第一条命中就停止。如果一个宽泛规则集排在 YouTube、Apple、Google 等专用规则之前,它可能提前接走连接。

    需要专用策略组严格生效时,应把更具体的规则放在更宽泛的规则之前,并在修改后重新运行 Mihomo 语法校验和应用测试。

    十七、方案 B 的故障边界

    Mihomo 正常,某个代理节点故障

    国内 DNS → 阿里 / 腾讯 DNS → 正常
    国内真实流量 → Q3000 WAN1 → 正常
    境外 DNS → 境外DNS通道自动切换地区
    

    全部代理节点故障,但 Mihomo 仍在运行

    国内 DNS → 仍可由国内上游解析
    国内真实流量 → 仍可由 Q3000 WAN1 直连
    境外 DNS / 国外代理访问 → 失败
    代理节点域名 → 仍可由 proxy-server-nameserver 解析
    

    Mihomo 进程停止

    dnsmasq 仍监听 192.168.10.3:53
    但唯一上游 127.0.0.1:1053 不可用
    国内外新域名都无法解析
    已有缓存和既有长连接可能暂时继续工作
    

    R4S 断电或网线断开

    客户端 DNS 固定为 192.168.10.3,所以新域名都无法解析。这个物理故障不属于 DNS 规则能够解决的范围。

    因此方案 B 的真实取舍是:

    配置集中、覆盖完整、重装简单,但 Mihomo 成为全网 DNS 的关键服务。

    十八、备份、回退与重装恢复

    方案 B 的最小恢复清单

    1. 安装 ImmortalWrt / OpenWrt 与 Nikki
    2. 导入已经验证过的 Mihomo YAML
    3. 确认 Mihomo DNS 监听 0.0.0.0:1053
    4. dnsmasq 唯一上游设置为 127.0.0.1#1053
    5. 确认 fake-ip-filter-mode 为 rule
    6. 确认 services.googleapis.cn 位于 cn_domain 之前
    7. 恢复境外DNS通道
    8. 确认 Q3000 八个 CN4 分组与两条分流规则仍存在
    9. 完成 DNS、国内应用、国外应用和 Google Play 验收
    

    回退到方案 A 时需要恢复

    旧 Nikki YAML
    旧 /etc/config/dhcp
    四千多条 dnsmasq 代理域名文件
    自定义域名例外文件
    自动更新脚本与 cron 任务
    域名分流 LuCI 插件
    

    最可靠的做法不是现场凭记忆重建,而是提前准备一键备份、一键应用和一键回退脚本,并在执行网络修改前设置定时自动回滚。

    十九、日常维护建议

    每月检查

    □ Nikki 与 Mihomo 正常运行
    □ 127.0.0.1:1053 正常监听
    □ 境外DNS通道状态为 Alive
    □ taobao.com 返回真实 IP
    □ google.com 返回 Fake-IP
    □ services.googleapis.cn 返回 Fake-IP
    □ Google Play 实际下载正常
    □ Q3000 的 CN4 分组仍被 CodexCNDirect 引用
    

    更新 YAML 或订阅后检查

    □ 节点域名仍能被 proxy-server-nameserver 解析
    □ 地区组名称没有改变
    □ 境外DNS通道引用的五个地区组仍存在
    □ 应用专用规则没有被更宽泛规则提前截获
    □ Nikki 混入设置没有覆盖 YAML 中的 DNS 配置
    

    大陆 IPv4 库

    当前八个 CN4 分组是保存在 Q3000 内部的地址数据,访问时不依赖外部脚本。但一次性导入的快照不会自动跟随地址分配变化,长期使用应增加定期同步、差异检查和自动备份。

    二十、可直接交给 Codex 的提示词

    1. 切换方案 B

    请先完整备份 Q3000 与 R4S 当前配置,并准备定时自动回滚。保持客户端网关 192.168.10.1、DNS 192.168.10.3,以及 Q3000 的 CodexCNDirect/CodexR4S 规则不变。把 R4S dnsmasq 的唯一上游改为 127.0.0.1#1053,并在现有 Nikki YAML 的唯一 dns: 段中启用 fake-ip-filter-mode: rule。国内域名返回 real-ip,非中国域名返回 fake-ip,services.googleapis.cn 必须在 cn_domain 前强制 fake-ip。增加跨地区“境外DNS通道”,让 8.8.8.8 和 1.1.1.1 DoH 明确经过该组。先用当前 Mihomo 内核校验候选配置,应用后验证淘宝、Google、Google Play 和实际代理链;全部通过后再移除旧四千域名、更新任务和域名分流插件。
    

    2. 诊断国内应用变慢

    请保持现有 Q3000 + R4S 方案 B 不变,分别检查 DNS 阶段和真实业务流量阶段。确认目标国内域名是否命中 cn_domain 并返回真实大陆 IP,Q3000 是否命中 CodexCNDirect,真实流量是否从 WAN1 直出。不要只根据 Zashboard 是否出现连接判断,也不要未经验证修改节点或全局 DNS。
    

    3. 诊断国外服务打不开

    请检查该服务实际使用的主域名、子域名、CNAME 和 CDN 域名;确认 DNS 返回 Fake-IP 还是实际 IP,Q3000 是否把连接送到 R4S,Mihomo 的正式 rules 命中了哪个规则集和策略组,境外DNS通道是否健康。对比 PC Clash 与 R4S 使用同一节点时的 DNS、QUIC、IPv6、MTU 和代理链,不要先假设是节点问题。
    

    4. 重装后恢复

    请在全新 R4S 上恢复方案 B:安装 Nikki,导入已验证 YAML,确认 Mihomo DNS 监听 1053,把 dnsmasq 唯一上游设置为 127.0.0.1#1053,验证 domestic real-ip、foreign fake-ip、services.googleapis.cn fake-ip 和境外DNS通道。保持 Q3000 的大陆 IP 分流规则不变。所有敏感密钥、订阅和节点凭据从备份恢复,不要输出到日志或文章。
    

    二十一、最终总结

    方案 B 最容易被误解为“所有流量都经过 Mihomo”。准确说法是:

    所有 DNS 查询都经过 Mihomo 分类
    但不是所有真实业务流量都经过 Mihomo
    

    国内服务:

    DNS → R4S dnsmasq → Mihomo → 国内 DNS → 真实大陆 IP
    业务流量 → Q3000 → CodexCNDirect → WAN1
    

    国外服务:

    DNS → R4S dnsmasq → Mihomo → Fake-IP
    业务流量 → Q3000 → CodexR4S → R4S/Nikki/Mihomo → 代理节点
    

    特殊服务:

    services.googleapis.cn → YAML 第一条规则强制 Fake-IP → Google 策略组
    

    境外 DNS:

    8.8.8.8 / 1.1.1.1 DoH → 境外DNS通道 → 跨地区自动切换
    

    整套方案真正稳定的原因,不是某一份域名名单足够长,而是每层职责都很清楚:

    • Mihomo 负责 DNS 分类、Fake-IP 映射和代理规则。
    • Q3000 负责根据大陆 IP 数据库选择真实道路。
    • dnsmasq 负责为全网客户端提供统一 DNS 入口。
    • Nikki 负责持久配置和 Mihomo 生命周期。
    • 境外DNS通道负责降低单一地区或节点故障对国外解析的影响。

    理解这五层之后,遇到问题就可以准确判断它发生在“域名解析、IP 选路、透明接管、代理规则还是节点连接”,不再把所有故障都笼统归结为“网络慢”或“节点不行”。

  • Q3000 + R4S 分流终极指南:同一套大陆 IP 分流下的两种 DNS 方案

    Q3000 + R4S 分流终极指南:同一套大陆 IP 分流下的两种 DNS 方案

    客户端默认网关使用 Q3000 192.168.10.1,DNS 使用 R4S 192.168.10.3。Q3000 根据大陆 IPv4 库决定国内流量直接走 WAN,其余流量交给 R4S/Nikki/Mihomo。本文完整比较两种 DNS 子方案:已经验证运行的“dnsmasq 选择性转发”,以及配置更集中的“Mihomo 全权解析”。

    一、先给出结论

    本文讨论的不是两套完全不同的网络,而是同一个大方案下的两种 DNS 实现。

    大方案始终不变:

    客户端 IP:192.168.10.100-192.168.10.200
    默认网关:192.168.10.1(Q3000)
    DNS:192.168.10.3(R4S dnsmasq)
    
    Q3000 判断目标 IP:
    大陆 IP → WAN1 直接出网
    其他 IP → 下一跳 192.168.10.3 → R4S/Nikki/Mihomo
    

    两种 DNS 子方案如下:

    子方案 DNS 的第一判断者 国内 DNS 是否依赖 Mihomo 手工维护内容 当前状态
    A:dnsmasq 选择性转发 dnsmasq 否 services.googleapis.cn 一条例外 已验证,正在使用
    B:Mihomo 全权解析 Mihomo 是 Nikki YAML 可行,但尚未切换

    当前更推荐方案 A。它已经解决淘宝、微信、Google、YouTube、Google SSH、Muse、Google Play 等实际问题,而且 Nikki/Mihomo 单独停止时,普通国内 DNS 仍可工作。

    方案 B 的优点是配置集中、重装项目更少;代价是 Mihomo 会成为全网 DNS 的单点。它适合愿意为配置统一性接受这一故障范围的人。

    二、先分清 Q3000、R4S、Nikki、Mihomo 和 dnsmasq

    Q3000(主路由)
    ├─ 负责 WAN 拨号和 NAT
    ├─ 负责 DHCP、Wi-Fi、VLAN 和客户端管理
    ├─ 保存大陆 IPv4 分组
    └─ 根据目标 IP 决定 WAN1 直连或下一跳 R4S
    
    R4S(硬件设备)
    └─ ImmortalWrt/OpenWrt(操作系统)
       ├─ dnsmasq(监听 192.168.10.3:53,接收客户端 DNS 查询)
       ├─ Tailscale(独立的组网服务)
       └─ Nikki(代理管理插件)
          └─ Mihomo(真正执行 DNS、Fake-IP、规则匹配和代理连接的核心)
    

    用通俗的话说:

    • Q3000 是总路口,负责决定真实业务流量往哪里走。
    • dnsmasq 是 DNS 前台,负责接收客户端“这个域名对应什么 IP”的问题。
    • Nikki 是管理 Mihomo 的控制系统,不是代理内核本身。
    • Mihomo 才是代理内核,也可以提供 DNS 和 Fake-IP 服务。
    • Zashboard 只是 Mihomo 的控制面板,不负责实际转发。

    “数据包到达 R4S”不等于“已经使用代理节点”。数据包还要被 Nikki 的透明代理规则接管,再由 Mihomo 根据 YAML 规则决定使用代理或 DIRECT。

    三、Q3000 的大陆 IP 分流如何触发

    1. 大陆 IP 数据放在哪里

    Q3000 中保存了约 6200 条大陆 IPv4 网段。为了适应设备的对象容量,它们被拆成八个 IP 分组:

    CN4-A01
    CN4-A02
    CN4-A03
    CN4-A04
    CN4-A05
    CN4-A06
    CN4-A07
    CN4-A08
    

    这些分组只是“地址资料库”,本身不会主动改变流量方向。

    2. 真正引用 IP 库的是两条规则

    Q3000 当前的核心规则是:

    优先级 0:CodexCNDirect
    来源地址:192.168.10.100-192.168.10.200
    目标地址:CN4-A01~CN4-A08
    动作:WAN1 直接出网
    
    优先级 1:CodexR4S
    来源地址:192.168.10.100-192.168.10.200
    目标地址:任意
    动作:下一跳 192.168.10.3
    

    客户端每建立一个连接,Q3000 都会用数据包中的“来源 IP”和“目标 IP”匹配规则:

    先检查 CodexCNDirect
      ↓
    目标 IP 属于大陆 IPv4 库
      → WAN1 直接出网
    
    目标 IP 不属于大陆 IPv4 库
      ↓
    继续检查 CodexR4S
      → 下一跳交给 R4S
    

    匹配工作由 Q3000 自己的路由引擎实时完成,不需要电脑脚本持续运行。此前的脚本只负责下载 IP 库、创建八个对象并建立两条引用规则。

    3. “被引用次数 1”不是访问次数

    IP 分组中的“被引用次数”表示有多少条配置规则正在使用这个分组:

    CN4-A01 被 CodexCNDirect 引用
    → 被引用次数为 1
    

    访问淘宝一万次仍然是 1。只有再创建一条规则并引用 CN4-A01,它才会变成 2。真正的流量和连接数量应在 Q3000 的终端监控、连接监控或流量统计中查看。

    四、一次上网请求其实分为两个阶段

    很多误解来自把 DNS 查询和网页流量混成一件事。实际上每次访问通常包含两个阶段。

    第一阶段是查询地址:

    “taobao.com 对应哪个 IP?”
    

    第二阶段才是真正访问:

    “向刚才得到的 IP 建立 TCP、TLS、QUIC 或其他连接。”
    

    DNS 服务器只负责回答地址,不承担网页、视频和下载数据。默认网关只处理真正的数据连接,不负责替客户端理解域名。

    下面分别展开两种 DNS 子方案。

    五、方案 A:dnsmasq 选择性转发

    这是当前已经验证、正在使用的方案。

    1. 核心结构

    所有客户端都向 192.168.10.3:53 查询 DNS,但 dnsmasq 会先分类:

    客户端 DNS 查询
      ↓
    R4S dnsmasq
      ├─ 命中自动代理域名表
      │    → 127.0.0.1:1053 → Mihomo DNS
      │
      ├─ 命中自定义插件例外
      │    → 127.0.0.1:1053 → Mihomo DNS
      │
      └─ 没有命中
           → 223.5.5.5 / 223.6.6.6
           → 返回真实 IP
    

    自动代理域名表来自 Loyalsoldier 的 gfw.txt,当前约 4393 条,通过以下程序每天更新:

    更新程序:/usr/local/sbin/update-gfw-dnsmasq
    定时任务:每天 04:17
    生成文件:/etc/dnsmasq.d/gwf-gfwlist.conf
    

    更新程序会拒绝安装少于 3000 条的不完整列表。因此这四千多条不需要人工逐条维护。

    自定义插件只维护自动列表没有收录的特殊例外:

    服务 → 域名分流
    当前只保留:services.googleapis.cn
    数据文件:/etc/dnsmasq.d/gwf-ui.conf
    

    2. 国内请求:第一阶段,DNS 查询

    以手机打开淘宝为例:

    淘宝 App(准备访问 taobao.com)
      ↓
    手机系统 DNS 缓存(检查是否已有未过期答案)
      ↓ 缓存没有可用答案
    手机网络系统(生成查询 taobao.com 的 DNS 数据包)
      ↓
    手机 Wi-Fi 网卡(把 DNS 数据包发送给 DNS 服务器 192.168.10.3)
      ↓
    无线 AP / K2P(只负责交换和转发局域网数据)
      ↓
    R4S 网卡(收到目标端口为 53 的 DNS 查询)
      ↓
    R4S Linux 网络系统(把本机 53 端口查询交给 dnsmasq)
      ↓
    dnsmasq(检查本地缓存、Hosts、自动代理域名表和插件例外)
      ↓ taobao.com 没有命中代理域名表
    dnsmasq(把查询发给默认上游 223.5.5.5 或 223.6.6.6)
      ↓
    R4S 默认路由(把上游 DNS 查询交给 Q3000 192.168.10.1)
      ↓
    Q3000 WAN(将查询发送到互联网中的国内 DNS)
      ↓
    国内上游 DNS(返回 taobao.com 的真实 IP)
      ↓
    Q3000(收到 DNS 回应并转回 R4S)
      ↓
    R4S dnsmasq(缓存答案,并生成发往手机的 DNS 回应)
      ↓
    无线 AP / K2P(把局域网回应转交手机)
      ↓
    手机系统 DNS 缓存(保存 taobao.com 的真实 IP)
      ↓
    淘宝 App(取得真实大陆 IP)
    

    这一阶段 R4S 参与了 DNS,但 Mihomo 没有参与。

    3. 国内请求:第二阶段,真实业务流量

    淘宝 App(向刚才得到的真实大陆 IP 发起 HTTPS/QUIC 连接)
      ↓
    手机网络系统(发现目标不在本地网段,需要交给默认网关)
      ↓
    手机 Wi-Fi 网卡(把真实业务数据发给默认网关 192.168.10.1)
      ↓
    无线 AP / K2P(把数据转交 Q3000)
      ↓
    Q3000(读取来源 IP 和目标 IP)
      ↓
    CodexCNDirect(发现目标 IP 位于 CN4-A01~CN4-A08)
      ↓
    Q3000 WAN1 与 NAT(把客户端私网地址转换成宽带出口地址)
      ↓
    运营商网络(把连接送到淘宝服务器)
      ↓
    淘宝服务器(返回网页、图片、视频或接口数据)
      ↓
    Q3000 连接跟踪与 NAT 还原(确认回应属于原客户端)
      ↓
    无线 AP / K2P(把回应转交手机)
      ↓
    淘宝 App(收到真实业务数据)
    

    真正的淘宝流量没有进入 R4S、Nikki 或 Mihomo。

    4. 国外请求:第一阶段,DNS 查询

    以手机打开 Google 为例:

    浏览器或 Google App(准备访问 google.com)
      ↓
    手机系统 DNS 缓存(没有可用答案)
      ↓
    手机网络系统(生成查询 google.com 的 DNS 数据包)
      ↓
    手机 Wi-Fi 网卡(发送给 DNS 服务器 192.168.10.3)
      ↓
    无线 AP / K2P(转交 R4S)
      ↓
    R4S 网卡与 Linux 网络系统(把 53 端口查询交给 dnsmasq)
      ↓
    dnsmasq(发现 google.com 命中自动代理域名表)
      ↓
    dnsmasq(把查询转交本机 127.0.0.1:1053)
      ↓
    Mihomo DNS(检查 DNS 缓存、nameserver-policy 和 Fake-IP 规则)
      ↓
    Mihomo Fake-IP 映射表(为 google.com 分配并记录 198.18.x.x)
      ↓
    Mihomo DNS(把 198.18.x.x 返回给 dnsmasq)
      ↓
    dnsmasq(把 Fake-IP 回答发送给手机)
      ↓
    手机系统 DNS 缓存(记录 google.com = 198.18.x.x)
      ↓
    浏览器或 Google App(取得 Fake-IP)
    

    Fake-IP 不是 Google 的真实服务器地址,而是 Mihomo 给该域名发放的内部号码牌。

    5. 国外请求:第二阶段,真实业务流量

    浏览器或 Google App(向 198.18.x.x:443 发起 HTTPS/QUIC 连接)
      ↓
    手机网络系统(把连接交给默认网关 192.168.10.1)
      ↓
    无线 AP / K2P(转交 Q3000)
      ↓
    Q3000(检查目标 IP)
      ↓
    CodexCNDirect(198.18.x.x 不属于大陆 IP 库,因此不匹配)
      ↓
    CodexR4S(兜底匹配,将连接的下一跳设为 192.168.10.3)
      ↓
    R4S 网卡(收到 Q3000 转来的业务连接)
      ↓
    Nikki 防火墙与透明代理规则(把连接交给 Mihomo)
      ↓
    Mihomo Fake-IP 映射表(查出 198.18.x.x 对应 google.com)
      ↓
    Mihomo 规则引擎(命中 Google 规则和对应策略组)
      ↓
    Mihomo 代理节点(建立加密代理连接)
      ↓
    R4S 默认路由(把代理节点连接交给 Q3000)
      ↓
    Q3000(发现来源是 R4S,不再送回 R4S,直接从 WAN1 出网)
      ↓
    代理节点(代表家庭网络访问 Google 真实服务器)
      ↓
    返回数据沿代理隧道回到 R4S/Mihomo
      ↓
    Mihomo(解密并交还原始客户端连接)
      ↓
    Q3000 / 局域网交换路径(把回应送回手机)
      ↓
    浏览器或 Google App(收到 Google 内容)
    

    6. Google Play 特例的完整流程

    services.googleapis.cn 是国外服务使用中国域名和大陆地址的特殊情况。若按普通国内域名处理,它会返回类似 211.95.34.139 的大陆真实 IP,Q3000 随即直接走 WAN,导致 Google Play 的控制连接和下载连接使用不同出口,下载卡在 1%。

    插件中加入该域名后:

    Google Play(查询 services.googleapis.cn)
      ↓
    R4S dnsmasq(命中插件自定义规则)
      ↓
    Mihomo DNS 1053(返回 198.18.x.x Fake-IP)
      ↓
    手机(向 Fake-IP 发起 Play 下载连接)
      ↓
    Q3000(Fake-IP 不属于大陆 IP,交给 R4S)
      ↓
    Nikki/Mihomo(还原 services.googleapis.cn 并使用 Google 代理策略)
      ↓
    Google Play(正常高速下载)
    

    这一条已经经过多次实际测试:删除就无法下载,恢复后立即正常。

    7. 方案 A 的故障边界

    Mihomo停止,但 R4S 和 dnsmasq 正常
    → 普通国内 DNS 仍可通过 223.5.5.5/223.6.6.6 解析
    → 国内真实流量仍由 Q3000 直接出网
    → 自动代理名单和插件例外无法正常使用
    
    整个 R4S断电或 dnsmasq停止
    → 客户端配置的 DNS 192.168.10.3 不可用
    → 国内外新域名都无法正常解析
    

    因此方案 A 只能隔离“Mihomo 进程故障”,不能抵抗“R4S 整机故障”。

    六、方案 B:所有 DNS 全权交给 Mihomo

    这是一套可行但尚未正式切换的替代方案。它不是让所有真实流量都经过 R4S,而是让所有 DNS 查询都先经过 Mihomo。

    1. 核心结构

    客户端所有 DNS 查询
      ↓
    R4S dnsmasq 192.168.10.3:53
      ↓ 无条件转发
    Mihomo DNS 127.0.0.1:1053
      ↓
    Mihomo 根据规则返回真实 IP 或 Fake-IP
      ↓
    Q3000 根据最终 IP 决定 WAN1 或 R4S
    

    Mihomo不是权威 DNS 数据库。它仍然需要向上游 DNS 查询,只是由它选择上游并决定最终向客户端返回真实 IP 还是 Fake-IP:

    cn_domain → 阿里 DNS / 腾讯 DNS 等国内上游
    geolocation-!cn → Google / Cloudflare 等国外 DoH 上游
    

    2. 为什么不能直接把所有 DNS 转给当前 Mihomo

    当前配置使用:

    fake-ip-filter-mode: blacklist
    

    在 blacklist 模式下,fake-ip-filter 是“不返回 Fake-IP”的例外名单。命中列表返回真实 IP,其他域名默认返回 Fake-IP。

    如果不修改 YAML 就把所有 DNS 都转给 Mihomo,很多没有进入例外名单的国内域名也会获得 Fake-IP,随后被 Q3000 交给 R4S,国内直连优势会被削弱。

    因此需要改成 Mihomo 支持的规则模式:

    dns:
      fake-ip-filter-mode: rule
      fake-ip-filter:
        - DOMAIN,services.googleapis.cn,fake-ip
        - RULE-SET,fakeipfilter_domain,real-ip
        - RULE-SET,cn_domain,real-ip
        - RULE-SET,geolocation-!cn,fake-ip
        - MATCH,real-ip
    

    注意:这段内容应替换现有 dns: 中的 fake-ip-filter-mode 和 fake-ip-filter,不能再增加第二个 dns:。

    持久配置应修改:

    /etc/nikki/profiles/nikki_optimized_config.yaml
    

    不要只修改:

    /etc/nikki/run/config.yaml
    

    后者是 Nikki 生成的运行配置,重启或重新应用配置后可能被覆盖。

    3. 每条规则是什么意思

    - DOMAIN,services.googleapis.cn,fake-ip
    

    精确匹配 Google Play 特例,并在国内域名规则之前强制返回 Fake-IP。

    - RULE-SET,fakeipfilter_domain,real-ip
    

    局域网、设备发现、时间同步等不适合 Fake-IP 的域名返回真实 IP。

    - RULE-SET,cn_domain,real-ip
    

    已知国内域名返回真实 IP,让 Q3000 有机会命中大陆 IP 库并直接出 WAN。

    - RULE-SET,geolocation-!cn,fake-ip
    

    已知非中国域名返回 Fake-IP,确保 Q3000 把连接交给 R4S。

    - MATCH,real-ip
    

    无法按域名判断的新域名返回真实 IP,最后由 Q3000 的大陆 IP 库判断走向。

    当前 Mihomo 为 v1.19.27,支持 fake-ip-filter-mode: rule;现有 fakeipfilter_domain、cn_domain 和 geolocation-!cn 也都是可用于 DNS 规则的 domain 类型规则集。

    这里必须分清两层规则:fake-ip-filter 只决定 DNS 阶段向客户端返回 Fake-IP 还是真实 IP,并不直接等于 PROXY 或 DIRECT。真实业务连接到达 R4S 后,仍由 YAML 中正式的 rules: 从上到下匹配,并由对应策略组决定使用代理节点还是直连。本文让国外域名优先取得 Fake-IP,是为了让 Q3000 稳定地把连接送到 R4S,同时让 Mihomo 能从 Fake-IP 映射表还原域名;最终代理动作仍属于第二阶段。

    4. 国内请求:第一阶段,DNS 查询

    淘宝 App(准备访问 taobao.com)
      ↓
    手机系统 DNS 缓存(没有可用答案)
      ↓
    手机网络系统(向 192.168.10.3 发送 DNS 查询)
      ↓
    无线 AP / K2P(转交 R4S)
      ↓
    R4S dnsmasq(不再检查四千域名,所有查询都转给 127.0.0.1:1053)
      ↓
    Mihomo DNS(从上到下检查 fake-ip-filter 规则)
      ↓
    RULE-SET,cn_domain,real-ip(确认 taobao.com 属于国内域名)
      ↓
    nameserver-policy(选择阿里 DNS 或腾讯 DNS 等国内上游)
      ↓
    国内上游 DNS(返回淘宝真实大陆 IP)
      ↓
    Mihomo DNS(因为规则要求 real-ip,所以把真实 IP 返回给 dnsmasq)
      ↓
    dnsmasq(把真实 IP 返回手机)
      ↓
    手机系统 DNS 缓存(保存淘宝真实 IP)
      ↓
    淘宝 App(取得真实大陆 IP)
    

    这里国内 DNS 也依赖 Mihomo,但真实业务流量仍然可以绕过 R4S。

    5. 国内请求:第二阶段,真实业务流量

    淘宝 App(向真实大陆 IP 发起连接)
      ↓
    手机默认网关 192.168.10.1
      ↓
    Q3000(目标 IP 命中 CN4-A01~CN4-A08)
      ↓
    CodexCNDirect
      ↓
    WAN1 与 NAT
      ↓
    淘宝服务器
      ↓
    Q3000 连接跟踪与 NAT 还原
      ↓
    手机淘宝 App
    

    Mihomo只参加了 DNS,没有承载淘宝业务流量。

    6. 国外请求:第一阶段,DNS 查询

    浏览器或 Google App(查询 google.com)
      ↓
    手机 DNS → R4S dnsmasq
      ↓
    dnsmasq(全部转给 Mihomo DNS 1053)
      ↓
    Mihomo DNS(检查规则)
      ↓
    RULE-SET,geolocation-!cn,fake-ip(确认属于非中国域名)
      ↓
    Mihomo Fake-IP 映射表(生成并记录 198.18.x.x = google.com)
      ↓
    Mihomo DNS → dnsmasq → 手机
      ↓
    手机系统 DNS 缓存(保存 Fake-IP)
    

    7. 国外请求:第二阶段,真实业务流量

    浏览器或 Google App(向 198.18.x.x 发起连接)
      ↓
    手机默认网关 192.168.10.1
      ↓
    Q3000(Fake-IP 不属于大陆 IP 库)
      ↓
    CodexR4S(下一跳 192.168.10.3)
      ↓
    R4S Nikki 透明代理
      ↓
    Mihomo(通过 Fake-IP 映射还原 google.com)
      ↓
    Mihomo规则引擎(选择 Google 策略组和代理节点)
      ↓
    Q3000 WAN1 → 代理节点 → Google
      ↓
    回应经代理隧道返回 Mihomo
      ↓
    Mihomo → 局域网 → 手机
    

    8. Google Play 特例:两个阶段

    第一阶段:

    Google Play(查询 services.googleapis.cn)
      ↓
    dnsmasq(全部转给 Mihomo)
      ↓
    Mihomo首先命中 DOMAIN,services.googleapis.cn,fake-ip
      ↓
    因为该规则位于 cn_domain 之前,所以强制返回 Fake-IP
      ↓
    手机取得 198.18.x.x
    

    第二阶段:

    Google Play(向 Fake-IP 建立下载连接)
      ↓
    Q3000(不命中大陆 IP 库)
      ↓
    CodexR4S → R4S/Nikki/Mihomo
      ↓
    Mihomo还原 services.googleapis.cn 并使用 Google 代理策略
      ↓
    Google Play 正常下载
    

    因此方案 B 不再需要域名分流插件维护这一条规则。

    9. 未识别域名:两个阶段

    第一阶段:

    客户端查询一个新域名
      ↓
    Mihomo没有命中 cn_domain 或 geolocation-!cn
      ↓
    MATCH,real-ip
      ↓
    Mihomo通过默认安全上游取得真实 IP
      ↓
    把真实 IP 返回客户端
    

    第二阶段:

    客户端向真实 IP 发起连接
      ↓
    Q3000检查大陆 IPv4 库
      ├─ 属于大陆 → WAN1
      └─ 不属于大陆 → R4S/Nikki/Mihomo
    

    这让 Q3000 的 IP 数据库成为域名规则没有覆盖时的最后保险。

    10. 方案 B 的故障边界

    Mihomo停止
    → dnsmasq虽然仍监听 192.168.10.3:53
    → 但唯一上游 127.0.0.1:1053 不可用
    → 国内外新域名都无法正常解析
    
    整个 R4S断电
    → 客户端 DNS 192.168.10.3 不可用
    → 国内外新域名都无法正常解析
    

    可以另做健康检查,在 Mihomo停止时临时把 dnsmasq 切回国内 DNS,从而保住国内访问;但这会增加新的自动化与回滚逻辑,不能把普通国内 DNS 与 Mihomo DNS 同时无条件配置成竞争上游,否则可能造成国外域名泄漏或解析不一致。

    七、两种 DNS 方案的本质区别

    方案 A

    dnsmasq先按域名名单决定:
    代理名单/插件例外 → Mihomo
    其他域名 → 国内普通 DNS
    
    Q3000再按最终 IP 决定:
    大陆 IP → WAN1
    其他 IP/Fake-IP → R4S
    

    方案 B

    dnsmasq把全部查询交给 Mihomo
    
    Mihomo按 YAML 决定:
    国内域名 → 真实 IP
    国外域名 → Fake-IP
    特殊域名 → 明确指定 fake-ip 或 real-ip
    未知域名 → 真实 IP
    
    Q3000再按最终 IP决定:
    大陆 IP → WAN1
    其他 IP/Fake-IP → R4S
    

    对比表

    对比项 方案 A:选择性转发 方案 B:Mihomo 全权解析
    国内 DNS dnsmasq 直接访问国内上游 Mihomo选择国内上游
    国外 DNS 自动名单命中后进入 Mihomo 全部进入 Mihomo并按规则分类
    Google Play 特例 LuCI 插件维护 YAML 第一条规则维护
    四千域名文件 需要,但自动更新 不需要
    自定义插件 需要,当前仅一条 不需要
    配置集中度 中等 高
    Mihomo停止后国内 DNS 大部分仍可用 不可用
    域名规则漏收 依赖 Q3000 非大陆 IP 兜底,但可能受普通 DNS 结果影响 MATCH real-ip 后由 Q3000 兜底
    当前实测状态 已完整验证 尚未正式切换
    推荐对象 稳定优先 配置统一优先

    八、为什么方案 A 当前更值得保留

    方案 A 看起来多了四千域名文件、更新脚本和一个插件,但实际人工维护量并不大:

    四千多条域名 → 每天自动更新
    插件 → 只维护 services.googleapis.cn 一条
    

    它最大的价值不是“域名更多”,而是把普通国内 DNS 与 Mihomo 的运行状态分开。Nikki/Mihomo 单独重启、崩溃或更新时,国内 DNS 和 Q3000 国内直连仍有机会继续工作。

    方案 B 的设计更加整洁,但整洁不等于故障范围更小。在家庭网络中,DNS 是所有应用的入口,一旦 Mihomo成为唯一 DNS 上游,它的停止会表现成“整个网络都打不开”。

    因此当前建议是:

    生产使用:继续方案 A
    研究或后续简化:先以可回滚方式测试方案 B
    

    九、重装 R4S 时分别恢复什么

    方案 A 的恢复项目

    1. 安装 Nikki 和 Mihomo 内核
    2. 导入并验证 nikki_optimized_config.yaml
    3. 恢复 dnsmasq 默认上游 223.5.5.5、223.6.6.6
    4. 安装 update-gfw-dnsmasq
    5. 恢复每天 04:17 的更新任务
    6. 生成 gwf-gfwlist.conf
    7. 安装 LuCI 域名分流插件
    8. 添加 services.googleapis.cn
    9. 检查 google.com 返回 Fake-IP
    10. 检查 services.googleapis.cn 返回 Fake-IP
    11. 检查 taobao.com 返回大陆真实 IP
    

    这些步骤适合封装成一个一键恢复脚本,不应依靠记忆手工重建。

    方案 B 的恢复项目

    1. 安装 Nikki 和 Mihomo 内核
    2. 导入包含 rule 模式 DNS 配置的 nikki_optimized_config.yaml
    3. 让 dnsmasq 的唯一上游指向 127.0.0.1#1053
    4. 检查 google.com 返回 Fake-IP
    5. 检查 services.googleapis.cn 返回 Fake-IP
    6. 检查 taobao.com 返回大陆真实 IP
    7. 确认 Q3000 的两条分流规则仍然存在
    

    方案 B 不需要四千域名更新程序和插件,但必须保证 Nikki 启动顺序、Mihomo DNS 监听和规则集下载全部正常。

    十、完整验收清单

    无论选择哪种 DNS 子方案,都应按以下顺序验收:

    DNS 层
    □ taobao.com 返回真实大陆 IP
    □ baidu.com 返回真实大陆 IP
    □ google.com 返回 198.18.x.x
    □ youtube.com 返回 198.18.x.x
    □ services.googleapis.cn 返回 198.18.x.x
    
    Q3000 分流层
    □ CodexCNDirect 已启用并引用八个 CN4 分组
    □ CodexR4S 已启用,下一跳为 192.168.10.3
    □ 客户端范围为 192.168.10.100-192.168.10.200
    
    应用层
    □ 淘宝和微信正常
    □ 国内网页打开速度正常
    □ Google 搜索正常
    □ YouTube 首页、缩略图和视频正常
    □ ssh.cloud.google.com 能建立长连接
    □ Google Play 可以下载并完成安装
    □ Muse 等曾经异常的网站正常
    □ Tailscale 子网访问和出口节点正常
    

    测试 Google Play 时,不要只看到商店首页就判断成功。应实际选择一个应用,确认下载进度超过 1% 并完成安装。

    十一、常见问题

    1. 所有流量是否第一时间经过 Nikki

    不是。客户端默认网关是 Q3000:

    大陆真实 IP → Q3000直接 WAN1
    非大陆真实 IP/Fake-IP → Q3000交给 R4S → Nikki/Mihomo
    

    两种 DNS 方案改变的是“如何得到目标 IP”,不是把国内业务流量重新强制送进 R4S。

    2. 为什么 Zashboard 中来源都是 192.168.10.1

    当前 Q3000 和 R4S 位于同一 LAN。Q3000 把连接折返给 LAN 内下一跳时会隐藏原始客户端地址,因此 Mihomo看到的来源是 Q3000。这个问题不影响分流和速度,但 Zashboard 无法按真实客户端统计。

    要保留真实来源 IP,需要为 Q3000 与 R4S 建立独立传输网段,可通过第二根网线或 VLAN 实现;这属于另一项网络架构改造,与两种 DNS 子方案无关。

    3. 客户端手动设置 8.8.8.8 会怎样

    客户端访问 8.8.8.8:53 时,目标 IP 不属于大陆地址库,Q3000 会把查询交给 R4S。若 Nikki 的透明代理 DNS 劫持正常,查询仍可能进入 Mihomo;但不应依赖客户端随意设置 DNS,标准配置仍是 192.168.10.3。

    4. 为什么 services.googleapis.cn 不能直接按 .cn 直连

    域名后缀只表示命名空间,不代表该服务在当前网络下可以完整直连。Google Play 同时使用控制域名、下载 CDN 和账号连接;如果其中一部分直连、一部分代理,同一个下载任务会产生出口不一致并卡住。

    5. Q3000 大陆 IP 库是否自动更新

    当前八个 CN4 分组是一次性导入的快照,数据保存在 Q3000 中,访问时无需外部脚本;但它不会自行跟随互联网地址分配变化。长期使用应增加定期同步与备份工具,并在更新后检查八个对象和两条规则的引用关系。

    十二、最终选择建议

    稳定性优先时:

    选择方案 A
    dnsmasq 维护自动代理域名表
    插件只保留 services.googleapis.cn
    国内 DNS 不依赖 Mihomo进程
    

    配置集中优先时:

    选择方案 B
    所有 DNS 交给 Mihomo
    YAML 使用 fake-ip-filter-mode: rule
    services.googleapis.cn 写入第一条强制 Fake-IP 规则
    接受 Mihomo成为全网 DNS 单点
    

    无论选择哪一种,真正决定国内业务流量是否绕过 R4S 的核心都不是域名列表,而是 Q3000 中的两条规则:

    大陆 IP → CodexCNDirect → WAN1
    其他 IP → CodexR4S → R4S/Nikki/Mihomo
    

    DNS 负责发放“目的地址”,Q3000 负责选择“实际道路”,R4S/Nikki/Mihomo 负责处理“需要代理的那部分连接”。把这三层职责分开理解,整套网络就不再神秘。

  • 谷歌云 (GCP) 开启 SSH Root 密码登录一键脚本(修复优化版)

    谷歌云 (GCP) 开启 SSH Root 密码登录一键脚本(修复优化版)

    很多朋友在使用谷歌云(GCP)或 AWS 等云服务器时,习惯使用 Root 账号和密码直接通过 SSH 工具(如 Xshell、FinalShell)进行连接。但在网上搜到的一些“一键开启 Root”脚本,直接复制粘贴进去却经常报错,或者执行后依然无法使用密码登录。

    这往往是因为网页排版导致的换行符断裂、脚本作者拼写遗漏,或者是云厂商强大的 cloud-init 默认配置强行覆盖了我们的修改。

    为了彻底解决这些问题,我对网络上流传的一键脚本进行了深度的语法修复与环境兼容优化。

    🚀 修复版一键脚本

    请在您的云服务器(如通过谷歌云网页端 SSH 终端)中,直接复制以下这一整段命令并回车执行:

    sudo bash -c 'set -e; f=/etc/ssh/sshd_config; cp -a "$f" "$f.bak.$(date +%Y%m%d-%H%M%S)"; mkdir -p /etc/ssh/sshd_config.d; printf "%s\n" "PermitRootLogin yes" "PasswordAuthentication yes" "KbdInteractiveAuthentication yes" "UsePAM yes" > /etc/ssh/sshd_config.d/99-enable-root-login.conf; for k in PermitRootLogin PasswordAuthentication KbdInteractiveAuthentication UsePAM; do grep -qiE "^[#[:space:]]*$k[[:space:]]+" "$f" && sed -i -E "s|^[#[:space:]]*$k[[:space:]].*|$k yes|I" "$f" || printf "\n%s yes\n" "$k" >> "$f"; done; sed -i -E "s/PasswordAuthentication[[:space:]]+no/PasswordAuthentication yes/gI" /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true; sed -i -E "s/PermitRootLogin[[:space:]]+(prohibit-password|without-password|no)/PermitRootLogin yes/gI" /etc/ssh/sshd_config.d/*.conf 2>/dev/null || true; passwd root; /usr/sbin/sshd -t; systemctl restart ssh || systemctl restart sshd || service ssh restart; echo -e "\nDone. Login with root, port 22."'

    💡 脚本优化与修复说明

    相较于原版残缺脚本,本次优化主要解决了以下几个核心问题(分条列出):

    1. 修复换行与截断错误: 修正了原版脚本中因网页复制粘贴产生的异常换行(例如配置项与文件名中间莫名被截断),确保单行命令能被系统连贯且正确地执行。
    2. 修正核心参数拼写: 原脚本漏掉了开启密码验证的核心参数 PasswordAuthentication(错写成了 Authentication)。如果缺少此项,即使开启了 Root 权限,服务器也会拒绝密码验证,现已完全补全。
    3. 清除隐形乱码字符: 清理了隐藏在原代码中的特殊空格字符(Non-breaking space)。这些肉眼看不见的字符会导致 Linux 的 Bash 终端在解析命令时直接报错崩溃。
    4. 深度兼容云厂商环境(防覆盖): 这是最关键的一步。由于 SSHD 配置文件采用“先匹配先优先生效”的原则,而谷歌云(GCP)等厂商默认会在 /etc/ssh/sshd_config.d/ 目录下生成如 50-cloud-init.conf 的文件来强制禁用密码登录。本脚本新增了批量匹配替换逻辑,强制将子目录中所有“禁止密码”或“禁止 Root”的规则改为 yes,彻底清除了这些“拦路虎”。
    5. 安全便捷的密码设置: 脚本执行到最后,会自动调用 passwd root 命令。此时终端会提示您输入并确认两次新的 Root 密码。设置完成后,脚本会自动测试配置文件语法并重启 SSH 服务,立即生效。

    使用提示: 看到终端输出 Done. Login with root, port 22. 后,您就可以使用刚刚设置的密码,通过第三方 SSH 客户端以 Root 身份登录您的云服务器了。

  • Q3000 + R4S + Nikki:国内直连、国外按需代理的分流教程(已弃用)

    Q3000 + R4S + Nikki:国内直连、国外按需代理的分流教程(已弃用)

    一套不依赖爱快付费“IP 归属地”的家庭网络方案。国内和普通境外服务由 Q3000 直接访问;只有命中代理域名表的请求才交给 R4S 上的 Nikki/Mihomo。本文既讲配置,也解释每一步为什么这样做。

    本文记录的是一套已经实际运行并解决淘宝、微信等国内应用卡顿问题的配置。它适合以下场景:

    • Q3000(爱快系统)作为主路由,负责拨号、DHCP、VLAN、Wi-Fi 和 NAT。
    • R4S 刷入 ImmortalWrt/OpenWrt,并安装 Nikki。
    • 希望国内流量尽量不进入 R4S,国外受限服务自动代理。
    • 希望保留 Tailscale 子网路由、出口节点等功能。
    • 不购买爱快“IP 归属地”功能。

    文中的地址是实际示例。照做前,请先把它们换成自己的网段。本文默认读者已经能登录 Q3000 和 R4S,不再讲网线连接、SSH 登录和 LuCI 登录。

    一、最终效果

    配置完成后,普通客户端会获得:

    项目 示例值 由谁提供
    客户端 IP 192.168.10.100 Q3000 DHCP
    默认网关 192.168.10.1 Q3000
    DNS 服务器 192.168.10.3 R4S 上的 dnsmasq
    Q3000 地址 192.168.10.1 主路由
    R4S 地址 192.168.10.3 DNS 分类器、代理入口、Tailscale 节点
    Mihomo Fake-IP 段 198.18.0.0/16 Mihomo

    最重要的一句话是:

    网关走 Q3000,DNS 问 R4S;只有 DNS 返回 198.18.x.x 的目标,Q3000 才把后续连接送到 R4S。

    因此:

    • 淘宝、微信、银行、视频网站等国内服务,通常从 Q3000 直接出 WAN。
    • 能在国内直连的普通境外网站,也从 Q3000 直接出 WAN。
    • Google、TikTok 等命中代理域名表的服务,自动交给 R4S/Nikki/Mihomo。
    • R4S 不再是所有设备的默认网关,也不再处理绝大多数国内连接。

    二、先弄懂四个组件的关系

    很多配置混乱,源头不是某个选项,而是把 R4S、Nikki、Mihomo 当成了同一个东西。它们的关系如下:

    Q3000(独立主路由)
    ├─ WAN 拨号与 NAT
    ├─ LAN/VLAN/Wi-Fi
    ├─ DHCP
    └─ 静态路由:198.18.0.0/16 → R4S
    
    R4S(硬件设备)
    └─ ImmortalWrt/OpenWrt(操作系统)
       ├─ dnsmasq:本方案的前置 DNS 分类器
       ├─ Tailscale:独立的组网服务
       └─ Nikki:OpenWrt 上的代理管理插件
          ├─ 管理配置文件和防火墙规则
          └─ 启动、停止并管理 Mihomo
             ├─ 真正执行 Fake-IP、代理规则和节点连接
             └─ 向 Zashboard 提供控制 API
    

    用通俗的话说:

    • R4S 是一台小电脑。
    • ImmortalWrt/OpenWrt 是它的操作系统。
    • Nikki 是系统里的管理工具,负责准备运行环境并叫醒 Mihomo。
    • Mihomo 才是真正执行代理和规则匹配的核心程序。
    • Zashboard 只是查看和操作 Mihomo 的网页控制台,不负责转发流量。
    • Tailscale 与 Nikki 是平级服务,不包含在 Nikki 里面。

    所以,“流量经过 R4S”并不等于“流量经过 Mihomo”;只有被 Nikki 防火墙规则接管的连接,才会进入 Mihomo。

    三、旧方案为什么会让国内 App 卡顿

    旧方案把客户端的默认网关和 DNS 都设置为 192.168.10.3:

    手机/电脑 → R4S → Nikki/Mihomo 判断 → DIRECT 或代理
    

    Mihomo 会根据 YAML 中的域名规则、IP 规则、GeoIP、规则集和最终 MATCH 判断走向。例如:

    - RULE-SET,cn_domain,DIRECT
    - RULE-SET,cn_ip,DIRECT,no-resolve
    - MATCH,PROXY
    

    这里的 DIRECT 只表示“不走代理节点”,不表示“绕开 R4S”。国内连接仍然先进入 R4S,再从 R4S 的同一个 LAN 网口送回 Q3000 出网,返回包也要经过相应路径。这种单臂旁路由折返会同时受到以下因素影响:

    • Nikki 的透明代理和 nftables 规则;
    • Fake-IP 与真实 IP 的映射;
    • DNS 劫持和 dnsmasq;
    • UDP/TCP 的不同接管方式;
    • 同一个物理接口上的来回转发;
    • 应用自身的多域名、多 IP、HTTP/3 和长连接。

    这不代表传统旁路由一定不能用,但在这套设备、固件和配置组合中,曾出现淘宝卡住、微信打不开、手机提示网络异常,而国外代理流量反而正常的现象。问题的关键是:即使最终判定为 DIRECT,国内数据包仍然已经进入 R4S 的复杂转发路径。

    四、新方案的核心原理

    新方案把“是否需要送进 R4S”的判断提前到 DNS 阶段。

    R4S 的 dnsmasq 中放入约四千多条代理域名规则。每条规则类似:

    server=/google.com/127.0.0.1#1053
    

    含义是:

    • 如果查询的域名命中列表,把 DNS 查询交给 Mihomo 的 1053 端口;
    • 如果没有命中,交给阿里公共 DNS,返回真实 IP。

    1. 国内或普通直连域名

    手机查询 taobao.com
    → R4S dnsmasq 没有命中代理域名表
    → 阿里 DNS 返回真实 IP
    → 手机连接真实 IP
    → 默认网关是 Q3000
    → Q3000 直接从 WAN 出网
    

    这个过程中,R4S 只回答了一次 DNS。真正的业务流量没有进入 R4S、Nikki 或 Mihomo。

    2. 需要代理的域名

    手机查询 google.com
    → R4S dnsmasq 命中代理域名表
    → 查询转交 Mihomo:1053
    → Mihomo 返回 198.18.x.x Fake-IP
    → 手机连接这个 Fake-IP
    → 默认网关先到 Q3000
    → Q3000 静态路由把 198.18.0.0/16 送给 R4S
    → Nikki 防火墙接管连接
    → Mihomo 恢复原域名并选择代理节点
    

    因此,并不是所有流量第一时间经过 Nikki。进入 Nikki 的主要是被标记成 Fake-IP 的连接。

    3. 能直连的境外服务

    一个域名只要没有命中代理列表,就会获得真实 IP,并由 Q3000 直接访问。它是不是“中国 IP”并不重要,所以不需要爱快付费的“IP 归属地”功能。

    这也回答了一个常见问题:国外服务不等于都要代理。可直连的境外网站不会因为 R4S 停机而失去转发路径,但如果客户端 DNS 仍指向已经停机的 R4S,新域名仍会因为无法解析而打不开。后文会用备用 Wi-Fi 解决这个故障场景。

    4. 这四千多条域名够不够

    这份表不是“全世界所有国外域名”,而是社区维护的、通常需要代理的域名集合。它的覆盖面比旧方案的完整 YAML 规则窄:

    • 旧方案:所有连接先进入 Mihomo,再由完整规则体系决定 DIRECT 或代理。
    • 新方案:只有前置域名表命中的连接进入 Mihomo,其余连接直接走 Q3000。

    优点是国内路径简单、稳定、延迟低;代价是新出现或未收录的受限域名可能被当作直连而失败。遇到漏网域名时,把它加入自定义代理域名表即可。

    五、准备工作与地址规划

    开始前至少准备:

    • Q3000 已能正常拨号上网;
    • R4S 已安装 ImmortalWrt/OpenWrt、Nikki 和 Mihomo 内核;
    • Nikki 已导入一份本身可用的订阅/YAML;
    • R4S 能使用 curl 下载文件;
    • 手边有一个完全直连的 Wi-Fi,方便配置失误时继续管理设备;
    • 已导出 Q3000 配置,并备份 R4S 的 /etc/config。

    本文地址规划:

    设备/网段 地址
    Q3000 LAN1 192.168.10.1/24
    R4S LAN 192.168.10.3/24
    Q3000 DHCP 池 192.168.10.100-192.168.10.200
    客户端网关 192.168.10.1
    客户端 DNS 192.168.10.3
    Mihomo Fake-IP 198.18.0.0/16

    **安全提醒:**修改网关、DHCP、静态路由和防火墙可能导致暂时失联。先备份,再改一项、验证一项。不要把订阅链接、节点信息、SSH 密码、API 令牌或 Mihomo 控制密钥粘贴到公开文章和公开对话中。

    六、配置 R4S 的基础网络

    R4S 使用固定地址:

    IP:192.168.10.3
    掩码:255.255.255.0
    网关:192.168.10.1
    DNS:可先用 192.168.10.1 或可靠的国内 DNS
    

    在 LuCI 中通常位于:

    网络 → 接口 → LAN → 编辑 → 常规设置
    

    R4S 不再负责给家庭主网分配地址,因此关闭 LAN 接口上的 DHCP 服务,但不要关闭 dnsmasq 服务本身。本方案仍需要 dnsmasq 在 192.168.10.3:53 回答 DNS。

    通常路径:

    网络 → 接口 → LAN → 编辑 → DHCP 服务器 → 忽略此接口
    

    确认 R4S 自身可以访问互联网:

    ping -c 3 223.5.5.5
    nslookup github.com 223.5.5.5
    

    七、配置 Nikki 与 Mihomo

    1. 先让原始 YAML 正常运行

    先在 Nikki 中导入自己的订阅或 YAML,确认节点本身可以连接。本文不提供任何订阅、节点或密钥。

    建议基础状态:

    项目 建议值
    TCP 代理方式 Redirect
    UDP 代理方式 TUN
    LAN 代理 开启
    路由器自身代理 按需开启
    Fake-IP 开启
    Fake-IP 范围 198.18.0.1/16
    Mihomo DNS 监听 127.0.0.1:1053 或配置实际使用的 1053 监听
    大陆 IP 提前绕过 关闭
    Nikki IPv4 DNS 劫持 关闭
    Nikki IPv6 DNS 劫持 关闭
    Nikki 代理配置中 TCP、UDP、DNS 劫持与 IPv4/IPv6 代理选项
    R4S 上的 Nikki 代理配置:TCP 使用 Redirect、UDP 使用 TUN,IPv4/IPv6 DNS 劫持关闭。

    “大陆 IP 提前绕过”不是绝对不可用,而是不作为本文方案的核心。本文依靠“DNS 分类 + Fake-IP 静态路由”决定哪些连接进入 R4S;再叠加一套大陆 IP 提前绕过,会增加判断层级和排错难度。

    2. 为什么要关闭 Nikki 的 DNS 劫持

    我们要让 dnsmasq 成为第一层分类器:普通域名走国内 DNS,命中列表的域名才去 Mihomo。如果开启 Nikki 的全局 DNS 劫持,所有查询可能先被重定向到 Mihomo,前置分类就失去意义。

    命令行等价设置为:

    uci set nikki.proxy.ipv4_dns_hijack='0'
    uci set nikki.proxy.ipv6_dns_hijack='0'
    uci commit nikki
    /etc/init.d/nikki restart
    

    3. OpenWrt 的“DNS 重定向”要不要关

    LuCI 中:

    网络 → DHCP/DNS → 常规 → DNS 重定向
    
    R4S 的 DHCP/DNS 常规设置中 DNS 重定向处于开启状态
    这里的 DNS 重定向属于 dnsmasq/OpenWrt,和 Nikki 的 DNS 劫持不是同一个开关。

    这个选项把经过 R4S 的客户端 DNS 查询强制拉回 R4S 的 dnsmasq,和 Nikki 的 DNS 劫持不是同一个开关。本方案中,普通客户端已经明确使用 192.168.10.3 作为 DNS,而默认网关又是 Q3000,因此该选项不是核心。可以保留开启;关键是关闭 Nikki 的 IPv4/IPv6 DNS 劫持。

    补充说明:它究竟在什么情况下生效

    192.168.10.3 就是 R4S。客户端使用 192.168.10.3 作为 DNS,确实就是主动向 R4S 上的 dnsmasq 查询。这种正常查询不需要“DNS 重定向”帮忙,因为目的地址本来就是 R4S。

    “DNS 重定向”处理的是另一种情况:客户端没有使用 DHCP 下发的 DNS,而是自行查询其他 DNS。例如在旧方案中,客户端默认网关也是 R4S:

    客户端默认网关:192.168.10.3
    客户端手动 DNS:8.8.8.8
    
    客户端 → 准备访问 8.8.8.8:53
           → DNS 请求先经过 R4S
           → R4S 的 DNS 重定向规则将它拦截
           → 改送给 R4S 自己的 dnsmasq
    

    此时,即使客户端手动填写 8.8.8.8,传统的 UDP/TCP 53 端口查询仍可能被 R4S 拉回本机,因此这个开关在“所有请求都经过 R4S”的旧方案中很有作用。

    当前方案的路径不同:

    客户端默认网关:192.168.10.1(Q3000)
    客户端手动 DNS:8.8.8.8
    
    客户端 → Q3000 → 8.8.8.8
    

    这个查询根本没有经过 R4S,所以 R4S 上的 DNS 重定向看不到、也拦不住它。结果是:

    • DNS 分类被绕过;
    • 需要代理的域名不会获得 198.18.x.x Fake-IP;
    • Q3000 的 Fake-IP 静态路由不会被触发;
    • Google 等服务会被当成普通直连,通常无法访问;
    • 在国内直接使用 8.8.8.8 还可能遇到超时、污染或不稳定。

    浏览器 DoH、Android 私人 DNS、iCloud 专用代理一类加密 DNS 也可能绕过传统的 53 端口重定向。因此,当前方案依赖客户端使用 Q3000 DHCP 下发的 192.168.10.3。从当前拓扑的实际作用看,R4S 的“DNS 重定向”关闭即可;即使保留开启,也不能阻止以 Q3000 为网关的客户端绕过 R4S DNS。

    4. YAML 和 Nikki UI 谁优先

    Nikki 启动时可能把“混入配置”合并到原始 YAML,再生成真正交给 Mihomo 的运行配置。因此:

    • 原始 YAML 是基础;
    • Nikki UI 和混入配置可能覆盖或补充它;
    • 最终运行结果要看 Nikki 生成的配置、UCI 设置以及防火墙状态;
    • 不要直接修改 /etc/nikki/run/config.yaml,它可能在重启后重新生成。

    例如,YAML 写了 Fake-IP,但 Nikki UI 又把 DNS 模式改掉,最后应以运行配置为准。排查时可查看:

    uci show nikki
    grep -nE 'enhanced-mode|fake-ip-range|listen' /etc/nikki/run/config.yaml
    nft list ruleset | grep -i nikki
    

    八、在 R4S 安装“分流 DNS”

    本文附带三份脚本:

    scripts/update-gfw-dnsmasq.sh
    scripts/install-split-dns.sh
    scripts/rollback-split-dns.sh
    

    它们不会包含你的订阅、密码或节点。安装脚本会先备份 /etc/config/dhcp、/etc/config/nikki、dnsmasq 附加目录和 root 定时任务。

    1. 上传脚本

    把下面两份文件上传到 R4S 的 /tmp:

    update-gfw-dnsmasq.sh
    install-split-dns.sh
    

    执行:

    chmod +x /tmp/update-gfw-dnsmasq.sh /tmp/install-split-dns.sh
    /tmp/install-split-dns.sh
    

    脚本完成以下工作:

    1. 创建带时间戳的备份目录,并把路径写入 /root/last-split-dns-backup。
    2. 让 dnsmasq 读取 /etc/dnsmasq.d。
    3. 把普通域名上游设为 223.5.5.5 和 223.6.6.6。
    4. 关闭 DNS Rebind 保护,使 dnsmasq 接受 Mihomo 返回的 198.18.x.x Fake-IP。
    5. 允许经过路由的局域网客户端使用该 DNS。
    6. 关闭 Nikki 的 IPv4/IPv6 DNS 劫持。
    7. 下载代理域名表,生成指向 127.0.0.1#1053 的规则。
    8. 每天 04:17 自动更新域名表。
    9. 重启 cron、Nikki 和 dnsmasq。

    关闭 Rebind 保护是为了接受 Fake-IP,会降低 dnsmasq 对“公网域名返回私有/保留地址”的防护。本方案的 R4S 位于 Q3000 内网,DNS 端口不得暴露到 WAN;防火墙应只允许可信 LAN/Tailscale 使用。

    2. 检查结果

    wc -l /etc/dnsmasq.d/gwf-gfwlist.conf
    grep -c '^server=/' /etc/dnsmasq.d/gwf-gfwlist.conf
    dnsmasq --test
    grep update-gfw-dnsmasq /etc/crontabs/root
    uci get nikki.proxy.ipv4_dns_hijack
    uci get nikki.proxy.ipv6_dns_hijack
    

    期望看到:

    • 域名规则数量大于 3000,实际数量会随上游更新变化;
    • dnsmasq --test 返回语法正确;
    • 两个 Nikki DNS 劫持值都是 0;
    • root 定时任务中存在更新命令。

    3. 检查 DNS 分类

    在 R4S 上执行:

    nslookup taobao.com 127.0.0.1
    nslookup google.com 127.0.0.1
    

    预期:

    • 淘宝返回真实公网 IP;
    • Google 返回 198.18.x.x 一类 Fake-IP。

    4. 手动补充漏网域名

    如果某个确定需要代理的域名没有命中,可新建:

    cat >/etc/dnsmasq.d/gwf-custom.conf <<'EOF'
    server=/example.com/127.0.0.1#1053
    server=/example.net/127.0.0.1#1053
    EOF
    
    dnsmasq --test && /etc/init.d/dnsmasq restart
    

    一条 server=/example.com/... 通常也匹配其子域名。添加前应先确认它确实需要代理;把国内 CDN 域名误加进去,可能重新引入国内 App 变慢的问题。

    九、配置 Q3000

    不同爱快版本的文字可能略有差异,但功能位置基本一致。

    1. LAN1 与 DHCP

    进入:

    网络设置 → DHCP 设置 → DHCP 服务端
    

    主网配置为:

    接口:LAN1
    地址池:192.168.10.100 - 192.168.10.200
    网关:192.168.10.1
    首选 DNS:192.168.10.3
    备用 DNS:留空
    租期:120 分钟(可按需调整)
    
    Q3000 主网 DHCP 服务端的地址池、网关与 DNS 设置
    Q3000 主网 DHCP:默认网关为 192.168.10.1,首选 DNS 为 R4S 的 192.168.10.3,备用 DNS 留空。

    备用 DNS 留空是有意的。若填入公共 DNS,部分手机和电脑会自行选择备用 DNS,绕过 R4S 的域名分类,表现为同一网站时好时坏。

    2. 检查静态 DHCP 绑定

    这是本次实际遇到的重要问题:DHCP 服务端已经把网关改为 .1,但 PC 反复禁用、启用网卡后,网关仍然是 .3。原因是该 PC 有一条静态 DHCP 绑定,里面单独写了旧网关。

    逐条检查静态分配/终端绑定:

    IP:按原计划保留
    网关:192.168.10.1
    DNS1:192.168.10.3
    DNS2:留空
    

    不要只检查全局 DHCP;终端专属设置通常优先于全局值。

    3. 添加 Fake-IP 静态路由

    进入:

    网络设置 → 静态路由
    

    添加:

    项目 值
    名称 NikkiFakeIP
    目标网段 198.18.0.0/16
    下一跳 192.168.10.3
    出口接口 LAN1/自动选择正确 LAN
    状态 启用
    Q3000 中指向 R4S 的 Fake-IP 与 Tailscale 静态路由
    Q3000 当前静态路由:Fake-IP 网段以及 Tailscale/远端 LAN 的回程路由都指向 R4S。

    这条路由就是整套方案的“搬运工”:Q3000 看到目标是 198.18.x.x,便知道这个地址不是从 WAN 直接访问,而应交给 R4S 解释。

    4. 不需要配置的项目

    本方案不需要:

    • 爱快付费“IP 归属地”;
    • 用五元组规则判断所有国外 IP;
    • 把默认路由或全网网关改成 R4S;
    • 按国家建立大量 IP 路由;
    • 为 GWF-5G 单独创建“全部下一跳 R4S”的策略路由。

    5. Q3000 能否单独封禁或限制某台客户端

    旧方案中,所有客户端先把流量交给 R4S,R4S 再转发或代理。若 R4S 对这些连接做了源地址转换,Q3000 看到的来源就可能全部是 192.168.10.3,从而无法在主路由上准确区分手机、电脑和电视。

    当前方案中,每台客户端的默认网关都是 Q3000。对于国内和普通境外直连流量,路径为:

    192.168.10.100(PC)   → Q3000 → WAN
    192.168.10.101(手机) → Q3000 → WAN
    192.168.10.102(电视) → Q3000 → WAN
    

    Q3000 可以直接看到每台设备原来的 IP 和 MAC,因此可以分别查看在线状态、禁止上网、设置上网时段、进行访问控制或配置固定地址。

    访问 Google 等代理服务时,第一段仍然经过 Q3000:

    客户端 → Q3000 → 198.18.x.x → R4S/Mihomo
    

    因此,如果 Q3000 使用客户端 IP/MAC,在流量进入转发路径时执行“终端禁止上网”或同类整机封禁,通常会在连接抵达 R4S 之前将其拦截。整机封禁不仅对国内和可直连服务有效,对 Google 等代理流量通常也同样有效。

    但 Mihomo 接管连接后,会以 R4S 的身份重新连接代理节点:

    R4S/Mihomo → 代理节点 → 目标网站
    

    这个真正从 WAN 发出的外层代理连接,在 Q3000 看来通常来自 192.168.10.3。因此,“能否封禁设备”和“能否精确统计代理流量”是两件不同的事:

    Q3000 上的操作 国内/直连流量 Google 等代理流量
    按 IP/MAC 禁止整台设备上网 有效 通常同样有效,因为第一段先经过 Q3000
    按 IP/MAC 设置上网时间 有效 只要规则作用于客户端转发入口,通常有效
    查看每台设备的直连连接和流量 可以准确区分 代理出口部分可能统计到 R4S
    对单台设备精确限制代理出口带宽 可以直接限制直连部分 是否准确取决于 Q3000 限速规则的作用阶段,需要实测
    按最终网站域名进行封禁 可使用 Q3000 的相关能力 可能受 Fake-IP 和代理封装影响,宜在 Mihomo 规则中处理

    为了让设备控制长期稳定,建议在 Q3000 中按 MAC 给需要管理的设备分配固定 IP,再使用 Q3000 的终端控制或 ACL。配置后应同时测试一个国内网站和一个代理网站,并查看规则命中情况;不同爱快版本中的“终端限速”“流控分流”和“访问控制”可能工作在不同阶段,不能仅凭界面上已经保存规则就判断代理流量一定被准确计量。

    十、让客户端重新获取网络参数

    完成 Q3000 配置后,让手机断开并重新连接 Wi-Fi;Windows 可执行:

    ipconfig /release
    ipconfig /renew
    ipconfig /flushdns
    ipconfig /all
    

    正确结果是:

    IPv4:192.168.10.x
    默认网关:192.168.10.1
    DNS:192.168.10.3
    

    这组结果看起来“不对称”,其实正是设计目标:

    • 网关 .1 决定业务流量默认从 Q3000 出去;
    • DNS .3 只负责给域名分类和返回地址。

    DNS 服务器不是默认网关,它们完全可以是两台不同设备。

    十一、从一次请求看完整运行过程

    场景 A:打开淘宝

    1. 手机向 192.168.10.3 查询淘宝相关域名。
    2. dnsmasq 未在代理域名表中命中。
    3. dnsmasq 向阿里 DNS 查询,返回真实 IP。
    4. 手机把业务包发给默认网关 192.168.10.1。
    5. Q3000 直接 NAT 到 WAN。
    6. R4S、Nikki 和 Mihomo不参与业务转发。

    场景 B:打开 Google

    1. 手机向 R4S 查询域名。
    2. dnsmasq 命中代理域名表,把查询送到 Mihomo 1053。
    3. Mihomo 返回 198.18.x.x。
    4. 手机把连接交给 Q3000。
    5. Q3000 命中 198.18.0.0/16 → 192.168.10.3 静态路由。
    6. R4S 上的 Nikki 防火墙捕获连接。
    7. Mihomo 根据 Fake-IP 映射还原域名,并按 YAML 选择代理组和节点。
    8. 返回数据沿连接状态回到手机。

    场景 C:访问可直连的境外网站

    1. 域名未命中代理表。
    2. 客户端得到真实 IP。
    3. Q3000 直接访问该境外 IP。
    4. R4S 不参与业务流量。

    场景 D:应用直接写死 IP

    如果应用完全不做 DNS 查询,而是直接连接某个真实 IP,就没有域名可供前置分类。这种连接默认由 Q3000 直连。若它必须代理,需要额外的 IP 路由、策略规则,或让该设备改用旧式“全量进入 Mihomo”的网络。不要为了极少数情况把整个主网重新改成全量旁路由。

    十二、Zashboard 为什么看不到国内连接

    旧方案中,所有流量都进入 Mihomo,因此 Zashboard 能看到淘宝、微信等国内连接,并显示最终走 DIRECT。

    新方案中,国内业务包从 Q3000 直接出 WAN,不进入 Mihomo,所以 Zashboard 通常只能看到代理域名产生的连接。它不是丢包,也不是监控坏了,而是说明分流发生在 Mihomo 之前。

    查看全网连接应使用 Q3000 的终端/连接监控;查看代理连接和节点选择才使用 Zashboard。

    十三、备用直连 Wi-Fi 有没有必要

    建议保留三个用途不同的网络:

    Wi-Fi 用途 网关/DNS思路
    GWF-5G 日常自动分流 网关 Q3000,DNS R4S
    WRT-5G 故障应急、完全直连 独立 VLAN,由 Q3000 提供网关和公共/运营商 DNS
    Guest-5G 访客隔离 独立访客 VLAN

    主网虽然不是把默认网关设成 R4S,但 DNS 依赖 R4S。R4S 停机后,已经建立的国内 IP 连接可能暂时继续,新的域名查询却会失败。备用 Wi-Fi 的价值就是完全不依赖 R4S:

    • 使用独立 VLAN;
    • 网关指向该 VLAN 的 Q3000 地址;
    • DNS 使用 Q3000、运营商 DNS 或可信公共 DNS;
    • 不添加“下一跳 R4S”的策略路由;
    • Fake-IP 静态路由即使是全局路由,也不会产生 Fake-IP,因为该 VLAN 不使用 R4S DNS。

    所以备用 Wi-Fi 不是为了日常分流,而是为了 R4S 重启、升级、配置失败时还能访问国内网络和管理设备。

    十四、Tailscale 的兼容配置

    Tailscale 和 Nikki 是 R4S 上两个独立服务:Tailscale 建立虚拟专网,Nikki 管理 Mihomo 代理。当前分流方案不会自动删除 Tailscale 设置,但回程路由必须正确。

    1. Q3000 添加 Tailscale 回程路由

    如果 R4S 关闭 Tailscale SNAT(NoSNAT=true),Q3000 必须知道怎样回到 Tailscale 地址和远端 LAN。

    添加:

    名称 目标网段 下一跳
    TailscaleCGNAT 100.64.0.0/10 192.168.10.3
    R2SRemoteLAN 192.168.110.0/24 192.168.10.3

    第一条让家庭 LAN 设备能把响应送回 Tailscale 客户端;第二条让本地设备知道公司/远端 LAN 位于 R4S 的 Tailscale 通道后面。

    2. Tailscale 出口节点能否继续使用代理

    可以,但需要 Nikki 接管来自 Tailscale 接口的流量。检查 Nikki 入站接口中是否包含 tailscale,并确认防火墙允许 Tailscale 与 LAN/WAN 互通。

    逻辑是:

    远端设备 → Tailscale 隧道 → R4S tailscale 接口
    → Nikki 透明代理规则(若包含该接口)
    → Mihomo 按 YAML 判断 DIRECT 或代理
    

    如果不希望出口节点流量经过代理,就不要让 Nikki 接管 Tailscale 入站,或添加明确的绕过规则。这里没有“一律正确”的开关,取决于出口节点的用途。

    3. 为什么远程桌面可能真的变流畅

    这不一定是错觉。旧方案把本地大量国内流量也送进 R4S/Nikki,增加 R4S 的连接跟踪、DNS、透明代理和单臂折返负担。新方案让绝大多数国内连接由 Q3000 直接处理,R4S 更空闲,Tailscale 的 CPU、队列和连接跟踪竞争都可能减少。

    不过远程桌面还受到宽带上行、运营商互联、Wi-Fi、Tailscale 是 direct 还是 DERP 等影响。要验证是否真实提速,可在相同时间段对比:

    tailscale ping 对端Tailscale地址
    tailscale status
    

    direct 表示两端直接打洞,通常更快;relay/DERP 表示经中继,延迟一般更高。

    4. 手机访问远端 LAN 慢,是否一定与本方案有关

    不一定。若 R4S 到远端 R2S 和远端 LAN 都很快,而手机到远端慢,应继续检查:

    • 手机到 R4S 是 direct 还是 DERP;
    • 手机移动网络是否限制 UDP;
    • 远端 R2S 是否正确发布 192.168.110.0/24;
    • Tailscale 管理后台是否批准子网路由;
    • 远端设备的默认网关和回程路由;
    • 公司侧曾做过的端口映射或防火墙规则。

    手机自身运行 Tailscale 时,访问 192.168.110.0/24 通常由手机 Tailscale 客户端直接送入隧道,不一定经过家庭 Q3000 的分流路径。因此不能仅凭“手机慢”认定是当前 DNS 分流造成的。

    十五、完整验收清单

    按顺序验证,不要只测试一个网站:

    检查项 正确结果
    客户端默认网关 192.168.10.1
    客户端 DNS 192.168.10.3
    淘宝/微信/银行 App 正常且无明显卡顿
    普通国内网站 正常
    Google/TikTok 可通过代理访问
    国内域名解析 返回真实公网 IP
    代理域名解析 返回 198.18.x.x
    Q3000 Fake-IP 路由 198.18.0.0/16 → 192.168.10.3
    Nikki DNS 劫持 IPv4、IPv6 均关闭
    Zashboard 主要看到代理连接,看不到多数国内连接
    WRT-5G R4S 停机时仍可直连国内网络
    Tailscale 需要时显示 direct,远端子网可达

    建议重启一次 R4S,再让客户端重新联网,确认配置不会只在当前运行状态下生效。

    十六、常见故障与排查

    1. 淘宝、微信卡住,Google 却正常

    优先检查:

    客户端网关是不是还在指向 192.168.10.3?
    静态 DHCP 绑定里是不是保留了旧网关?
    国内域名是不是错误返回了 198.18.x.x?
    Nikki DNS 劫持是不是又被打开?
    

    不要一上来反复修改 YAML 节点规则。先确认国内业务流量有没有真的绕开 R4S。

    2. PC 更新租约后网关仍是 .3

    检查 Q3000 中该 PC 的静态 DHCP/终端绑定。专属绑定中的网关会覆盖 DHCP 服务端的全局网关。修改后再执行 ipconfig /release 和 ipconfig /renew。

    3. 手机 DNS 仍是 192.168.10.3

    这是正确状态,不是残留配置。R4S 在本方案中仍是 DNS 分类器,只是不再是默认网关。

    4. 国内正常,某个受限网站打不开

    执行:

    nslookup 目标域名 192.168.10.3
    grep -F '目标域名' /etc/dnsmasq.d/gwf-gfwlist.conf
    

    若返回真实 IP 且列表没有该域名,将正确的主域名加入 gwf-custom.conf。若已经返回 Fake-IP,再去检查 Q3000 静态路由、Nikki 防火墙、Mihomo 规则和节点。

    5. 所有域名都打不开,但直接访问 IP 可能正常

    检查 R4S 是否在线、dnsmasq 是否监听 53 端口、Q3000 DHCP 是否仍只发布 R4S DNS:

    /etc/init.d/dnsmasq status
    netstat -lnup | grep ':53 '
    dnsmasq --test
    logread -e dnsmasq
    

    然后临时连接 WRT-5G 直连网络,不要急着给主网增加公共 DNS2,否则会破坏稳定分类。

    6. Zashboard 没有国内连接

    这是设计结果。国内业务没有进入 Mihomo,去 Q3000 查看全网连接。

    7. 重启 Nikki 后行为变化

    Nikki 的混入配置可能重新生成运行配置。比较:

    uci show nikki
    grep -nE 'dns:|enhanced-mode|fake-ip-range|listen:' /etc/nikki/run/config.yaml
    

    不要直接编辑运行目录中的 YAML。应修改原始配置或 Nikki UI/混入配置中的对应项目。

    8. mmstat.com 这类国内域名怎么判断

    本方案不是让 Mihomo 猜它是不是国内域名。dnsmasq 只问一个更直接的问题:“它是否在需要代理的域名表里?”不在列表就返回真实 IP,让 Q3000 直连。这样不需要维护千千万万个国内域名,也不依赖 GeoIP 先查归属地。

    9. RULE-SET,cn_ip,DIRECT,no-resolve 还要不要改

    它仍可保留在 Mihomo YAML 中,作为已经进入 Mihomo 后的保护规则。no-resolve 表示这条 IP 规则不为了匹配而额外触发 DNS 解析。新方案的关键不在于删改这一条,而是多数国内连接根本不再进入 Mihomo。

    十七、回滚方法

    1. 回滚 R4S

    把 rollback-split-dns.sh 上传到 R4S 的 /tmp,执行:

    chmod +x /tmp/rollback-split-dns.sh
    /tmp/rollback-split-dns.sh
    

    脚本读取 /root/last-split-dns-backup,恢复安装前的 DHCP/DNS 与 Nikki 配置,并删除自动更新任务和生成的域名表。

    2. 回滚 Q3000 为完全直连

    为了最快恢复基本上网:

    1. 保持客户端网关为 Q3000,例如 192.168.10.1。
    2. 把 DHCP DNS 临时改为 Q3000、运营商 DNS 或可靠公共 DNS。
    3. 禁用或删除 198.18.0.0/16 → 192.168.10.3 静态路由。
    4. 客户端重新获取 DHCP 并清除 DNS 缓存。

    这会失去自动代理,但能先恢复稳定直连。确认网络正常后,再逐层恢复分流,不要同时改动 Q3000、dnsmasq、Nikki 和 YAML。

    十八、让 Codex 协助配置的提示词

    下面的提示词可以直接改地址后使用。不要在提示词中写明文密码、订阅链接和 API 密钥;需要登录时单独安全提供,并在完成后更换临时凭据。

    1. 只读审计现状

    请只读检查我的 Q3000 与 R4S 当前网络配置,不要修改任何设置。目标方案是:Q3000 做默认网关和 DHCP,R4S 的 dnsmasq 做前置 DNS 分类;未命中代理域名表的请求由 Q3000 直连,命中的域名由 Mihomo 返回 198.18.0.0/16 Fake-IP,再由 Q3000 静态路由送往 R4S。请检查 DHCP、静态 DHCP、静态路由、Nikki DNS 劫持、dnsmasq 上游、Fake-IP 范围、Mihomo 1053 DNS 监听、Tailscale 回程路由,并先给出风险和回滚点。不要显示或记录任何密钥。
    

    2. 实施前备份并分阶段修改

    请按“先备份、一次只改一层、每层验证、失败自动回滚”的方式实施 Q3000 + R4S 分流。我的主网是 192.168.10.0/24,Q3000 是 192.168.10.1,R4S 是 192.168.10.3,Mihomo Fake-IP 是 198.18.0.0/16。先备份 Q3000 和 R4S 配置;先改 R4S DNS 分类并验证,再改 Q3000 DHCP 与 Fake-IP 静态路由。修改主网网关前提醒我切到备用 Wi-Fi。完成后列出每个 UI 路径、旧值、新值、验证结果和回滚命令。
    

    3. 排查国内 App 卡顿

    当前表现是国外代理正常,但淘宝、微信或其他国内 App 卡顿。请不要先改节点或重写 YAML。先只读确认客户端实际网关和 DNS、Q3000 静态 DHCP 是否覆盖全局 DHCP、国内域名是否返回真实 IP、Nikki IPv4/IPv6 DNS 劫持是否关闭、国内业务包是否仍进入 Mihomo。请用证据定位是哪一层出错,再提出最小修改,并准备回滚。
    

    4. 排查单个网站未代理

    某个域名在当前“dnsmasq 域名分类 + Mihomo Fake-IP”方案下无法访问。请检查它及实际使用的子域名/CNAME 是否命中 /etc/dnsmasq.d/gwf-gfwlist.conf,解析结果是实际 IP 还是 198.18.x.x,Q3000 的 198.18.0.0/16 静态路由是否命中,以及连接是否出现在 Zashboard。只有确认是列表漏收后,才把最小必要主域名加入 gwf-custom.conf,并测试 dnsmasq 语法和访问结果。
    

    5. 检查 Tailscale,不动代理主方案

    请单独诊断 R4S 上的 Tailscale,不修改现有 Nikki/Mihomo 分流。检查 peer 是 direct 还是 DERP、R4S 与远端子网路由器的延迟、子网路由发布/批准状态、NoSNAT 设置,以及 Q3000 是否有 100.64.0.0/10 和远端 LAN 经 192.168.10.3 的回程路由。请区分“家庭分流问题”和“手机运营商/Tailscale 打洞问题”。
    

    6. 生成重装后的复原报告

    请根据实际运行状态生成一份可复原报告,内容包括 Q3000 的 LAN、DHCP、静态 DHCP、VLAN/Wi-Fi、静态路由;R4S 的网络、dnsmasq、Nikki UCI、Mihomo Fake-IP/DNS 监听、Tailscale 与防火墙;并隐去密码、令牌、订阅地址和节点信息。报告中同时给出验证命令和完整回滚顺序。
    

    十九、重装时只记住这四个核心改动

    如果以后重新刷机,整套方案最核心的是:

    1. Q3000 做主网默认网关和 DHCP:客户端网关是 Q3000,不是 R4S。
    2. R4S dnsmasq 做域名分类:普通域名走国内 DNS,代理域名转给 Mihomo 1053。
    3. Q3000 添加 Fake-IP 静态路由:198.18.0.0/16 → R4S。
    4. Nikki 只接管送到 R4S 的连接:关闭 Nikki 全局 DNS 劫持,保留 Mihomo Fake-IP 与代理规则。

    YAML、UI 混入、Tailscale 和备用 Wi-Fi 都是在这四个核心之上的功能层。理解这四点,就不容易在重装时又退回“所有流量先过 R4S”的旧结构。

    二十、附录:完整脚本

    以下脚本与本文附件一致。复制时请保留 Unix 换行符;若从 Windows 编辑器上传,建议先执行 sed -i 's/\r$//' 文件名 清除可能的 CRLF。

    1. update-gfw-dnsmasq.sh

    #!/bin/sh
    
    set -eu
    
    TARGET_DIR="/etc/dnsmasq.d"
    TARGET_FILE="$TARGET_DIR/gwf-gfwlist.conf"
    TMP_LIST="/tmp/gwf-gfwlist.$$.txt"
    TMP_CONF="/tmp/gwf-gfwlist.$$.conf"
    URL_PRIMARY="https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
    URL_FALLBACK="https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt"
    
    cleanup() {
        rm -f "$TMP_LIST" "$TMP_CONF"
    }
    trap cleanup EXIT INT TERM
    
    mkdir -p "$TARGET_DIR"
    
    if ! curl -fsSL --connect-timeout 15 --max-time 120 "$URL_PRIMARY" -o "$TMP_LIST"; then
        curl -fsSL --connect-timeout 15 --max-time 120 "$URL_FALLBACK" -o "$TMP_LIST"
    fi
    
    awk '
        BEGIN {
            print "# Generated from Loyalsoldier/v2ray-rules-dat gfw.txt"
            print "# Matching domains are resolved by Mihomo fake-IP DNS."
        }
        {
            gsub(/\r/, "")
            domain = tolower($0)
            if (domain ~ /^[a-z0-9][a-z0-9._-]*[a-z0-9]$/ && domain ~ /\./) {
                print "server=/" domain "/127.0.0.1#1053"
            }
        }
    ' "$TMP_LIST" | sort -u > "$TMP_CONF"
    
    rule_count="$(grep -c '^server=/' "$TMP_CONF" || true)"
    if [ "$rule_count" -lt 3000 ]; then
        echo "Refusing to install incomplete GFW list: $rule_count rules" >&2
        exit 1
    fi
    
    if ! dnsmasq --test --conf-file="$TMP_CONF" 2>&1 | grep -q 'syntax check OK'; then
        echo "dnsmasq rejected the downloaded configuration" >&2
        exit 1
    fi
    
    if [ -f "$TARGET_FILE" ] && cmp -s "$TMP_CONF" "$TARGET_FILE"; then
        echo "GFW DNS list is already current: $rule_count rules"
        exit 0
    fi
    
    PREVIOUS_FILE=""
    if [ -f "$TARGET_FILE" ]; then
        PREVIOUS_FILE="$TARGET_FILE.previous"
        cp "$TARGET_FILE" "$PREVIOUS_FILE"
    fi
    
    cp "$TMP_CONF" "$TARGET_FILE.new"
    mv "$TARGET_FILE.new" "$TARGET_FILE"
    
    if ! dnsmasq --test 2>&1 | grep -q 'syntax check OK'; then
        if [ -n "$PREVIOUS_FILE" ] && [ -f "$PREVIOUS_FILE" ]; then
            mv "$PREVIOUS_FILE" "$TARGET_FILE"
        else
            rm -f "$TARGET_FILE"
        fi
        echo "dnsmasq rejected the generated configuration" >&2
        exit 1
    fi
    
    if [ -n "$PREVIOUS_FILE" ]; then
        rm -f "$PREVIOUS_FILE"
    fi
    
    /etc/init.d/dnsmasq restart
    echo "Installed $rule_count GFW DNS rules"
    

    2. install-split-dns.sh

    #!/bin/sh
    
    set -eu
    
    if [ ! -f /tmp/update-gfw-dnsmasq.sh ]; then
        echo "Upload update-gfw-dnsmasq.sh to /tmp first" >&2
        exit 1
    fi
    
    BACKUP_DIR="/root/before-split-dns-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$BACKUP_DIR"
    cp -a /etc/config/dhcp /etc/config/nikki "$BACKUP_DIR/"
    cp -a /etc/crontabs/root "$BACKUP_DIR/crontab-root" 2>/dev/null || true
    cp -a /etc/dnsmasq.d "$BACKUP_DIR/dnsmasq.d" 2>/dev/null || true
    cp -a /usr/local/sbin/update-gfw-dnsmasq "$BACKUP_DIR/update-gfw-dnsmasq" 2>/dev/null || true
    echo "$BACKUP_DIR" > /root/last-split-dns-backup
    
    mkdir -p /etc/dnsmasq.d /usr/local/sbin
    
    uci set dhcp.@dnsmasq[0].confdir='/etc/dnsmasq.d'
    uci set dhcp.@dnsmasq[0].noresolv='1'
    uci set dhcp.@dnsmasq[0].rebind_protection='0'
    uci set dhcp.@dnsmasq[0].localservice='0'
    uci -q delete dhcp.@dnsmasq[0].server || true
    uci add_list dhcp.@dnsmasq[0].server='223.5.5.5'
    uci add_list dhcp.@dnsmasq[0].server='223.6.6.6'
    uci commit dhcp
    
    uci set nikki.proxy.ipv4_dns_hijack='0'
    uci set nikki.proxy.ipv6_dns_hijack='0'
    uci commit nikki
    
    cp /tmp/update-gfw-dnsmasq.sh /usr/local/sbin/update-gfw-dnsmasq
    chmod 0755 /usr/local/sbin/update-gfw-dnsmasq
    /usr/local/sbin/update-gfw-dnsmasq
    
    cron_line='17 4 * * * /usr/local/sbin/update-gfw-dnsmasq >/tmp/update-gfw-dnsmasq.log 2>&1'
    touch /etc/crontabs/root
    if ! grep -Fqx "$cron_line" /etc/crontabs/root; then
        printf '%s\n' "$cron_line" >> /etc/crontabs/root
    fi
    
    /etc/init.d/cron restart
    /etc/init.d/nikki restart
    /etc/init.d/dnsmasq restart
    
    echo "Backup: $BACKUP_DIR"
    echo "Split DNS installed"
    

    3. rollback-split-dns.sh

    #!/bin/sh
    
    set -eu
    
    POINTER="/root/last-split-dns-backup"
    if [ ! -s "$POINTER" ]; then
        echo "Backup pointer not found: $POINTER" >&2
        exit 1
    fi
    
    BACKUP_DIR="$(cat "$POINTER")"
    if [ ! -d "$BACKUP_DIR" ]; then
        echo "Backup directory not found: $BACKUP_DIR" >&2
        exit 1
    fi
    
    cp "$BACKUP_DIR/dhcp" /etc/config/dhcp
    cp "$BACKUP_DIR/nikki" /etc/config/nikki
    
    if [ -f "$BACKUP_DIR/crontab-root" ]; then
        cp "$BACKUP_DIR/crontab-root" /etc/crontabs/root
    else
        sed -i '\|/usr/local/sbin/update-gfw-dnsmasq|d' /etc/crontabs/root 2>/dev/null || true
    fi
    
    if [ -f "$BACKUP_DIR/update-gfw-dnsmasq" ]; then
        cp "$BACKUP_DIR/update-gfw-dnsmasq" /usr/local/sbin/update-gfw-dnsmasq
        chmod 0755 /usr/local/sbin/update-gfw-dnsmasq
    else
        rm -f /usr/local/sbin/update-gfw-dnsmasq
    fi
    
    if [ -f "$BACKUP_DIR/dnsmasq.d/gwf-gfwlist.conf" ]; then
        cp "$BACKUP_DIR/dnsmasq.d/gwf-gfwlist.conf" /etc/dnsmasq.d/gwf-gfwlist.conf
    else
        rm -f /etc/dnsmasq.d/gwf-gfwlist.conf
    fi
    
    /etc/init.d/cron restart
    /etc/init.d/nikki restart
    /etc/init.d/dnsmasq restart
    
    echo "R4S split DNS rolled back from $BACKUP_DIR"
    

    二十一、参考资料


    **最后提醒:**这套方案的目标不是让规则数量最多,而是把国内直连路径变得简单,把复杂代理判断限制在真正需要代理的连接上。修改后必须以客户端实际拿到的网关/DNS、DNS 返回结果、Q3000 路由命中和 Mihomo 连接记录为准,不能只看 UI 中某个开关。

  • Cloudflare WAF 误拦 WordPress REST API:安全放行 Application Password 的正确方法

    Cloudflare WAF 误拦 WordPress REST API:安全放行 Application Password 的正确方法

    这次问题发生在我现在这套写作和发布架构里:前台 sirenyan.cn 是部署在 Cloudflare Pages 上的静态站,后端 wp.sirenyan.cn 继续作为 WordPress 内容后台。公开读取的 REST API 用来给静态站同步文章;已认证的 REST API 则用来做自动发文。

    这套架构本身很舒服:WordPress 负责写作和保存原始内容,Cloudflare Pages 负责把最终页面静态化展示。但安全规则一收紧,就很容易出现一个细节问题:Cloudflare 在请求到达 WordPress 之前就会先判断、拦截。

    故障现象

    最开始的规则是为了保护 WordPress REST API:允许公开读取,禁止写入。规则逻辑大致是拦截 /wp-json/ 下所有非 GET、HEAD、OPTIONS 的请求。

    这样做看起来合理,因为静态同步只需要读取文章。但实际使用时出现了两个问题:

    • 在 WordPress 后台创建 Application Password 时失败。
    • 使用 Application Password 调用 REST API 发文时返回 403 Forbidden。

    也就是说,规则不仅挡住了匿名写入,也把后台操作和已认证的 API 写入一起挡住了。

    根因

    根因不是 WordPress Application Password 失效,而是请求根本没有走到 WordPress。Cloudflare WAF 是站在 WordPress 前面的,它会先根据域名、路径、HTTP 方法和请求头判断要不要拦截。

    当规则写成“只要是 /wp-json/ 下的非读取请求就拦截”时,Cloudflare 不会等 WordPress 验证用户名和 Application Password。请求还没到 WordPress,就已经被挡掉了。

    正确策略

    更合适的策略是分层处理:

    • GET、HEAD、OPTIONS 继续允许,用于公开读取和静态同步。
    • 带 Authorization: Basic 的写请求放行到 WordPress,由 WordPress 自己验证凭据。
    • 其余没有认证头的匿名写请求,继续由 Cloudflare 拦截。

    这里要特别注意:允许带 Basic 头的请求到达 WordPress,不等于认证一定成功。错误的用户名或错误的 Application Password 仍然会被 WordPress 拒绝。Cloudflare 只负责第一层过滤,WordPress 负责真正的身份验证。

    最终规则表达式

    最终使用并通过 Cloudflare 接受的表达式如下:

    (http.host eq "wp.sirenyan.cn" and starts_with(http.request.uri.path, "/wp-json/") and not http.request.method in {"GET" "HEAD" "OPTIONS"} and not any(http.request.headers["authorization"][*] contains "Basic "))

    这里有一个小细节:Cloudflare Rules language 里的 http.request.headers["authorization"] 是数组。使用 any(...[*] contains ...) 比直接写 [0] 更稳,也更符合它对多值请求头的表达方式。

    验证结果

    修改后验证了几件事:

    • 公开 GET /wp-json/... 正常,静态站同步不受影响。
    • 匿名 POST /wp-json/... 被 Cloudflare 返回 403。
    • 带有效 Application Password 的 POST 可以成功创建文章。
    • 危险入口的拦截规则仍然保留。
    • WordPress 登录页和后台仍然保持 Managed Challenge。
    • 规则只限定在 wp.sirenyan.cn,不影响 sirenyan.cn 的 Cloudflare Pages 前台。

    安全建议

    这种做法的重点不是把 REST API 全部暴露出去,而是让 Cloudflare 和 WordPress 各自负责自己擅长的一层:

    • Cloudflare 负责拦截匿名写请求和高风险路径。
    • WordPress 负责验证已认证请求的用户名和 Application Password。
    • 全程必须使用 HTTPS。
    • Application Password 应该单独创建,用完可以随时撤销。
    • 不要把 Application Password 写入日志、文章、代码仓库或公开配置文件。
    • 自动化发布时,只把凭据放在安全的运行环境里。

    后续整理

    这次顺手也把三条自定义规则的显示说明改成了中文,后面维护时更容易看懂。Cloudflare 入口规则集本身的 name 字段属于锁定字段,所以保留英文名称,只把规则说明改成中文。

    结论

    WordPress REST API 的安全规则不能只按路径和方法一刀切。对于需要自动发布的站点,更合理的做法是:公开读取放行,匿名写入拦截,带认证头的写入交给 WordPress 验证。这样既保留了 Cloudflare 的前置防护,也不会破坏 Application Password 这种标准认证机制。

  • 极限榨干 1GB 内存:GCP 免费云主机 + HestiaCP 搭建高性能 WordPress 终极指南

    极限榨干 1GB 内存:GCP 免费云主机 + HestiaCP 搭建高性能 WordPress 终极指南

    对于很多独立站站长和极客来说,Google Cloud Platform (GCP) 提供的 e2-micro 免费实例(1GB 内存 + 30GB 标准硬盘)是一项不可多得的福利。然而,1GB 内存加上速度较慢的标准机械硬盘,如果直接套用传统的宝塔面板或 1Panel,往往会在安装数据库时因为内存耗尽(OOM)而直接死机。

    今天,我们将另辟蹊径,抛弃臃肿的容器化方案,采用轻量级原生面板 HestiaCP,配合 Cloudflare CDN 和底层 Linux 内核深度优化,把这台 1GB 内存的“小霸王”调校成能秒开 B2B 外贸网站的高性能服务器!

    核心架构思路

    • 弃用 Docker: 采用原生安装的 HestiaCP,省去容器层的内存损耗。
    • 极致做减法: 剔除杀毒软件(ClamAV)和邮件服务器,只保留纯粹的 Nginx + PHP + MariaDB。
    • 物理外援: 强开 2GB Swap 虚拟内存防止死机。
    • 流量剥离: 利用 Cloudflare 承担几乎所有的静态资源分发。

    步骤一:云端前置准备(防火墙与解析)

    在动服务器之前,我们需要先铺好路:

    1. 开放端口: 登录 GCP 控制台,进入 VPC 网络 -> 防火墙。创建一条新规则(入站、允许所有 IP 0.0.0.0/0),放行 TCP 端口:80, 443, 8083(如果你后续想改面板端口,比如改为 8888,请一并在这里放行)。
    2. 域名解析: 前往 Cloudflare,将你的主域名(如 [www.yourdomain.com](https://www.yourdomain.com))和面板专属管理域名(如 hcp.yourdomain.com)的 A 记录都指向 GCP 服务器的外部公网 IP。注意:此时先保持“小灰云”状态(仅 DNS),不要开启代理。

    步骤二:服务器底层“保命”设置 (SSH)

    通过 GCP 控制台的 SSH 连入服务器,获取最高权限并设置规范的主机名(必须设置规范的 FQDN 主机名,否则后续 Let’s Encrypt SSL 证书大概率申请失败):

    Bash

    sudo su
    hostnamectl set-hostname hcp.yourdomain.com
    

    步骤三:借助 AI (Codex) 完成极简自动化部署

    在这一步,我没有选择手动逐行敲击代码,而是使用 Codex 连接 VPS,进行全自动化部署。在让 AI 开始执行之前,我先给它下达了明确的安装要求:

    “这台 VPS 仅用于部署 2-3 个轻量级的 WordPress 小站点。由于系统资源极度有限(1GB 内存),请务必采用最精简、最轻量的方案进行安装,彻底剔除所有非必要的吃内存组件,然后再执行安装操作。”

    接收到指令后,AI 便自动接管了面板的搭建。以下是 AI 执行部署时的核心优化思路(供参考):

    1. 环境预检与 Swap 兜底: AI 首先自动排查了服务器的 80、443、8083 端口是否被占用,并紧接着为这台 1GB 内存的机器强制分配了 2GB 的 Swap 虚拟内存。这是极其关键的一步,从物理层面防止了后续数据库安装时可能发生的 OOM(内存耗尽)死机。
    2. 定制极简安装策略(做减法): 在调用 HestiaCP 官方安装脚本时,AI 采用了非常严苛的“断舍离”参数配置:
      • 保留核心: 仅安装维持网站运行必备的 Nginx、PHP-FPM、MariaDB,以及基础安全套件(iptables + Fail2Ban)。
      • 剔除累赘: 坚决砍掉了邮件服务(Exim/Dovecot)、DNS 服务(BIND)、FTP,以及最吃内存的杀毒与反垃圾邮件组件(ClamAV/SpamAssassin)。
    3. 自动化环境收尾: 安装完成后,AI 顺手解决了 Debian 系统下常见的中文语言包缺失问题(自动配置 zh_CN.UTF-8 locale),避免了面板出现乱码,并执行了服务器重启。

    通过 AI 代理执行,不仅避开了容易出错的长串命令,更精准地将服务器资源全部留给了建站本身。重启后,我们即可通过 https://面板域名:8083 顺利登录后台。

    步骤四:面板收尾与建站配置

    配置 PHP 与部署网站:

    1. 浏览器输入你配置好的独立域名(如 [https://hcp.example.com:8083](https://hcp.example.com:8083))登录面板。
    2. 点击右上角齿轮 (Server) -> Web Server 下的 php-fpm -> Edit -> Configure -> Advanced Options。将 upload_max_filesize 和 post_max_size 修改为 128M(否则 WordPress 无法上传大插件)。
    3. 点击顶部菜单 WEB -> Add Web Domain,填入你的主域名,勾选启用 SSL 和 Let’s Encrypt。
    4. 在 WEB 列表点击该域名的编辑,右上角选择 Quick Install App,一键安装 WordPress。

    步骤五:Cloudflare 完美协同(避坑指南)

    现在去 Cloudflare 把 DNS 记录的“小灰云”点亮为“小黄云”(开启代理),但必须注意一个致命死循环!

    • 避坑法则: 既然源站(HestiaCP)已经有了 SSL 证书并开启了强制 HTTPS 重定向,在 Cloudflare 的 SSL/TLS 加密模式中,必须选择“完全(严格)” (Full – Strict)。
    • 如果你选了“灵活(Flexible)”,会导致无限的 ERR_TOO_MANY_REDIRECTS 重定向死循环!

    步骤六:Linux 内核终极“超频”优化

    HestiaCP 优化了 Web 层,我们要亲自优化 Linux 内核,弥补 1GB 内存和慢速硬盘的物理缺陷。在 SSH 中一次性执行以下代码:

    Bash

    cat >> /etc/sysctl.conf << EOF
    
    # 1. 开启 BBR 算法,榨干网络带宽,大幅提升跨国访问速度
    net.core.default_qdisc = fq
    net.ipv4.tcp_congestion_control = bbr
    
    # 2. 压制 Swap 倾向 (默认60,改设10)。保护 GCP 极慢的标准硬盘不被频繁读写拖死
    vm.swappiness = 10
    
    # 3. 激进回收 TCP 连接。防止僵尸访客占用宝贵的内存资源
    net.ipv4.tcp_fin_timeout = 15
    net.ipv4.tcp_keepalive_time = 300
    net.ipv4.tcp_max_tw_buckets = 5000
    net.ipv4.tcp_tw_reuse = 1
    EOF
    
    # 使配置立即生效
    sysctl -p
    

    ✅ 验证优化是否成功:

    执行完上述命令后,单独运行下面这条命令来检查 BBR 算法是否已被成功激活:

    Bash

    lsmod | grep bbr
    

    如果屏幕输出结果中包含了类似 tcp_bbr 20480 1 的字样,恭喜你,这说明 BBR 已经成功在内核中全速运行接管网络流量了!

    结语

    经过上述六个步骤的手术级改造,你的 GCP 1GB 免费实例已经脱胎换骨。双层缓存体系(Cloudflare 静态拦截 + Nginx FastCGI 动态缓存)加上坚实的内核底层(BBR + 内存防崩策略),足以支撑起一个访问速度极快、极具专业感的 B2B 独立站。建站,有时候比拼的不是财力,而是将资源运用到极致的极客精神!

  • 软路由极致折腾手册:从底层存储到全能网关

    软路由极致折腾手册:从底层存储到全能网关

    第一阶段:网络环境初始化

    刷机后的 R2S 是“白纸”一张,无法下载插件。我们需要利用电脑作为“奶妈”提供初始流量。

    1. 利用 Clash Verge 局域网共享(详细步骤)

    如果你电脑上有 Clash Verge 且有节点,可以让 R2S “借道”翻墙。

    • 电脑端设置:
      1. 打开 Clash Verge,进入 设置 (Settings) -> 系统设置。
      2. 找到 允许局域网 (Allow LAN) 开关,将其打开。
      3. 记录下电脑的 局域网 IP 地址(例如 192.168.10.10)和 混合端口 (Mixed Port)(通常是 7890 或 7897)。
      4. 注意:如果连接失败,请暂时关闭 Windows 防火墙或允许 Clash 通过防火墙。
    • R2S 端设置 (HomeProxy):
      1. 安装并进入 HomeProxy。
      2. 添加一个节点,类型选 手动 (Manual)。
      3. 地址 (Address):填写你电脑的 IP(如 192.168.10.10)。
      4. 端口 (Port):填写 Clash 的端口(如 7897)。
      5. 保存并启用,此时 R2S 即可正常下载后续插件。

    2. Nikki安装与配置

    • 核心步骤:HomeProxy 稳定后安装 Nikki 并导入配置文件。
    • 【避坑重点】:配置文件中 UI 控制面板相关的几行代码严禁注释,否则初次使用无法打开UI面板!
    • 配置文件 删除QUIC模块: ports: [443, 8443],原因,保留这个模块后,国内淘宝APP卡顿。
    • 保持状态:全程保持翻墙状态,否则 UI 面板数据无法下载,会导致插件空壳运行。

    第二阶段:安装Docker 防坑与硬盘锁

    R2S 系统闪存极其有限,必须通过“物理手段”防止系统盘爆满。

    1. 磁盘分区与逻辑

    • 软件包常规安装 DiskMan
    • 使用 DiskMan 进行分区:
      • Docker 分区:划分 10GB 空间。
      • 剩余存储区:剩余空间格式化为 ext4,并挂载为 /mnt/tf-disk

    2. 【核心】chattr +i 系统盘防爆锁

    必须在挂载硬盘前,于 SSH 执行:

    Bash

    # 1. 创建入口文件夹
    mkdir -p /opt/docker
    
    # 2. 安装锁定工具
    opkg update && opkg install chattr
    
    # 3. 锁定系统盘坑位(防止数据“掉入”系统闪存)
    chattr +i /opt/docker
    

    3. 精准挂载与 Docker 安装

    • 路径:系统 -> 挂载点 -> 添加。
    • 设置:将 10GB 分区 挂载到 /opt/docker。
    • 自动挂载策略:
      • 自动挂载未配置分区:取消勾选 (OFF)(防止路径偏移)。
      • 自动挂载已配置分区:保持勾选 (ON)(确保重启后硬盘自动就位)。

    4.在 SSH 终端中重启路由器:

    Bash

    reboot

    验证锁定与挂载: 重启回来后,请务必执行以下命令验证你的 10G 硬盘 是否已经成功“盖住”了那个锁死的坑位:

    Bash

    df -h | grep /opt/docker
    
    • 预期结果:你应该能看到类似 /dev/sda1(你的分区名)挂载在 /opt/docker 下,且容量显示为 10G。
    • 如果看到了 10G:恭喜,你可以立即执行 opkg install luci-app-dockerman 开始安装 Docker 了!

    5.安装 Docker:

    重启路由器后,在 SSH 执行:

    # 更新并安装 Docker 管理插件及中文语言包
    opkg update
    opkg install luci-app-dockerman
    opkg install luci-i18n-dockerman-zh-cn
    • 由于 /opt/docker 被锁且已被 10G 硬盘覆盖,镜像将直接进入硬盘。

    6.核心避坑:解决 Docker 端口打不开的问题

    问题描述:

    你安装了 Docker 插件(如 Jellyfin 8096 或 Navidrome 4533),且在 Docker 管理界面看到端口映射 0.0.0.0:xxxx 已经生效,但使用浏览器访问 路由器IP:端口 却提示“连接超时”或“无法访问”。

    原因分析:

    ImmortalWrt 默认的防火墙规则出于安全考虑,将**“默认转发(Forward)”**设置为了“拒绝(Reject)”。由于 Docker 容器运行在虚拟网桥(docker0)上,访问容器属于跨区域流量,会被防火墙直接拦截。

    解决方案:

    1. 登录 Web 管理后台。
    2. 进入 网络 -> 防火墙 -> 常规设置。
    3. 找到 “默认转发” 选项,将其从 “拒绝” 修改为 “接受 (Accept)”。
    4. 点击底部的 “保存并应用”。

    第三阶段:安装文件分享Samba

    针对 Win11 访问需要用户名密码的情况,手动创建 xxx 用户。

    1.软件包常规安装Samba

    2. SSH 创建用户

    Bash

    # 1. 安装工具
    opkg update && opkg install shadow-useradd
    # 2. 创建用户(不创建家目录,禁止 shell 登录)
    useradd -M -s /bin/false sirenyan
    # 3. 设置 Samba 密码(输入 sirenyan)
    smbpasswd -a sirenyan

    3.创建剩余磁盘空间

    在磁盘管理中,把当前系统存储卡的剩余空间创建并格式化为ext4

    并在挂载点中创建一个挂载点:/mnt/tf-disk

    4. 网页端配置 (Samba)

    进入:服务 -> 网络共享 (Samba) -> 共享目录 (针对 tf-disk)。

    • 允许用户 (Allowed users):填入 sirenyan。
    • 允许访客:可以取消勾选或保留。
    • 强制 Root:取消勾选(使用 sirenyan权限)。

    4. 权限修正

    若无权限读写,勾选强制 Root即可,但不推荐,推荐以下方案,执行:

    Bash

    # 将文件夹及其内部所有文件的“所有者”改为 sirenyan
    chown -R sirenyan:sirenyan /mnt/tf-disk
    chmod -R 755 /mnt/tf-disk

    第四阶段:安装iStore 和首页

    1. iStoreOS 商店核心安装方案

    在原生 ImmortalWrt 终端中执行以下脚本。该方案利用 /tmp 内存目录作为中转,安全且高效。

    Bash

    opkg update || exit 1
    cd /tmp
    wget https://github.com/linkease/openwrt-app-actions/raw/main/applications/luci-app-systools/root/usr/share/systools/istore-reinstall.run
    chmod 755 istore-reinstall.run && ./istore-reinstall.run

    关键点解析:

    • opkg update || exit 1:这是脚本的“保险开关”。opkg update 负责更新软件列表,如果更新失败(例如网络不通),exit 1 会立即停止后续操作,防止在错误的环境下继续安装。
    • /tmp 目录:在内存中运行安装包,保护路由器的闪存(Flash)寿命。

    2.安装网络向导与首页 (ARM64 & x86-64 通用)

    安装完商店核心后,为了获得更好的交互体验和类似 iStoreOS 的首页外观,需要手动安装中文语言包和快速启动插件:

    执行指令:

    is-opkg install luci-i18n-quickstart-zh-cn

    作用说明:

    • is-opkg:这是 iStore 专用的包管理命令。
    • 首页功能:安装后,你的路由器后台会多出一个“首页”菜单,提供直观的设备状态监控、网络状态展示以及应用快捷入口。

    第五部分:安装Tailscale

    1. 准备工作

    访问 asvow/luci-app-tailscale 下载以下适用于 R2S 的特定版本文件:

    • 主程序:luci-app-tailscale_1.2.6_all.ipk
    • 简体中文包:luci-i18n-tailscale-zh-cn_250509.33792_all.ipk

    2. 安装核心依赖

    首先,我们需要安装 Tailscale 程序本身和 SSL 证书支持。

    opkg update
    opkg install tailscale ca-bundle

    3. 上传并覆盖安装插件

    将下载好的两个 .ipk 文件通过 WinSCP 上传到路由器的 /tmp 目录。

    注意:由于该插件会替换系统原有的启动脚本,必须使用 --force-overwrite 参数,否则会提示文件冲突。

    cd /tmp
    opkg install --force-overwrite luci-app-tailscale_1.2.6_all.ipk luci-i18n-tailscale-zh-cn_250509.33792_all.ipk

    4. 清理缓存与启动

    执行以下命令清理界面缓存并启动服务:

    rm -rf /tmp/luci-indexcache
    /etc/init.d/tailscale enable
    /etc/init.d/tailscale start

    5. 账号绑定与初始化

    • 进入后台:刷新路由器网页,在 “服务” -> “Tailscale” 即可看到控制界面。

    第六部分:Tailscale直连优化与避坑

    如果你希望手机在外面能通过 Tailscale 使用家里的 R2S 旁路由代理上网,并能像在家里一样管理主路由和 Docker 服务,以下设置是成功的关键。

    1. 联动 Nikki/Clash 翻墙与 MTU 优化

    在旁路由环境下,让代理插件感知并处理来自 Tailscale 的流量是第一步。由于 Tailscale 的流量经过加密封装,如果不将其接口告知代理插件,流量会绕过代理直接流出。

    在 Nikki 插件中进行如下设置:

    • Nikki 设置:进入“代理配置” -> “局域网代理”中,将 tailscale0 添加到入站接口(Inbound Interfaces)。这确保了手机传回 R2S 的流量在解密后能立即进入代理分流逻辑。

    为了防止因 MTU 大小不匹配导致的数据包分片丢失,建议在防火墙规则中添加以下 MSS 钳制命令。这能显著提升网页加载的顺滑度,防止因 5G 或公共 WiFi 环境导致的卡顿(可选):

    nft add rule inet fw4 forward oifname "eth0" tcp flags syn tcp option maxseg size set rt mtu
    

    2. 禁止Tailscale接管系统DNS(必做)

    Tailscale 默认接管了系统的 DNS 设置。 当它启动时,会尝试将系统的 DNS 修改为它自己的服务器(通常是 100.100.100.100),或者强制推送到 /etc/resolv.conf。在旁路由模式下,这往往会破坏原本指向主路由或 SmartDNS 的解析路径,导致系统无法解析外部域名,因此必须执行禁用。

    请在 SSH 终端执行以下命令:

    tailscale up --accept-dns=false --reset 

    --accept-dns=false:拒绝接受官方 DNS 设置。这是旁路由环境的“救命参数”,防止 Tailscale 修改系统 DNS 导致与 Nikki/Clash 产生死锁,确保 DNS 解析权掌握在 R2S 代理手上。

    下面的完整命令,包括:DNS 处理方式、路由宣告、出口节点能力,可在插件界面设置中设置路由宣告、出口节点(下面第3步在插件界面的操作等同于这一步的命令):

    tailscale up --reset --advertise-exit-node --advertise-routes=192.168.10.0/24 --accept-dns=false

    上述命令中参数的详细设置要点如下:

    • --advertise-exit-node:宣称自己为“出口节点”。它是实现“异地借线翻墙”的核心,允许其他设备将全局流量发给 R2S 转发。
    • --advertise-routes=192.168.10.0/24:宣称内网网段路由。让手机在外网也能像在家里一样,通过 192.168.10.x 访问 NAS 或管理主路由。

    3. 插件界面设置与官网后台“放行”

    在完成 SSH 命令后,还需要通过图形界面确认状态,并去 Tailscale 云端控制台手动授予转发权限。

    在 R2S 的 Tailscale 插件高级设置中确认以下项:

    • 启用路由:勾选(允许接受其他节点广播的子网)。
    • 允许 DNS:取消勾选(如果允许DNS,会造成软路由系统联网错误,不能下载istore应用等)。
    • 出口节点:勾选(确认为 Tailscale 广域网出口)。
    • 公开网段:确保列表中已添加 192.168.10.0/24。

    随后登录 Tailscale 官网后台进行 Edit route settings 操作:

    • 登录 Tailscale Admin Console。
    • 找到 R2S 设备,点击右侧三个点,选择 Edit route settings。
    • 必须勾选 Use as exit node:确认该设备可以作为全网出口。
    • 必须勾选 192.168.10.0/24:确认允许该设备转发此内网网段的流量。

    4. 解决“无法管理主路由 (10.1)”:NAT 规则配置

    即便开启了子网路由,由于主路由(如高恪)默认不认识 Tailscale 的 100.x 网段,回程包会发往 WAN 口导致丢失。我们需要在 R2S 的防火墙中配置 MASQUERADE(IP 伪装),让主路由以为是 R2S 本身在访问它,才可以在外网打开192.168.10.1主路由地址(最新测试,可选,没做也可以访问)。

    在“网络” -> “防火墙” -> “NAT 规则”中新建如下参数:

    • 名称:Tailscale-MASQ
    • 地址族限制:选择 仅 IPv4。
    • 协议:选择 任意。
    • 出站区域:选择 lan (对应 R2S 连接主路由的物理接口 br-lan)。
    • 源地址:选择 任意。
    • 目标地址:选择 任意。
    • 操作 (Action):选择 MASQUERADE - 自动重写源地址为出站接口 IP。

    5. 解决“打不开 Google.com”:DNS 覆盖(必做)

    这是最容易被忽视的坑:手机开启出口节点后,如果解析不到真实国外 IP,翻墙就会失败。

    操作路径:Tailscale 官网后台 -> DNS -> Nameservers -> Add nameserver。

    添加 Nameservers:

    • 添加 1.1.1.1 (Cloudflare):点击设置,开启 Use with exit node。确保手机拿到真实国际 IP 以触发代理。
    • 添加 223.5.5.5 (阿里):点击设置,开启 Use with exit node。保证国内 App 访问速度。
    • 开启覆盖:确保 Override local DNS 开关为 开启 (蓝色)。

    6. Docker 访问配置:打通虚拟网卡权限(必做)

    如果你在 Docker 中运行了服务(如 Navidrome 音乐服务器、Jellyfin 等),默认情况下 Tailscale 流量无法进入 Docker 虚拟网络。

    请在 R2S 后台的“Docker” -> “配置”中执行以下操作:

    • 找到 “允许的访问接口” 选项。
    • 在列表中手动勾选或填入 tailscale0。
    • 点击“保存并应用”。这一步能确保来自 Tailscale 隧道的流量可以穿透 Docker 的虚拟防火墙。

    7. 解决 Tailscale 节点显示 relay,无法直连问题

    有时候 Tailscale 节点虽然网络正常,但状态会显示:

    active; relay "sfo"

    表示当前通过 DERP 中继服务器通信,没有建立设备之间的 P2P 直连。

    例如:

    R2S → 手机:direct
    R2S → Windows:relay "sfo"

    这种情况不一定是网络问题,可能只是 Tailscale 节点连接状态没有及时刷新。

    排查网络

    分别执行:

    tailscale netcheck

    确认:

    UDP: true
    MappingVariesByDestIP: false

    说明 UDP 正常,NAT 类型正常,具备直连条件。

    解决方法

    在 Windows 端管理员 PowerShell 执行:

    Restart-Service Tailscale

    等待几十秒后重新查看:

    tailscale status

    如果显示:

    direct xxx.xxx.xxx.xxx:41641

    说明已经恢复 P2P 直连。

    常用检查命令

    查看节点状态:

    tailscale status

    检测网络:

    tailscale netcheck

    测试连接:

    tailscale ping 节点名称

    如果网络条件正常,但仍显示 relay,可以优先尝试重启 Tailscale 服务,让节点重新进行 NAT 穿透协商。

    8. 高恪 (GoCloud) 主路由映射与状态验证

    最后,需要在高恪主路由上为 Tailscale 开启“直连快车道”,以确保异地组网的低延迟体验。

    在高恪主路由中按以下路径和参数进行配置:

    • 路径:网络设置 -> 端口映射 -> 端口映射。点击“新建”:
    • 名称:Tailscale-Direct
    • 协议:UDP
    • 外部端口:41641
    • 内部 IP 地址:填写 R2S 的内网 IP(如 192.168.10.3)
    • 内部端口:41641
    • 端口回流:禁用(保持默认即可)。
    • 关闭 UPnP:建议在高恪中关闭 UPnP 功能,避免随机端口抢占导致手动映射失效。

    为了保证打洞成功率,请在 R2S 的防火墙常规设置中,将 入站数据、出站数据、转发 全部改为 “接受 (Accept)”。最后在 SSH 中输入以下命令验证是否成功直连:

    tailscale status --active
    

    判定标准:若显示 active; direct,说明 UDP 打洞成功,延迟最低。若显示 relay,则说明正在走服务器中转,请检查高恪端口映射是否生效。

    第六阶段:安装Cloudflare Tunnel

    • 软件安装:iStore 中常规安装 Cloudflare。
    • 令牌 (Token):直接粘贴以 ey... 开头的超长令牌。
    • 证书上传:在 Cloudflare 官网选择域名后,系统会自动下载 cert.pem,需在路由器插件界面对应的“源站证书”位置上传。

    第七阶段:媒体中心——Navidrome 防火墙神坑

    • 安装:Docker 部署 Navidrome 镜像。
    • 故障现象:服务正常但 http://192.168.10.3:4533 无法打开。
    • 解决方案:进入 网络 -> 防火墙 -> 常规设置,将 转发 (Forward) 设为 接受 (ACCEPT)。
    • 原理:这是为了打通 LAN 区域到 Docker 区域(172.17.x.x)的“跨区”传送门。

    其它操作

    GoWebDav:将剩余空间映射到 /mnt/tf-disk 后,在此插件中开启共享,方便网页端文件存取。

  • 利用 Nikki 节点为 Cloudflare Tunnel 与 FRP 开启“降维打击”级加速

    利用 Nikki 节点为 Cloudflare Tunnel 与 FRP 开启“降维打击”级加速

    对于很多玩 R2S(NanoPi R2S)的小伙伴来说,搭建好 Emby 或音乐服务器后的“最后 1 公里”往往最让人头疼:明明家里有百兆上行,但在外面用手机流量访问时,无论是 Cloudflare Tunnel 还是直连 VPS 的 FRP,速度经常只有几百 KB/s。

    今天我们要分享的是一套“降维打击”级的方案:利用 R2S 本地的 Nikki 插件,配合高速代理节点(如 IEPL 专线),为这些穿透隧道进行二次加速。

    一、 为什么你的隧道需要“二次加速”?

    无论是 Cloudflare Tunnel 还是 FRP,它们本质上都是在你的 R2S 和远程服务器之间建立了一条“加密管道”。

    • Cloudflare Tunnel:数据要绕道 Cloudflare 的全球边缘节点,而在国内直连这些节点的路由通常非常糟糕,且容易受到运营商的 QOS 限制。
    • FRP:虽然是直连你的 VPS,但如果 VPS 线路不是 CN2 GIA 等高端线路,国际出口的丢包率足以让你的视频流频繁缓冲。

    Nikki 的作用:它像是一个聪明的“交通警察”。通过配置规则,它能识别出这些隧道进程,并将原本要走公网“堵车路段”的数据包,强行塞进你购买的高速代理专线中。

    二、 实战篇:Cloudflare Tunnel 的调优艺术

    Cloudflare Tunnel 默认使用 QUIC (UDP) 协议,这在经过代理节点转发时效率极低。

    1. 修改启动协议

    首先,必须将 cloudflared 的传输协议改为 HTTP2 或 TCP。在 R2S 的 /etc/init.d/cloudflared 启动脚本中,加入以下参数:

    Bash

    procd_append_param command "--protocol" "http2" 
    

    改用 HTTP2 后,数据会封装在稳定的 TCP 流中,极大提升了在代理环境下的稳定性。

    2. Nikki 规则配置

    在 Nikki 的 YAML 配置文件中,我们需要开启“双重保险”规则:

    • 进程匹配:强制拦截 cloudflared 进程的所有流量。
    • 域名匹配:拦截所有指向 argotunnel.com 的请求(这是隧道的握手域名)。

    配置示例:

    YAML

    find-process-mode: always # 必须开启进程识别
    rules:
      - PROCESS-NAME,cloudflared,☁️ Cloudflare # 进程规则
      - DOMAIN-SUFFIX,argotunnel.com,☁️ Cloudflare # 域名补漏
    

    通过这种方式,你的 Cloudflare 隧道将不再直连,而是通过你的高速节点中转,实测下载速度能从几百 KB 稳定提升至 2MB/s 以上。

    三、 实战篇:FRP 配合 Nikki 的暴力提速

    相比 Cloudflare Tunnel,FRP 的协议更精简,配合 Nikki 加速后的效果往往更加惊人。

    1. 加速逻辑:处理“出站上传”

    很多用户误以为 Nikki 只能加速“下载”,其实它处理的是双向流量。当你通过 FRP 远程看片时,R2S 的 frpc 进程正在向 VPS “上传”数据。Nikki 会拦截这个出站连接,并通过全双工的高速节点进行推送。

    2. 配置技巧

    针对 FRP,由于其 IP 通常是固定的,建议直接使用 IP-CIDR 规则或 进程规则:

    YAML

    rules:
      - PROCESS-NAME,frpc,🚀 高速节点
      - IP-CIDR,你的VPS_IP/32,🚀 高速节点
    

    3. 性能榨取

    为了降低 R2S 那颗弱小 CPU 的负担,建议在 frpc.toml 中关闭 FRP 自身的加密(因为代理节点已经带了加密),并开启 tcpMux(多路复用),这样能显著降低视频拖动时的延迟。

    四、 深度对比:谁才是外网影音的最强方案?

    特性Cloudflare Tunnel + NikkiFRP + Nikki 加速
    配置难度较高(需修改协议脚本)简单(直接针对 IP 设规则)
    协议效率一般(HTTP2 有额外开销)极高(原生 TCP 转发)
    稳定性容易出现 stream closed 报错非常稳定
    安全性极高(隐藏真实 IP)一般(暴露 VPS IP)

    实测结论:如果你追求极致的视频播放体验,FRP + Nikki 加速 是目前 R2S 环境下的最优解。它不仅能提供更稳定的 2MB/s 以上上传带宽,还能有效避免 Cloudflare 常见的握手超时问题。

    五、 避坑指南:为什么你的加速没效果?

    1. 硬盘是基础:无论网络多快,如果你的 R2S 硬盘处于“只读 (Read-Only)”或文件系统损坏状态,I/O 阻塞会直接导致播放器缓冲。务必确保磁盘挂载状态为 rw。
    2. 转码是杀手:R2S 硬件性能有限,播放时请确保 Emby 显示为“直接播放”。如果触发转码,CPU 满载会导致网络协议栈处理变慢,产生 http2: stream closed 报错。
    3. 节点质量:加速的效果上限取决于你的代理节点。建议选择支持 HTTP2/gRPC 且带有中转专线的节点,以获得最低的丢包率。

    结语

    通过 Nikki 为内网穿透隧道“修路”,本质上是利用了现代代理工具强大的分流能力。对于 R2S 这种内存敏感型设备,这种“以空间换时间”的方案能让你在世界的任何角落,都能丝滑地享受家里的海量影音库。

  • 密码保护:构建基于 Hysteria 2 隧道加速的 FRP 内网穿透全攻略

    密码保护:构建基于 Hysteria 2 隧道加速的 FRP 内网穿透全攻略

    此内容受密码保护。如需查阅,请在下方输入密码。

  • 玩转 R2S 家庭服务器:Cloudflare Tunnel + 优选 IP 全家桶提速终极实操指南(自动化进阶版)

    玩转 R2S 家庭服务器:Cloudflare Tunnel + 优选 IP 全家桶提速终极实操指南(自动化进阶版)

    前言:为什么你的内网穿透那么慢?

    对于很多像我一样使用 NanoPi R2S 这种轻量级网关,且身处**移动宽带(无公网 IP)**环境的用户来说,Cloudflare Tunnel(Argo Tunnel)几乎是低成本实现内网穿透的唯一真神。它不需要公网 IP,也不需要去路由器做复杂的端口转发,就能通过安全隧道把内网服务发布到公网。

    但理想很丰满,现实很骨感。默认模式下,Cloudflare 的 Anycast(任播)网络会将你的流量随机分配到全球边缘节点。对于国内移动线路,流量经常绕道美国、欧洲或新加坡,导致延迟高达 300ms 以上,Jellyfin 看个海报墙都要转半天圈。

    本博文将记录如何通过“优选 IP”技术,将平均延迟降低至 60ms 以内,并实现“一次配置,终身受益”的子域名全自动化加速方案。


    一、 核心武器:CloudflareSpeedTest 与提速原理

    1. 什么是 CloudflareSpeedTest (CFST)?

    CloudflareSpeedTest 是一款开源的、专为寻找 Cloudflare 优质节点而设计的测速工具。它不仅测试 Ping 延迟(ICMP),更支持 TCPing(端口延迟)和 HTTPing(下载测速),能真实反映出节点对网页访问和流媒体传输的实际贡献。

    2. 加速原理:绕过“糟糕”的路由

    Cloudflare 全球有成千上万个节点,但并非所有节点都对中国移动宽带友好。

    • Anycast 的局限:运营商默认的 DNS 解析往往会将你带向拥堵的骨干网出口。
    • Hosts 劫持与 Dnsmasq 泛解析:通过优选工具筛选出物理距离最近、带宽最充裕的直连 IP(如移动专线的香港/新加坡节点),并强行在 R2S 本地将你的域名指向该 IP。
    • 原理:流量不再走“公海路由”,而是直接由 R2S 的 DNS 引擎导向最快节点,实现“直线超车”。

    二、 进阶策略:从手动硬编码到“泛域名自动化”

    在之前的版本中,我们通过脚本往 /etc/hosts 里写死域名。但这有一个巨大的短板:不支持通配符。每当你新增一个服务,就要改一次脚本。

    终极方案:我们利用 OpenWrt 自带的 Dnsmasq 功能。在 Dnsmasq 配置文件中,一行 address=/.sirenyan.cn/IP 即可实现泛域名劫持。这样,除了根域名 sirenyan.cn 外,所有现有的和未来新增的子域名(如 *.sirenyan.cn)都会自动享受加速。


    三、 第一阶段:环境准备与工具安装

    1. 硬件与系统

    • 硬件:NanoPi R2S。
    • 系统:ImmortalWrt 24.10.4。
    • 分区:Docker 分区已扩充至 20GB,足以应对后续各服务的缓存需求。

    2. 工具安装

    通过 SSH 连接到 R2S,创建目录并下载最新版 CloudflareST:

    Bash

    mkdir -p /opt/cf_speedtest && cd /opt/cf_speedtest
    # 下载 arm64 架构版
    wget -O CloudflareST_linux_arm64.tar.gz https://github.com/XIU2/CloudflareSpeedTest/releases/download/v2.2.5/CloudflareST_linux_arm64.tar.gz
    tar -zxf CloudflareST_linux_arm64.tar.gz
    chmod +x CloudflareST
    

    四、 第二阶段:编写“未来就绪”自动化脚本 (cfst_hosts.sh)

    请全选并替换你的脚本内容。此版本使用了 Dnsmasq 配置文件路径,确保全自动泛解析。

    Bash

    #!/bin/bash
    # =========================================================
    # 脚本名称:Cloudflare Tunnel 全自动泛域名加速脚本
    # 适用环境:R2S (ImmortalWrt) + 移动宽带
    # 特点:支持通配符,未来新增子域无需改动脚本
    # =========================================================
    
    WORK_DIR="/opt/cf_speedtest"
    # Dnsmasq 额外配置文件路径 (OpenWrt 标准路径)
    DNSMASQ_CONF="/etc/dnsmasq.d/cloudflare_speedtest.conf"
    DOMAIN="sirenyan.cn"
    
    cd $WORK_DIR
    
    # 1. 清理旧缓存
    rm -f result.csv
    
    # 2. 运行优选 (针对 R2S 性能调优)
    # -n 50: 降低线程保护 CPU;-tp 443: 必须测 HTTPS 端口;-ip: 移动宽带专用段
    echo "正在为移动线路探测最快节点..."
    ./CloudflareST -n 50 -t 4 -tp 443 -tl 200 -dn 1 -ip 104.16.0.0/16
    
    # 3. 提取最优 IP
    best_ip=$(sed -n '2p' result.csv | cut -d',' -f1)
    
    # 4. 判断并写入 Dnsmasq 泛域名配置
    if [ -n "$best_ip" ] && [ "$best_ip" != "IP 地址" ]; then
        echo "探测成功!最优节点 IP 为: $best_ip"
        
        # 使用 address 语法实现泛域名劫持:*.sirenyan.cn 全部生效
        # 这里加一个点表示包含子域,且不影响你可能在 hosts 里对主域名的特殊定义
        echo "address=/.$DOMAIN/$best_ip" > $DNSMASQ_CONF
        
        # 5. 重启 DNS 服务与隧道服务
        /etc/init.d/dnsmasq restart
        /etc/init.d/cloudflared restart 2>/dev/null
        echo "全自动加速已生效,所有 $DOMAIN 的子域名已指向最优节点。"
    else
        echo "优选失败,保持现有配置。"
    fi
    

    赋予权限并初次运行

    Bash

    chmod +x /opt/cf_speedtest/cfst_hosts.sh
    /opt/cf_speedtest/cfst_hosts.sh
    

    五、 第三阶段:解决代理与防火墙的“深度碰撞”

    很多用户提速后发现网络崩溃,通常是因为 R2S 内部的“交通管制”冲突。

    1. 解决 Nikki (Clash) 劫持问题

    现象:隧道连不上,报错 dial tcp 198.18.0.x:7844: i/o timeout。 原因:Nikki 尝试代理隧道自己的流量,导致死循环。 解决:在 Nikki 插件的“直连名单”中添加 sirenyan.cn 和 argotunnel.com。

    2. 开启防火墙“通行证”

    操作:进入 网络 -> 防火墙 -> 区域设置 -> lan。

    • 勾选“IP 动态伪装” (Masquerading)。
    • 勾选“MSS 钳制”。
    • 意义:这是保证内网电脑能通过 R2S 转发访问优选 IP 的关键。

    六、 最终验证:不仅是快,更是稳

    优化完成后,你可以尝试 Ping 任意一个你从未在脚本里写过的子域名,比如: ping whatever.sirenyan.cn -c 4

    数据分析:

    • 延迟:平均延迟由原来的 300ms 降至 60ms 左右。
    • 丢包率:从 10% 以上降至 0 丢包。
    • 下载带宽:测速可达 14.94 MB/s,足以让 Jellyfin 里的 4K 电影像本地一样播放。

    七、 总结与自动化运维

    通过 Dnsmasq 泛域名映射,我们不仅解决了当前服务的卡顿问题,更一劳永逸地处理了未来扩容服务的加速需求。

    自动化定时任务

    进入 系统 -> 计划任务,添加:

    代码段

    0 4 * * * /opt/cf_speedtest/cfst_hosts.sh > /dev/null 2>&1
    

    每天凌晨 4 点,R2S 会自动帮你找一次最快的节点,让你的内网穿透永远保持在“头等舱”速度。