0%

【问题解决】科学上网简单搭建

linux下科学上网简单搭建

linux下利用shadowsock+kcptun+udp2raw的科学上网搭建。

本文适读人群

  1. 对 linux 服务有简单了解
  2. 对科学上网稳定性有强迫,在使用 shadowsocks+kcptun+udp2raw 会让科学上网非常稳定不容易掉包

本文能够知道什么

  1. linux 下成功使用 shadowsocks+kcptun+udp2raw 搭建科学上网
  2. 常见故障怎么定位(key 不一致、MTU 溢出、换网络后失效、TUN 冲突)

本文不知道什么

  1. 搭建工具原理

链路总览

先把数据流搞清楚,后面每一步都对得上号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
浏览器 / 终端
│ SOCKS5 127.0.0.1:9872 (TCP)
▼
sslocal (shadowsocks 客户端)
│ TCP 加密流量 → 127.0.0.1:30001
▼
kcptun client (KCP,UDP 承载)
│ UDP → 127.0.0.1:30001
▼
udp2raw client (把 UDP 伪装成假 TCP,faketcp)
│ UDP 4096 → 服务器公网 IP:4096
═══════════ 公网 ═══════════
│
udp2raw server :4096
│ TCP → 127.0.0.1:8400
▼
kcptun server :8400
│ TCP → 127.0.0.1:39201
▼
ssserver :39201
│
▼
互联网

各环节端口对应关系:

环节 协议 端口
本机 SOCKS5 TCP 9872
sslocal → kcptun client TCP 30001
kcptun client → udp2raw client UDP 30001
udp2raw client → udp2raw server UDP 4096
udp2raw server → kcptun server TCP 8400
kcptun server → ssserver TCP 39201

30001 同时出现两次是正常的:kcptun 监听 TCP 30001 给 sslocal 连,udp2raw 监听 UDP 30001 给 kcptun 连,协议不同不冲突。

VPS 购买

  1. 我买的是 digitalocean 价格在 5 刀一个月偏贵,只要是国外服务器就行。

防火墙只要开一个口

  • 只需要放行 UDP 4096 入站,其它(22 除外)全部关掉。
  • 8400、39201 是纯本地回环,绑定 127.0.0.1 即可,不要对外暴露。
1
2
sudo ufw allow 4096/udp
sudo ufw enable

shadowsocks搭建

  1. 下载 shadowsocks 已发布的版本 https://github.com/shadowsocks/shadowsocks-rust/releases;
  2. 选 x86_64-unknown-linux-musl 那个包(静态链接,不依赖系统 glibc,扔到任何发行版都能跑):
1
2
wget https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.6.10/shadowsocks-v1.6.10-release.x86_64-unknown-linux-musl.tar.xz
tar -xf shadowsocks-v1.6.10-release.x86_64-unknown-linux-musl.tar.xz
  1. 压缩包解压后我们要使用的有两个文件:ssserver(服务端)、sslocal(客户端)。

注意:新版 shadowsocks-rust 的配置文件字段改过(变成 servers / locals 数组),老版本用单个 JSON 对象的写法在新版会报错。下面统一改用命令行参数,跨版本最稳。

客户端配置

  1. 将 sslocal 拷贝至 /usr/local/bin/ 下
1
sudo cp sslocal /usr/local/bin/
  1. 创建开机启动服务
1
sudo vim /lib/systemd/system/ss-server.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=shadowsocks local client (SOCKS5)
After=network-online.target kcptun.service
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/sslocal -b 127.0.0.1:9872 -s 127.0.0.1:30001 -k "你的密码" -m aes-256-gcm
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target

参数含义:-b 本地 SOCKS5 监听地址;-s 上游地址,也就是 kcptun 客户端的 TCP 30001。

  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start ss-server.service
sudo systemctl enable ss-server.service

服务端配置

  1. 将 ssserver 拷贝至 /usr/local/bin/ 下
1
sudo cp ssserver /usr/local/bin/
  1. 创建开机启动服务
1
sudo vim /lib/systemd/system/ss-server.service
1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=shadowsocks server
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/ssserver -b 127.0.0.1:39201 -k "你的密码" -m aes-256-gcm
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target

**这里必须绑 127.0.0.1**。老版本照着抄 "server": "::" 会把 39201 暴露在公网,等于绕过 udp2raw + kcptun 直连 shadowsocks,既失去抗封锁效果又多一个被扫的入口。

  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start ss-server.service
sudo systemctl enable ss-server.service

kcptun搭建

  1. 下载 kcptun 已发布的版本 https://github.com/xtaci/kcptun/releases,选 linux-amd64 包(文件名形如 kcptun-linux-amd64-<日期>.tar.gz,日期随版本变,用最新的那个):
1
2
3
# 以 20251017 为例,换成你下载到的实际文件名
wget https://github.com/xtaci/kcptun/releases/download/v20251017/kcptun-linux-amd64-20251017.tar.gz
tar -xf kcptun-linux-amd64-20251017.tar.gz

国内访问 GitHub Releases 经常失败,可以从 SourceForge 镜像拿:https://sourceforge.net/projects/kcptun.mirror/files/,文件完全一样。

  1. 解压后要用两个文件:server_linux_amd64(服务端)、client_linux_amd64(客户端)。

客户端配置

  1. 将 client_linux_amd64 拷贝至 /usr/local/bin/ 下
1
sudo cp client_linux_amd64 /usr/local/bin/
  1. 创建开机启动服务
1
sudo vim /lib/systemd/system/kcptun.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=kcptun client
After=network-online.target udp2raw.service
Wants=network-online.target udp2raw.service

[Service]
Type=simple
ExecStart=/usr/local/bin/client_linux_amd64 -l :30001 -r 127.0.0.1:30001 --crypt salsa20 --key "你的KEY" --nocomp --mtu 1200
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target

三个容易踩的点:

  • --mtu 1200 不能省。kcptun 默认 1350,加上 28 字节 IP/UDP 头就是 1378 字节,超过 udp2raw 单包上限 1375,日志会刷 huge packet,data len=1378 (>=1375),表现为能连上但速度很怪。
  • --nocomp 必须和服务端 "nocomp": true 一致。
  • 老版本抄来的 PIDFile=/var/run/kcp-server.pid 是无效行——kcptun 不写 pid 文件,Type=simple 下写它只会让 systemd 打警告,删掉。
  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start kcptun.service
sudo systemctl enable kcptun.service

服务端配置

  1. 将 server_linux_amd64 拷贝至 /usr/local/bin/ 下
1
sudo cp server_linux_amd64 /usr/local/bin/
  1. 创建配置文件
1
sudo vim /etc/kcptun.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"listen": ":8400",
"target": "127.0.0.1:39201",
"crypt": "salsa20",
"mode": "fast2",
"key": "你的KEY",
"nocomp": true,
"mtu": 1200,
"keepalive": 10,
"nodelay": 0,
"dscp": 0,
"sockbuf": 4194304,
"nc": 1,
"resend": 2,
"parityshard": 3,
"rcvwnd": 4096,
"interval": 20,
"sndwnd": 4096,
"datashard": 10
}

mtu 两端必须一致,客户端 --mtu 1200 服务端就写 1200。
target 指向本机 ssserver 的 39201,listen 监听 8400,两个都在回环里,不要绑 0.0.0.0。

  1. 创建开机启动服务
1
sudo vim /lib/systemd/system/kcptun.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=kcptun server
After=network-online.target ss-server.service
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/server_linux_amd64 -c /etc/kcptun.json
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target

ExecStart 里的文件名要和拷贝的文件名一致。官方包里的二进制叫 server_linux_amd64,不是 kcptun;两者不一致会直接 status=203/EXEC 起不来。

  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start kcptun.service
sudo systemctl enable kcptun.service

udp2raw搭建

  1. 下载 udp2raw 已发布的版本 https://github.com/wangyu-/udp2raw-tunnel/releases;
  2. 解压后使用对应架构的 udp2raw_amd64,udp2raw 服务端客户端是同一个文件。

客户端配置

  1. 将 udp2raw_amd64 拷贝至 /usr/local/bin/ 下
1
sudo cp udp2raw_amd64 /usr/local/bin/
  1. 先看一个能跑但不够用的版本
1
2
[Service]
ExecStart=/usr/local/bin/udp2raw_amd64 -c -l 0.0.0.0:30001 -r 服务器IP:4096 -k "你的KEY"
  1. 实际推荐:加 --lower-level auto --source-ip --dev

udp2raw 的 faketcp 是自己在二层拼包发出去的。如果本机装了 Mihomo / Clash 之类会建 TUN 设备接管全局路由的软件,内核给它选的源地址会变成 TUN 的地址(比如 198.18.0.1),伪 TCP 的 SYN 会被对端直接 RST,日志永远是 client_tcp_handshake 反复重发 SYN。

需要显式告诉它走哪块网卡、用哪个源 IP:

1
2
sudo /usr/local/bin/udp2raw_amd64 -c -l 0.0.0.0:30001 -r 服务器IP:4096 -k "你的KEY" \
--lower-level auto --source-ip 192.168.1.23 --dev wlan0

--source-ip 必须填主路由表里的真实局域网 IP,不是 TUN 地址;--dev 是对应网卡。手工写死的问题是换个 WiFi 就失效,所以做成启动时自动探测的 wrapper:

1
sudo vim /usr/local/bin/udp2raw-client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/sh
set -eu
SERVER=服务器IP
REMOTE_PORT=4096
LOCAL_PORT=30001
KEY='你的KEY'
BIN=/usr/local/bin/udp2raw_amd64

detect() {
dev=$(ip -o route show table main default 2>/dev/null |
awk '{for (i = 1; i <= NF; i++) if ($i == "dev") {print $(i + 1); exit}}')
[ -n "${dev:-}" ] || return 1
src=$(ip -o -4 addr show dev "$dev" scope global 2>/dev/null |
awk '{print $4; exit}' | cut -d/ -f1)
[ -n "${src:-}" ] || return 1
echo "$dev $src"
}

tries=0
while [ "$tries" -lt 15 ]; do
if info=$(detect); then DEV=${info%% *}; SRC=${info#* }; break; fi
tries=$((tries + 1)); sleep 2
done
[ -n "${DEV:-}" ] && [ -n "${SRC:-}" ] || { echo "未探测到默认路由/源地址" >&2; exit 1; }

echo "udp2raw-client: dev=$DEV src=$SRC server=$SERVER:$REMOTE_PORT"
exec "$BIN" -c -l "0.0.0.0:${LOCAL_PORT}" -r "${SERVER}:${REMOTE_PORT}" -k "$KEY" \
--lower-level auto --source-ip "$SRC" --dev "$DEV"
1
sudo chmod 755 /usr/local/bin/udp2raw-client
  1. 开机启动服务
1
sudo vim /lib/systemd/system/udp2raw.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=udp2raw client
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/udp2raw-client
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target
  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start udp2raw.service
sudo systemctl enable udp2raw.service

服务端配置

  1. 将 udp2raw_amd64 拷贝至 /usr/local/bin/ 下
1
sudo cp udp2raw_amd64 /usr/local/bin/
  1. 创建开机启动服务
1
sudo vim /lib/systemd/system/udp2raw.service
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=udp2raw server
After=network-online.target kcptun.service
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/udp2raw_amd64 -s -l 0.0.0.0:4096 -r 127.0.0.1:8400 -a -k "你的KEY"
Restart=on-abnormal
RestartSec=5

[Install]
WantedBy=multi-user.target

服务端 -a 会自动写 iptables 规则拦掉内核发的 RST,必须先装 iptables,否则启动直接失败:sh: 1: iptables: not found。
apt install iptables 即可。客户端不需要 -a(用 --lower-level 直达二层就没这个问题)。

  1. 启动并设置开机启动
1
2
3
sudo systemctl daemon-reload
sudo systemctl start udp2raw.service
sudo systemctl enable udp2raw.service

密钥必须两侧一致

这是最容易出错的地方,三组独立的值、各自成对:

位置 客户端 服务端
shadowsocks 密码 sslocal -k ssserver -k
kcptun key / nocomp / mtu --key --nocomp --mtu "key" "nocomp" "mtu"
udp2raw key -k -k

任一处不一致的典型表现:**握手看起来成功,但一拿数据就 curl: (52) Empty reply from server**。

验证链路

1
2
3
4
5
6
7
8
9
10
11
# 1. 端口都在听
sudo ss -lntup | grep -E '9872|30001|4096|8400|39201'

# 2. udp2raw 是否握上了
sudo journalctl -u udp2raw -n 30 --no-pager
# 出现 state changed from client_handshake2 to client_ready 才算通

# 3. 走代理测通
curl -sS --socks5-hostname 127.0.0.1:9872 -o /dev/null -w '%{http_code}\n' https://www.google.com
curl -sS --socks5-hostname 127.0.0.1:9872 https://ipinfo.io/ip
# 第二个命令返回的应该是你的 VPS IP

常见故障对照表

现象 原因 处理
curl: (52) Empty reply from server key/密码两侧不一致 三组密钥逐个核对
改完 unit 但行为没变 只改了文件没重载 sudo systemctl daemon-reload && sudo systemctl restart 服务名
日志刷 huge packet,data len=1378 (>=1375) kcptun mtu 太大 两端都设 mtu 1200
换了 WiFi / 断了重连后就不通 udp2raw 卡在死会话里,或源 IP 变了 sudo systemctl restart udp2raw
日志一直 client_tcp_handshake,反复 (re)sent tcp syn 被 TUN 接管路由,源地址选错 加 --lower-level auto --source-ip <局域网IP> --dev <网卡>
sh: 1: iptables: not found 用了 -a 但没装 iptables apt install iptables,或去掉 -a
服务 status=203/EXEC ExecStart 里的文件名写错 二进制实际名字是 server_linux_amd64 / client_linux_amd64
隧道通但网速极慢 热点/2.4G 空口本身就是瓶颈,或 MTU 溢出丢包 先按上面修 MTU,再换 5G 测

可选:加一个看门狗,隧道断了自动拉起 udp2raw。用 timer 每两分钟探一次 SOCKS5,不通就 restart。

谷歌设置shadowsock

  1. 首先下载谷歌插件 SwitchyOmega
  2. auto switch 配置规则列表网址 https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt
  3. proxy 配置代理协议 Socks5,代理服务器 127.0.0.1,代理端口为刚才 shadowsock 端口 9872
  4. 不代理列表配置
1
2
3
127.0.0.1
::1
localhost

Chrome 新版本只支持 Manifest V3 扩展,SwitchyOmega 2 已从 Chrome 商店下架。Firefox 上仍可正常安装,Chrome 上可以换 SmartProxy 之类的替代品,或者直接用系统代理 / 终端方式。

终端配置科学上网

1
2
sudo apt install proxychains4
sudo vim /etc/proxychains4.conf

在 [ProxyList] 下增加(**协议名是 socks5,不是 sock4**):

1
socks5 127.0.0.1 9872

几个要点:

  • 段名是 [ProxyList],不是 [Proxylist](大小写敏感,写错整段不生效)。
  • 默认那行 socks4 127.0.0.1 9050 是给 tor 用的,删掉或注释,保留你自己的 socks5 127.0.0.1 9872。
  • 确认 proxy_dns 处于开启状态(新版默认就是开着的,行首没有 #),让域名也走代理解析,否则 DNS 泄漏且部分站点解析不到。
  • 用的时候:proxychains4 curl https://www.google.com。
  • 不想装 proxychains4 也可以单条命令指定:curl --socks5-hostname 127.0.0.1:9872 https://www.google.com。

换网络后必须做的一件事

因为 udp2raw 的源 IP 是启动时探测的,换了 WiFi、换了网段、或者从休眠唤醒之后:

1
sudo systemctl restart udp2raw

其它服务不用动(kcptun 和 sslocal 只是本地 TCP,不依赖网卡地址)。

懒人版:一键部署 deb

上面这些手工步骤可以打包成一个 deb,在任意 amd64 Debian/Ubuntu 上一条命令装完:

1
sudo dpkg -i vpn-tunnel_1.0.0_amd64.deb

包内包含三个静态二进制、自动探测网卡源 IP 的 wrapper、看门狗 timer,以及四个 systemd 单元。所有参数集中在 /etc/vpn-tunnel/tunnel.conf(权限 600,作为 conffile 在卸载时保留):

1
2
3
4
5
6
7
8
9
10
11
12
13
SERVER=服务器IP
UDP2RAW_REMOTE_PORT=4096
UDP2RAW_KEY=你的KEY
UDP2RAW_LOCAL_PORT=30001
KCP_LISTEN=:30001
KCP_REMOTE=127.0.0.1:30001
KCP_KEY=你的KEY
KCP_CRYPT=salsa20
KCP_MTU=1200
SS_LOCAL=127.0.0.1:9872
SS_SERVER=127.0.0.1:30001
SS_PASSWORD=你的密码
SS_METHOD=aes-256-gcm

改完配置 sudo systemctl restart udp2raw kcptun ss-server 即可。卸载用 sudo apt remove vpn-tunnel(保留配置)或 sudo dpkg --purge vpn-tunnel(连配置一起删)。