Ubuntu 使用 sing-box 将 SOCKS5 转换为 TUN 全局系统代理

目录

在 Linux 下,很多程序支持通过 HTTP_PROXYHTTPS_PROXYALL_PROXY 使用代理,但也有不少程序不会读取这些环境变量。

如果已经有一个 SOCKS5 代理,可以借助 sing-box 的 TUN 模式将其转换为系统级透明代理,让应用程序无需单独配置代理。

本文记录一种比较实用的方案:

应用程序
    │
    ▼
Linux TUN
    │
    ▼
sing-box
    │
    ├── 局域网 ──────────────→ DIRECT
    │
    ├── 指定直连域名 ────────→ DIRECT
    │
    ├── DNS ─→ DoH ─────────→ SOCKS5
    │
    ├── 公网 TCP ────────────→ SOCKS5
    │
    └── 公网 UDP ────────────→ DROP

这种方案尤其适合上游 SOCKS5 只支持 TCP、不支持 UDP的情况。

一、为什么使用 TUN

传统代理通常需要应用程序主动支持 SOCKS5/HTTP Proxy。

例如:

export http_proxy=...
export https_proxy=...
export ALL_PROXY=...

这种方式存在一个明显限制:应用程序必须主动读取这些代理配置。

TUN 的思路不同。

sing-box 创建一个虚拟网络接口,并通过系统路由接管应用程序流量:

Application
    ↓
TUN
    ↓
sing-box
    ↓
SOCKS5
    ↓
Internet

因此大多数应用不需要知道代理的存在。

浏览器、命令行工具、开发工具等都可以通过系统 TUN 自动进入 sing-box。


二、安装 sing-box

安装完成后首先确认版本:

sing-box version

本文配置以较新的 sing-box 1.13 系列为例。

不同大版本之间 DNS、TUN 和路由配置格式可能存在变化,因此如果使用其他版本,建议先参考对应版本的官方文档。


三、基本 TUN 配置

TUN inbound 可以配置为:

{
  "type": "tun",
  "tag": "tun-in",
  "interface_name": "singtun0",
  "address": [
    "172.19.0.1/30"
  ],
  "auto_route": true,
  "auto_redirect": true,
  "strict_route": true,
  "stack": "system"
}

其中几个比较重要的参数:

  • auto_route:自动配置 TUN 所需路由。
  • auto_redirect:Linux 下结合 nftables 进行透明代理路由。
  • strict_route:使用更严格的路由策略。
  • stack: system:使用 Linux 系统网络栈。

创建成功后可以查看:

ip addr show singtun0

四、配置 SOCKS5 出站

假设已经有一个 SOCKS5 服务,可以定义:

{
  "type": "socks",
  "tag": "proxy",
  "server": "SOCKS5_SERVER",
  "server_port": 1080,
  "version": "5"
}

同时保留一个直连出口:

{
  "type": "direct",
  "tag": "direct"
}

最终主要存在两个出口:

proxy   → SOCKS5
direct  → 本地网络直接连接

五、防止代理回环

这是 TUN 配置中非常重要的一点。

如果 SOCKS5 服务器自身的连接再次被 TUN 接管:

sing-box
   ↓
SOCKS5 Server
   ↓
TUN
   ↓
sing-box
   ↓
SOCKS5 Server
   ↓
...

就可能产生代理回环。

因此应该让 SOCKS5 服务器地址明确直连:

{
  "ip_cidr": [
    "SOCKS5_SERVER/32"
  ],
  "outbound": "direct"
}

同时可以开启:

"auto_detect_interface": true

帮助 sing-box 为自身出站选择正确的物理网络接口。


六、局域网保持直连

如果希望访问路由器、NAS、SSH 主机等局域网资源时不经过代理,可以使用:

{
  "ip_is_private": true,
  "outbound": "direct"
}

这样常见私有网络:

10.0.0.0/8

172.16.0.0/12

192.168.0.0/16

都会保持直连。

最终形成:

LAN       → DIRECT
Internet  → PROXY

七、SOCKS5 不支持 UDP 怎么办

有些 SOCKS5 服务只支持 TCP。

这种情况下不建议简单配置:

UDP → DIRECT

因为这会让部分公网流量绕过代理。

更严格的方案是:

TCP → SOCKS5
UDP → DROP

对应规则:

{
  "network": "udp",
  "action": "reject",
  "method": "drop"
}

这样公网 UDP 不会从本地网络出口直接发送。

HTTP/3 / QUIC

HTTP/3 基于 QUIC,而 QUIC 使用 UDP。

因此:

HTTP/3
  ↓
UDP 443
  ↓
DROP

浏览器通常会回退到:

HTTPS / HTTP2
       ↓
     TCP 443
       ↓
    SOCKS5

对于 TCP-only SOCKS5,这是比较合理的处理方式。


八、DNS 是另一个需要处理的问题

仅仅让 TCP 经过 SOCKS5,并不代表 DNS 也一定经过代理。

例如 Linux 当前 DNS 服务器可能来自 DHCP:

Application
    ↓
UDP 53
    ↓
Local DNS Server

即使 HTTPS 流量经过 SOCKS5,DNS 仍可能使用本地网络。

因此可以让 sing-box 接管 DNS。

首先增加 DNS 路由:

{
  "port": 53,
  "action": "hijack-dns"
}

它应该位于普通私网直连规则之前。

例如:

DNS hijack
     ↓
SOCKS5 Server DIRECT
     ↓
Private Network DIRECT
     ↓
其他规则

这是因为系统 DNS 服务器本身可能就是一个私网地址。

如果先匹配:

"ip_is_private": true

DNS 请求可能会直接进入局域网,而不是被 sing-box 接管。


九、使用 DoH 解决 TCP-only SOCKS5 的 DNS 问题

普通 DNS 通常使用 UDP 53。

但上游 SOCKS5 不支持 UDP,因此可以将 DNS 转换成:

DNS over HTTPS(DoH)

例如:

{
  "type": "https",
  "tag": "dns-proxy",
  "server": "1.1.1.1",
  "server_port": 443,
  "path": "/dns-query",
  "detour": "proxy"
}

关键是:

"detour": "proxy"

这样 DNS 路径变成:

Application
     ↓
UDP/TCP 53
     ↓
TUN
     ↓
hijack-dns
     ↓
sing-box DNS
     ↓
DoH
     ↓
HTTPS / TCP 443
     ↓
SOCKS5
     ↓
DoH Server

这样即使 SOCKS5 完全不支持 UDP,DNS 仍然可以通过 TCP 完成。


十、添加指定域名直连

有时候并不希望所有网站都经过 SOCKS5。

可以建立一个直连域名白名单:

{
  "domain_suffix": [
    "example.com",
    "example.org"
  ],
  "outbound": "direct"
}

domain_suffix 会同时匹配主域名及其子域名。

例如:

example.com
www.example.com
api.example.com
cdn.example.com

都会匹配:

"domain_suffix": [
  "example.com"
]

如果只希望 TCP 直连:

{
  "network": "tcp",
  "domain_suffix": [
    "example.com",
    "example.org"
  ],
  "outbound": "direct"
}

这样可以保持统一策略:

指定域名 TCP → DIRECT
其他公网 TCP → SOCKS5
公网 UDP     → DROP
DNS          → DoH → SOCKS5

十一、推荐的规则顺序

sing-box 的规则顺序非常重要。

一种比较清晰的结构是:

1. sniff

2. DNS :53
   → hijack-dns

3. SOCKS5 Server
   → DIRECT

4. Private Network
   → DIRECT

5. 指定域名 TCP
   → DIRECT

6. Public UDP
   → DROP

7. 其他流量
   → SOCKS5

例如:

"route": {
  "rules": [
    {
      "action": "sniff"
    },
    {
      "port": 53,
      "action": "hijack-dns"
    },
    {
      "ip_cidr": [
        "SOCKS5_SERVER/32"
      ],
      "outbound": "direct"
    },
    {
      "ip_is_private": true,
      "outbound": "direct"
    },
    {
      "network": "tcp",
      "domain_suffix": [
        "example.com"
      ],
      "outbound": "direct"
    },
    {
      "network": "udp",
      "action": "reject",
      "method": "drop"
    }
  ],
  "final": "proxy",
  "auto_detect_interface": true
}

十二、检查配置

修改配置以后,不建议立即重启服务。

首先执行:

sudo sing-box check -c /etc/sing-box/config.json

确认没有错误后再:

sudo systemctl restart sing-box

设置开机启动:

sudo systemctl enable sing-box

查看状态:

systemctl status sing-box --no-pager

查看实时日志:

journalctl -u sing-box -f

十三、检查 TUN 和系统路由

检查 TUN:

ip addr show singtun0

检查策略路由:

ip rule

如果启用了 auto_redirect,还可以检查 nftables:

sudo nft list ruleset | grep -i sing

十四、验证 TCP 是否经过代理

首先通过 SOCKS5 显式查询公网 IP:

curl --socks5-hostname SOCKS5_SERVER:1080 \
    https://api.ipify.org
echo

记录返回的公网 IP。

然后不指定任何代理:

curl https://api.ipify.org
echo

如果两个结果一致,基本说明:

curl
 ↓
TUN
 ↓
sing-box
 ↓
SOCKS5

已经正常工作。

此时不需要额外设置:

HTTP_PROXY
HTTPS_PROXY
ALL_PROXY

十五、验证 UDP 是否被阻止

需要注意,Ubuntu 官方仓库中的 curl 即使版本较新,也不一定编译了 HTTP/3 支持。

可以通过:

curl --version

查看 Features。

如果只有:

HTTP2

而没有:

HTTP3

那么:

curl --http3-only ...

无法用于测试 QUIC。

可以直接使用抓包验证。

首先找到默认物理网卡:

ip route | grep default

然后:

sudo tcpdump -ni <网卡> 'udp'

另一个终端主动产生公网 UDP 流量,再观察物理网卡是否出现对应数据包。

如果策略正确:

公网 UDP
   ↓
TUN
   ↓
sing-box
   ↓
DROP

那么不应该从物理网络接口看到对应的公网 UDP 数据包直接发送出去。


总结

对于一个​只支持 TCP 的 SOCKS5 服务​,使用 sing-box TUN 做 Linux 系统级代理时,可以采用:

Linux
                      │
                     TUN
                      │
                   sing-box
                      │
       ┌──────────────┼──────────────┐
       │              │              │
      LAN            DNS            TCP
       │              │              │
    DIRECT        hijack-dns       SOCKS5
                      │
                     DoH
                      │
                   SOCKS5

公网 UDP ─────────────────────────→ DROP

几个最重要的原则是:

  1. SOCKS5 服务器自身必须避免进入代理回环。
  2. 局域网可以通过 ip_is_private 保持直连。
  3. TCP-only SOCKS5 不应把公网 UDP 简单改成直连。
  4. DNS 可以通过 hijack-dns + DoH + SOCKS5 解决 UDP 限制。
  5. DNS 劫持规则应该位于私网直连规则之前。
  6. 需要直连的网站可以通过 domain_suffix 建立白名单。
  7. 使用 network: tcp 可以只允许白名单域名的 TCP 直连,同时继续阻止公网 UDP。
  8. 修改配置后先使用 sing-box check 验证,再重启 systemd 服务。

最终可以实现一种非常清晰的 Linux 网络策略:

局域网                  → DIRECT
指定白名单域名 TCP       → DIRECT
其他公网 TCP             → SOCKS5
DNS                     → DoH → SOCKS5
其他公网 UDP             → DROP

相比单纯设置 HTTP_PROXY / ALL_PROXY,TUN 的优势在于它工作在系统网络层,对应用程序基本透明,更适合需要统一管理 Linux 整机网络流量的场景。