在Sing-box上使用WireGuard

注意⚠️ 本文存在一些疏漏,请不要照搬。有空我会逐个修复。

此外,我不再推荐使用第三方图形化的sing-box客户端,我已经不止一次遇到图形上的性能问题。

以下为文章:

这次折腾,是为了能够在singbox上使用WireGuard,为指定程序走WireGuard出站,实现远程异地组网。

以前这比较好写配置文件,但是随着singbox1.11.0诞生,配置文件的很多字段发生了更改,基本上大改了,之前有的字段已经废弃了,于是本文记录踩坑过程(~o ̄3 ̄)~

预备知识

假设你了解局域网,假设你知道手机热点大概工作原理,假设你基本了解网络拓扑,假设你基本了解云服务器,假设你基本了解Linux。

需求:

  • 可以随时随地远程连接WireGuard局域网里的设备
  • 可以随时随地访问家里的局域网里的设备
  • 拥有良好远程串流体验

要达到的效果

  • 家中任何一台电脑,运行SingBox,就可以通过这台机器接入WireGuard网络。

    实现出门在外随时随地连接到家里的设备,也可以与家里的设备串流。

目前环境

设备:
ECS服务器:Ubuntu 24.04.1 LTS
PC:Windows 11 安装了 GUI.for.SingBox
平板:HUAWEI Matepad Pro 11 冤种2022款 安装了WireGuard客户端
手机:HUAWEI P30 Pro 安装了WireGuard客户端

模拟的网络布局

这里用Cisco Packet Tracer连几根线意思一下,示意图。

cisco

家中网络 WireGuard网络 远程网络
192.168.6.0/24 10.1.0.0/24 叽叽咕咕我不知道是啥捏

开始战斗了⚔️

服务器里的设置:

首先你要有一台公网IP的云服务器。

我选择的是一年99元阿里云ECS,不需要域名,省去备案的麻烦。

安装WireGuard和ufw

bash
apt update
apt install -y wireguard ufw
ufw allow 22/tcp
ufw allow 22/udp
ufw allow 51820/tcp
ufw allow 51820/udp
ufw enable
sysctl -w net.ipv4.ip_forward=1

生成密钥对:

bash
cd /etc/wireguard
wg genkey | tee gw-privatekey | wg pubkey > gw-publickey
wg genkey | tee mypc-privatekey | wg pubkey > mypc-publickey
wg genkey | tee matepad-privatekey | wg pubkey > matepad-publickey

生成云服务器上的配置:

bash
cat > wg0.conf <<EOF
[Interface]
ListenPort = 51820 # 客户端连过来填写的端口,安全组的tcp和udp都要放行
Address = 10.1.0.1/24  #wg之前通信组网的内网ip和段
PrivateKey = $(cat gw-privatekey)   # 使用 shell 读取gateway的私钥到这里

PostUp = iptables -I FORWARD -s 10.1.0.0/24 -i wg0 -d 10.1.0.0/24 -j ACCEPT
PostUp = iptables -I FORWARD -s 10.1.0.0/24 -i wg0 -d 192.168.6.0/24 -j ACCEPT
PostUp = iptables -I FORWARD -s 192.168.6.0/24 -i wg0 -d 10.1.0.0/24 -j ACCEPT

PostDown = iptables -D FORWARD -s 10.1.0.0/24 -i wg0 -d 10.1.0.0/24 -j ACCEPT
PostDown = iptables -D FORWARD -s 10.1.0.0/24 -i wg0 -d 192.168.6.0/24 -j ACCEPT
PostDown = iptables -D FORWARD -s 192.168.6.0/24 -i wg0 -d 10.1.0.0/24 -j ACCEPT

# mypc
[Peer]
PublicKey = $(cat pc-publickey)
AllowedIPs = 10.1.0.2/32,192.168.6.0/24

# matepad
[Peer]
PublicKey = $(cat home-pc-publickey)
AllowedIPs = 10.1.0.3/32

EOF

家中电脑的配置:

bash
cat > mypc.conf <<EOF
[Interface]
PrivateKey = $(cat mypc-privatekey)
Address = 10.1.0.2/24 #wg之前通信组网的内网ip和段,主机位每个得不一样

[Peer]
PublicKey = $(cat gw-publickey)  # gateway的公钥
AllowedIPs = 10.1.0.0/24, 192.168.6.0/24
Endpoint = $(curl -s ip.sb):51820 #gateway 公网ip和端口
PersistentKeepalive = 30 # 心跳时间
EOF

平板的配置:

bash
cat > matepad.conf <<EOF
[Interface]
PrivateKey = $(cat matepad-privatekey)
Address = 10.1.0.3/24 #wg之前通信组网的内网ip和段,主机位每个得不一样

[Peer]
PublicKey = $(cat gw-publickey)   # gateway的公钥
AllowedIPs = 10.1.0.0/24
Endpoint = $(curl -s ip.sb):51820  # gateway 公网ip和端口
PersistentKeepalive = 10 # 心跳时间
EOF

在云服务器上启动WireGuard服务( ̄︶ ̄)

bash
wg-quick up wg0 
# WireGuard会默认读取 /etc/wireguard/$name.conf 作为启动的配置文件
# 若要手动指定配置文件启动WireGuard: wg-quick up /etc/wireguard/wg0.conf
wg-quick down wg0

如果要查看组网状态,输入 wg 回车即可

WireGuard半自动搭建脚本:

SingBox配置:

我的老天爷,这玩意儿的配置折腾了好久好久,在SingBox里配置WireGuard总是出现问题。

结果是需要为endpoints里加入detour字段,设置endpoints的出站,但他文档里没写哇。

(╬▔皿▔)╯!!!!!!!

因此需要在SingBox配置里加入endpoints的部分:

json
{ 
  "endpoints": [
    {
      "type": "wireguard",
      "tag": "wg-ep",
      "system": false,
      "name": "wg0",
      "mtu": 1408,
      "detour": "🎯 全球直连",
      "address": [
        "10.1.0.2/32"
      ],
      "private_key": "私钥",
      "peers": [
        {
          "address": "47.119.127.104",
          "port": 51820,
          "public_key": "公钥",
          "allowed_ips": [
            "10.1.0.0/24","192.168.6.0/24"
          ],
          "persistent_keepalive_interval": 30,
          "reserved": [ 0, 0, 0 ]
        }
      ]
    }
  ]  
}

整个配置的结构示意:

注意哦⚠️,这里跟以前不一样了,WireGuard配置不需要写在outbounds里了,而是单独拿出来写在了endpoints里。

json
{
  "log": {},
  "dns": {},
  "ntp": {},
  "endpoints": [],//在这里插入配置
  "inbounds": [],
  "outbounds": [],
  "route": {},//在这里加入路由
  "experimental": {}
}

针对几个远程串流的软件,路由规则还需要加上:

json
"process_name": [
	"parsecd.exe",
	"pservice.exe",
	"mstsc.exe",
	"sunshine.exe"
], 
 "outbound": "wg-ep"

这是我不知道从哪里抄来,然后简单改改的,单个可以启动的SingBox配置文件:

json
{
  "log": {
    "disabled": false,
    "level": "info"
  },
  "dns": {
    "servers": [
      {
        "address": "udp://223.6.6.6"
      }
    ]
  },
  "inbounds": [
    {
      "type": "tun",
      "address": [
        "172.18.0.1/30"
      ],
      "auto_route": true,
      "route_address": [
        "10.1.0.0/24"
      ],
      "route_exclude_address": [
        "47.119.127.104/32"
      ],
      "stack": "gvisor"
    }
  ],
  "endpoints": [
    {
      "type": "wireguard",
      "tag": "out-proxy",
      "system": false,
      "name": "wg0",
      "mtu": 1408,
      "detour": "out-direct",
      "address": [
        "10.1.0.2/32"
      ],
      "private_key": "私钥",
      "peers": [
        {
          "address": "47.119.127.104",
          "port": 51820,
          "public_key": "公钥",
          "allowed_ips": [
            "0.0.0.0/0"
          ],
          "persistent_keepalive_interval": 10,
          "reserved": [ 0, 0, 0 ]
        }
      ]
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "out-direct"
    }
  ],
  "route": {
    "rules": [
      {
        "type": "logical",
        "mode": "and",
        "rules": [
          {
         "process_name": [
        "parsecd.exe",
        "pservice.exe",
        "mstsc.exe",
        "cmd.exe"
      ],            "ip_cidr": [
              "10.1.0.0/24"
            ]
          }
        ],
        "outbound": "out-proxy"
      }
    ],

    "auto_detect_interface": true
  }
}

针对GUI.for.SingBox所做的设置:

在配置里的【混入和脚本】所做的设置:

混入设置:

json
{ 
  "endpoints": [
    {
      "type": "wireguard",
      "tag": "wg-ep",
      "system": false,
      "name": "wg0",
      "mtu": 1408,
      "detour": "🎯 全球直连", //1.12.0-beta已经不在需要,这个参数更像是用来掩盖问题的
      "address": [
        "10.1.0.2/32"
      ],
      "private_key": "私钥",
      "peers": [
        {
          "address": "47.119.127.104",
          "port": 51820,
          "public_key": "公钥",
          "allowed_ips": [
            "10.1.0.0/24","192.168.6.0/24"
          ],
          "persistent_keepalive_interval": 30,
          "reserved": [ 0, 0, 0 ]
        }
      ]
    }
  ]  
}

脚本操作:

javascript
const onGenerate = async (config) => {
  config.inbounds.forEach((inbound) => {
    if (true) {
      if (inbound.route_address) {
        inbound.route_exclude_address = [
          "47.119.127.104/32",
        ];
      }
    }
  });

  config.route.rules.push({ 
        "process_name": [
          "parsecd.exe",
          "pservice.exe",
          "mstsc.exe",
          "sunshine.exe"
        ], //"ip_cidr": [
          //"10.1.0.0/24"
        //],
    "outbound": "wg-ep"
  });

  return config;
};

真是一场酣畅淋漓的战斗⚔️💦

经过测试,如果需要访问家里局域网的设备,需要在云服务器配置里的“allowed_ips”,加上局域网的网络,同时在局域网里的设备(任意一台)的“allowed_ips”也要加上局域网的网络。也就说这台设备是是内网与外部网络的互访的桥梁。

如果没有加入局域网的网络,Sunshine串流会失败,暂未找到具体的原因。其他远程访问方式正常(微软远程桌面,Todesk,Parsec)

原因已经找到:在sing-box中需要加入路由才可以正常访问。

示例如下:

json
{
    "action": "route",
    "ip_cidr": "10.66.66.0/24",
    "outbound": "wg-ep"
},

如果加入了局域网的网络,一切正常,Sunshine可以正常串流。

目前确定不是Windows防火墙的问题

不是云服务防火墙的问题(100%确定)

参考:

https://zhangguanzhang.github.io/2020/08/05/wireguard-for-personal/#/centos7

https://nxw.name/2023/wireguard