内网穿透折腾记

服务端

为什么不使用 FRP / WireGuard / TailScale / ZeroTier: 不希望内网服务直接暴露在公网,公司电脑限制部分 VPN 软件的安装,iOS 端不方便连接自建服务器,也无法同时连接多个 VPN,等等。

为什么不使用宽带公网 IP + DDNS:同出于安全考虑不会直接暴露服务,不走 VPN 理由同上,实际上我是分别在家里和 VPS 同时都有搭建代理,前者通过公网 IP 访问,后者加了一层中转,以备不时之需。

服务端配置和正常搭梯子区别不大,家里我开的 SS,VPS 使用 V2Ray(WS + TLS 以免被误伤。我使用的是纯 V2Ray 反向代理方案,参见 这篇文章,也可以考虑 V2Ray + FRP)。

客户端

因为我有多个不同用途的代理以及多台设备(macOS、iOS、OpenWRT),为了方便管理,统一使用 SubConverter 生成不同客户端的配置文件

我的目标是能够智能分流,在家的时候不需要走反代,外出的时候 192.168.0.0/16 这个 IP 段能实现内网穿透。此外这个规则允许在客户端进行修改,比如外出时候可以不走反代。

以生成圈 X 配置文件为例。

我是用的是 Docker 版本的 SubConverter, 然后通过不同的 Profile 来配置不同类型的订阅文件。docker-compose.yml 如下:

version: '3.9'
services:
    subconverter:
        container_name: subconverter
        restart: unless-stopped
        ports:
            - 41022:25500
        volumes:
            - ./proxies:/base/proxies # used to store my proxy nodes
            - ./custom_rules:/base/custom_rules # used to store additional rules            
            - ./pref.toml:/base/pref.toml # used to specify token for profiles
            - ./profiles:/base/profiles # used to store profiles for different targets
            - ./custom_config:/base/custom_config # used to store additional preferences
            - ./custom_sub_template/quanx.conf:/base/base/quanx.conf # used to replace subscription template file for Quanxx
        image: ihainan/subconverter:latest

替换默认的 pref.toml 是为了更新 token,而非使用默认的 token,从而确保安全。

把我的代理连接信息放到 /base/proxies/proxies.list 文件中,挂载到容器:

port: 7890
socks-port: 7891
allow-lan: true
mode: Rule
log-level: info
external-controller: :9090
proxies:
  - {name: NT-BJ, server: proxy.example.com, port: <port>, type: vmess, uuid: <uuid>, alterId: 0, cipher: auto, tls: true, skip-cert-verify: true, network: ws, ws-opts: {path: /ray2, headers: {Host: proxy.example.com}}, udp: true}  

custom_rules/home-rule.list 文件定义我家的 IP 段,后面会让这个 IP 段分配规则:

# for NAT only
IP-CIDR,192.168.0.0/16,no-resolve

profiles 里面存储具体的 profile 文件,比如圈 X 的配置文件 quanx.conf 内容如下:

[Profile]
target=quanx
url=proxies/proxies.list
config=custom_config/pref.ini
udp=true
expand=true

custom_config 里面存储要覆盖 pref.toml 的额外配置项,例如给圈 X 用的 pref.ini

[custom]
;设置规则标志位,省略了无关其他规则集
ruleset=🏡 内网穿透,custom_rules/home-rule.list
ruleset=🎯 全球直连,https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Surge/Lan/Lan.list
; 其他规则集...
设置规则标志位

;设置分组标志位
custom_proxy_group=🏡 内网穿透`ssid`🌊 NAT优先`🌊 NAT优先`IHAINAN_5G=💧 直连优先`IHAINAN=💧 直连优先`IHAINAN-5G_GAME=💧 直连优先
custom_proxy_group=💧 直连优先`select`[]DIRECT`.*`[]♻️ 自动选择
custom_proxy_group=🌊 NAT优先`select`NAT`[]DIRECT
; 其他代理组...
;设置分组标志位

quanx_rule_base=base/quanx.conf
; 其他配置项...

这里有几个需要注意的点。

首先需要定义规则集 🏡 内网穿透 指向我们前面指定的 192.168.0.0/16 IP 段。

接下来为了方便在手机、Wi-Fi 不同 SSID 中间使用不同的规则,我定义了 💧 直连优先 代理组优先选择直连,🌊 NAT优先 代理组优先选择我们的代理,这两个可以手动在客户端选择其他的节点。

然后规定当访问 🏡 内网穿透 规则集定义的 IP 段,如果是 Wi-Fi 环境并且 SSID 为 IHAINAN 开头时,会切换成 💧 直连优先,而在其他时候,包括移动网络和其他 SSID,会走 🌊 NAT优先 指向的节点。

SubConverter 目前 存在一个 bug,导致我的 SSID 规则生成的订阅文件,会被圈 X 以语法解析错误拒绝加载,所以 我自己做了修改 并构建了一个 新的 Docker image 自用。

此外 SubConverter 默认的 圈 X 订阅模板文件 会把 192.168.0.0/16 网段加到 excluded_routes 中,所以我需要覆盖这个模板文件,同时因为 另一个疑似 bug 导致本地的 quanx.conf 只能使用特定路径,所以我在 docker-compose.xml 里面挂载了 /base/base/quanx.conf 同时在 pref.ini 里面添加了 quanx_rule_base=base/quanx.conf。文件内容如下:

[general]
excluded_routes=172.16.0.0/12, 100.64.0.0/10, 10.0.0.0/8
geo_location_checker=http://ip-api.com/json/?lang=zh-CN, https://github.com/KOP-XIAO/QuantumultX/raw/master/Scripts/IP_API.js
network_check_url=http://www.baidu.com/
server_check_url=http://www.gstatic.com/generate_204

[dns]
server=119.29.29.29
server=223.5.5.5
server=1.0.0.1
server=8.8.8.8

[policy]

[server_remote]

[filter_remote]

[rewrite_remote]

[server_local]

[filter_local]

[rewrite_local]

[mitm]