在 Linux 下,很多程序支持通过 HTTP_PROXY、HTTPS_PROXY 或 ALL_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
几个最重要的原则是:
- SOCKS5 服务器自身必须避免进入代理回环。
- 局域网可以通过
ip_is_private保持直连。 - TCP-only SOCKS5 不应把公网 UDP 简单改成直连。
- DNS 可以通过
hijack-dns + DoH + SOCKS5解决 UDP 限制。 - DNS 劫持规则应该位于私网直连规则之前。
- 需要直连的网站可以通过
domain_suffix建立白名单。 - 使用
network: tcp可以只允许白名单域名的 TCP 直连,同时继续阻止公网 UDP。 - 修改配置后先使用
sing-box check验证,再重启 systemd 服务。
最终可以实现一种非常清晰的 Linux 网络策略:
局域网 → DIRECT
指定白名单域名 TCP → DIRECT
其他公网 TCP → SOCKS5
DNS → DoH → SOCKS5
其他公网 UDP → DROP
相比单纯设置 HTTP_PROXY / ALL_PROXY,TUN 的优势在于它工作在系统网络层,对应用程序基本透明,更适合需要统一管理 Linux 整机网络流量的场景。