VPS 与 anytls:自己搭一条稳定的线
决定体验的是回程线路和协议选型,不是机器配置。1 核 1G 就够,线路差三倍。
学完这节你能做到
- 看懂 CN2 GIA / GT / AS4837 这些标签,并用 mtr 自己验证回程
- 说清 TLS in TLS 的指纹问题,以及 anytls 用哪三招缓解它
- 用 sing-box 与 mihomo 配出一条带真实证书的 anytls 链路
建议先学
跳过这几节会看不懂本节的部分推导
先决定要不要自建
上一节的判断树如果指到"需要一条自己的出口",第一个问题不是用什么协议,是要不要自己搭。
| 维度 | 自建(VPS + 自己部署) | 订阅服务 |
|---|---|---|
| 成本 | 一台小 VPS,按月固定 | 按月,通常更便宜 |
| 可控性 | 完全可控,出口 IP 固定 | 出口共享,IP 可能被目标站点风控 |
| 稳定性 | 取决于你选的线路和维护 | 取决于服务方 |
| 隐私 | 流量只经过自己的机器 | 流量经过第三方 |
| 维护成本 | 要自己管:证书续期、升级、监控 | 无 |
| 适合 | 长期、要固定出口、要接 CI | 临时、个人、不想维护 |
给工程师的实际建议:要在 CI 或服务器上稳定使用,自建 —— 因为你需要固定出口 IP 和可预期的可用性。个人临时用途没必要。
这一节按自建的路走一遍:选机器 → 选线路 → 配服务端 → 选协议。 协议这一步会落到 anytls,理由在后半段。
VPS:决定体验的是线路,不是配置
1 核 1G 就足够跑一个代理服务,CPU 和内存基本不是瓶颈。真正要看的是四件事:
- 地域:香港延迟最低(大陆访问通常几十毫秒),美西便宜、机房和线路选择多,但延迟通常 在一百多到两百毫秒。做交互式操作(SSH、Web 控制台)地域的影响比带宽大得多
- 流量与带宽:看清是"月流量配额"还是"不限量但限速"。前者用超会直接停机, 拉几个大镜像就可能触顶
- 回程线路:同一机房不同线路的体验能差好几倍,这是最容易被价格掩盖的一项
- 快照与重建:选支持快照的,配置搞坏了能回滚;同时看清换 IP 的政策(IP 被封时能不能换)
线路名词对照
报价页上那几个缩写是真正决定体验的东西:
| 标签 | 是什么 | 体验 |
|---|---|---|
| CN2 GIA | 电信 CN2 的高优先级产品(AS4809) | 最好:低丢包、晚高峰不塌,也最贵 |
| CN2 GT | 电信 CN2 的普通档 | 中等:比普通骨干好,晚高峰仍会拥塞 |
| 163 / AS4134 | 电信普通骨干 | 最便宜,晚高峰丢包和抖动明显 |
| AS4837 / CU4837 | 联通骨干 | 性价比常见选择,质量介于两者之间 |
| Eyeball | 面向末端消费者网络优化的线路(eyeball network 是行业术语,指承载大量家庭宽带用户的 ISP) | 到家宽用户的路径更短 |
判断线路真假不靠商家宣传,看 traceroute 的骨干跳:途经 59.43.x.x 说明走了 CN2,
只见 202.97.x.x 则是普通骨干(这个判据来自
haoel 的科学上网笔记)。
具体推荐
下面这几家按价格从高到低、稳定性也从高到低排列 —— 这两件事在这个领域基本是正相关的:
| 服务商 | 机房与线路 | 定位 |
|---|---|---|
| 搬瓦工 bwh8.net | US · E-Commerce VPS | 最贵也最稳。要长期挂服务、不想操心的选它 |
| DMIT dmit.io | US · Premium / Eyeball | 次之。线路分档细,按用途挑 |
| CubeCloud cubecloud.net | HK · CN2 GIA / US · CN2 GIA | 性价比档。要低延迟就选香港那条 |
| CloudCone cloudcone.com | US | 最便宜,稳定性最弱。适合临时验证、随时可弃 |
按用途对应:
- CI 或服务器上长期用(拉依赖、跑构建)→ 取前两档。这类场景断一次就是流水线红一片, 稳定性的钱值得花
- 个人日常看文档、查 issue → 第三档足够,香港线路的交互体验明显更好
- 临时验证一个想法、或者只是想练一遍部署 → 最后一档,用完就删
关键是回程(VPS → 大陆),不是去程。 从自己电脑 traceroute 出去测的是去程,
而中国大陆的国际链路去回程经常走完全不同的路径 —— 去程漂亮、回程拥塞是很常见的组合。
所以要在 VPS 上反向测:
# 在 VPS 上装 mtr,往大陆三网各测一个地址
mtr -r -c 20 202.96.209.133 # 电信(上海)
mtr -r -c 20 210.22.84.3 # 联通(上海)
mtr -r -c 20 211.136.112.50 # 移动(上海)
# 看骨干跳里有没有 59.43(CN2)
mtr -r -c 20 202.96.209.133 | grep -E '59\.43|202\.97'看两件事:丢包出现在哪一跳,以及晚高峰(20:00–23:00)的延迟涨多少。 白天测都好看,晚高峰才见真章 —— 至少跨一个晚高峰再决定要不要长租。
多数商家有几天的退款窗口,先按月付测一轮再转年付,比一上来省 20% 划算。
这个市场变动很快:机房线路会被降级、库存补货靠抢、同一款产品不同批次的实际线路可能不同。
上面的排序是长期口碑而不是当下承诺,下单前一定用上面那套 mtr 自己验一遍。
另外注意两件事:不要用同一台机器承载重要业务和这类用途(IP 被封会一起受影响), 以及账号别复用密码 —— 这类商家的安全水位参差不齐。
服务端的两项基础配置
# 1. BBR:长肥链路上收益最直接(tcp-behavior 那节讲过原因)
cat >> /etc/sysctl.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
sysctl -p
sysctl net.ipv4.tcp_congestion_control # 确认生效
# 2. 域名 + 真实证书(自签会立刻暴露)
certbot certonly --standalone -d proxy.example.com
# 续期交给 cron / systemd timer,过期当天服务就断
真实证书不是"更安全一点"的选项,是必需项 —— 下一段会说清为什么。
为什么是 anytls:TLS in TLS 这个坎
现代代理协议用的都是成熟密码学,内容本身没人解得开。协议还在不停演进, 是因为要解决另一个问题:
加密流量看起来"像什么"。
即使内容不可解密,路上的设备仍然能看到这些:
| 特征 | 具体内容 |
|---|---|
| 握手指纹 | TLS ClientHello 里的密码套件顺序、扩展列表、ALPN、GREASE 值 |
| 证书与 SNI | 目标域名(SNI 明文)、证书链、有效期 |
| 包长分布 | 每个包多大、大小的分布形状 |
| 时序 | 请求间隔、突发模式、连接持续时间 |
| 连接模式 | 一个 IP 上有多少并发连接、连接建立频率 |
| 主动探测响应 | 别人直接连你这个端口时,你返回什么 |
其中最核心的一个叫 TLS in TLS。代理把 HTTPS 流量装进自己的 TLS 连接时:
外层 TLS 握手(你的客户端 ←→ 你的服务器)
└─ 建立完成后,里面开始跑:
内层 TLS 握手(浏览器 ←→ 目标网站)
↑ 这个握手的包长与时序模式,在外层加密后依然有迹可循
问题在于握手是有固定形状的:ClientHello 一般偏小、ServerHello 加证书偏大、 后续几个包大小相对固定、时序也有规律。这些特征即使被外层加密包住, 包长序列和时间间隔仍然暴露在外。
于是可以判断:这条"普通 HTTPS 连接"建立后不久,里面又发生了一次 TLS 握手 —— 而正常的 HTTPS 浏览不会这样。
anytls 正是为缓解这个指纹而设计的协议,三个手段:
| 手段 | 做什么 | 减少了哪个特征 |
|---|---|---|
| 灵活的分包策略 | 不按原始边界切,重新划分数据块 | 包长分布 |
| 填充策略 | 往包里塞无意义字节,改变长度 | 包长分布 |
| 连接复用 | 多个请求走同一条已建立的连接 | 握手频次 —— 少产生嵌套握手 |
第三点值得单独说:减少嵌套握手的最好办法是不要反复建立它。 连接复用既降低了延迟(省掉握手往返),也顺带减少了可观测的握手次数 —— 一举两得,这也是它被列为核心特性的原因。
anytls-go 的项目定位写得很明确:它是协议的参考实现,
目的是展示协议细节、交互流程与边界场景。
# 参考实现的用法,适合本地跑一遍看协议怎么工作
./anytls-server -l 0.0.0.0:8443 -p <密码>
./anytls-client -l 127.0.0.1:1080 -s "anytls://<密码>@<服务器>:<端口>"而且它的示例服务端与客户端默认采用不安全的配置 —— 假定你不会遇到 TLS 中间人攻击。这个假定不成立时,流量可能被拦截。
日常使用要用实现了该协议的成熟软件(sing-box、mihomo), 并且自己确认 TLS 校验相关的配置是打开的。各家第三方实现与规范的贴合程度由各自团队负责, 不同版本行为可能有差异 —— 这也是"换客户端后行为变了"的常见原因。
服务端:sing-box 的 anytls 入站
{
"inbounds": [
{
"type": "anytls",
"tag": "anytls-in",
"listen": "::",
"listen_port": 443,
"users": [
{ "name": "me", "password": "<用 openssl rand -base64 16 生成>" }
],
"tls": {
"enabled": true,
"server_name": "proxy.example.com",
"certificate_path": "/etc/letsencrypt/live/proxy.example.com/fullchain.pem",
"key_path": "/etc/letsencrypt/live/proxy.example.com/privkey.pem"
}
}
]
}
padding_scheme 不写就用协议的默认值,它长这样:
["stop=8", "0=30-30", "1=100-400",
"2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000",
"3=9-9,500-1000", "4=500-1000", "5=500-1000", "6=500-1000", "7=500-1000"]
读法:第 n 个数据包被填充到某个长度区间,stop=8 表示第 8 个包之后不再填充 ——
握手阶段是指纹最集中的地方,填充只需要盖住开头那几个包。c 是分片标记。
默认值是被调过的,没有具体理由不要自己改:改坏了反而制造出一个独一无二的指纹。
客户端:mihomo 的 anytls 出站
proxies:
- name: my-vps
type: anytls
server: proxy.example.com
port: 443
password: "<和服务端一致>"
client-fingerprint: chrome # 伪装 TLS 指纹,别留默认值
sni: proxy.example.com
alpn: [h2, http/1.1]
skip-cert-verify: false # ← 生产必须 false
udp: true
# 连接复用的三个旋钮,默认值适用于绝大多数场景
idle-session-check-interval: 30 # 多久检查一次空闲会话
idle-session-timeout: 30 # 空闲超过多久就关掉
min-idle-session: 0 # 检查时至少保留几条空闲会话
三个 session 参数就是上面"连接复用"那一招的实际控制面。把 min-idle-session
调到 1~2 能让首次请求省掉建连往返,代价是空闲时也维持着连接 —— 交互式使用值得,
长期挂机没必要。
很多教程为了"先跑通"让人打开它,然后就再没关过。打开之后:证书对不对不检查、 域名匹不匹配不检查 —— 协议花力气伪装成正常 HTTPS,你却把 HTTPS 的核心保证关掉了。
如果打开它才能连通,说明证书链有问题(多半是只配了 cert.pem 而不是 fullchain.pem),
去修证书,不要关校验。
顺带一提:anytls 不能和 Reality 搭配,mihomo 文档明确说了不做支持。 要隐藏 SNI 只能走 ECH / ShadowTLS 这条路;非 Reality 不可的话,换 VLESS 系协议。
抗主动探测:三件事缺一不可
被动观察之外还有主动探测:直接连你那个端口,看它怎么回应。
正常的 HTTPS 服务:返回网页,证书匹配域名
配置粗糙的代理: 返回错误、直接断开、或返回可识别的特征响应
← 这本身就是特征
三件配套的事:
- 真实域名 + 真实证书(Let's Encrypt 即可)—— 自签证书本身就是强特征
- 端口用 443 —— 非标准端口上跑 TLS 是另一个特征
- 端口上要有个说得过去的东西 —— 用 sing-box 的
fallback类能力或前置一个 nginx, 把认证失败的连接转给一个真实网页,比直接断开自然得多
CDN 前置:要用就得换协议
当出口 IP 本身成为问题时(被封或被限速),可以把 CDN 挡在前面:
客户端 → CDN 边缘节点(大量正常流量共用的 IP)→ 回源到你的服务器
但这里有个必须讲清楚的限制:anytls 走不了 CDN。CDN 只代理 HTTP 语义的流量,
而 anytls 是裸 TLS 之上的私有协议,边缘节点不认。要上 CDN 就得换成 WebSocket 类传输
(VLESS + ws + tls 之类),端口也限于 80/8080/443/8443 这些标准 HTTP(S) 端口。
| 方案 | 抗 TLS in TLS | 能过 CDN |
|---|---|---|
| anytls | 强(分包 + 填充 + 复用) | 否 |
| VLESS + ws + tls | 弱(嵌套握手照样暴露) | 是 |
代价也很实在:多一跳、延迟增加、CDN 的连接数与流量可能产生费用, 而且回源 IP 仍可能被识别 —— CDN 只是遮住了直连关系。
经 CDN 时每建一条新连接都要完整走一遍 TLS 握手 + CDN 的回源建连,开销明显。
所以走 CDN 时一定要开传输层的多路复用(mux),既降延迟又减少握手次数 ——
和 anytls 的连接复用是同一个思路,只是换了个地方实现。
另一类问题:原生 IP
有些服务不是"能不能连上"的问题,而是连上了但被目标站点风控: 数据中心 IP 会被识别并限制(流媒体、部分 AI 服务、部分电商)。
这与前面讲的流量特征无关,属于 IP 归属问题。常见处理是用 WARP 这类服务拿一个 "住宅属性"更强的出口:
# 代理模式:本地起一个 SOCKS5,只把需要的流量送过去
warp-cli registration new
warp-cli mode proxy
warp-cli connect
# 默认监听 127.0.0.1:40000
然后在客户端里只把特定域名分流到它,其余照常走自己的出口 —— 具体怎么写规则是下一节的事。
- 内存泄漏:长期运行的实例存在内存缓慢增长的情况,实践中通常配一个定期重启
- 文件描述符:并发高时要调大
LimitNOFILE,否则会出现连接失败
它更适合作为特定域名的旁路出口,而不是承载全部流量的主链路。
代价对照表
每种手段都不是免费的:
| 手段 | 收益 | 代价 |
|---|---|---|
| 灵活分包 + 填充 | 改变包长分布 | 有效载荷率下降(填充占带宽) |
| 连接复用 | 减少握手、降延迟 | 单连接故障影响面变大(队头阻塞) |
| 真实证书 + 伪装回落 | 抗主动探测 | 要维护域名与证书续期 |
| CDN 前置 | 遮住直连关系 | 多一跳延迟 + 费用 + 只能用 WS 类传输 |
| WARP 旁路 | 解决 IP 归属 | 多一跳 + 需要运维(重启、fd 上限) |
一个务实的原则:从最简单的开始,遇到具体问题再加一层。 一上来就把所有手段叠满,结果是延迟高、配置复杂、出问题难定位 —— 而多数场景根本用不到那么多。一台 CN2 GIA 的小机器 + anytls + 真实证书, 已经覆盖了绝大部分开发者的日常需要。
检查点
TLS in TLS 的指纹问题,本质是什么被观察到了?
选 VPS 时,最该在下单前自己验证的是哪一项?
已经在用 anytls,现在出口 IP 被目标站点封了,想套一层 CDN。可行吗?
这节课的落点
- 要在 CI 或服务器上长期用才值得自建;个人临时用途,订阅服务更省事
- VPS 上真正稀缺的是回程线路,不是 CPU 内存。看骨干跳有没有
59.43(CN2), 并且在 VPS 上反向 mtr、跨一个晚高峰再长租 - 服务端两项基础:开 BBR、真实域名与证书(
fullchain.pem,配好自动续期) - 协议演进不是因为加密不够强,是因为加密流量"看起来像什么"; 最核心的一个是 TLS in TLS —— 内层握手的包长与时序在外层加密后仍可识别
- anytls 的三招:灵活分包、填充、连接复用,第三招一举两得(降延迟 + 少握手)
anytls-go是参考实现且示例配置默认不校验中间人,生产用 sing-box / mihomopadding_scheme默认值是调过的,没有理由不要改 —— 改坏了等于造了个独一无二的指纹skip-cert-verify必须是false;要打开才通说明证书链配错了,去修证书- anytls 不能过 CDN(裸 TLS)也不能配 Reality;要 CDN 就换 WS 类传输,代价是抗指纹变弱
- "原生 IP"是另一类问题(IP 归属而非流量特征),用 WARP 做特定域名的旁路出口
- 从最简单的开始,遇到问题再加一层,叠满手段只会让延迟与排障成本一起上升