一行配置都没手敲:用 Claude Code 从零搭一套 sing-box 代理(VLESS Reality + Hysteria2)

在 AWS EC2 上开一台 Ubuntu,剩下的交给 Claude Code:SSH 配置、安装 sing-box、挑 Reality 伪装域名、开 BBR、写 Clash Verge 配置、配 fail2ban、再加一个 Hysteria2 节点。本文是视频的图文版,按步骤给出每一步对 Agent 说的话、它实际做了什么,以及途中踩到的两个坑:新版 Ubuntu 上 fail2ban 抓不到 SSH 失败日志、AWS 安全组没放行 UDP。

以前自建代理,最折腾的不是买服务器,而是把它配通:问 ChatGPT 要一份 sing-box 配置,再对着官方文档把 inbound、outbound、route 一个个改,改完自己测。一个参数对不上,一晚上就没了。

这次我只做了两件事:在 AWS 控制台开一台机器、放行安全组。剩下的全部交给 Claude Code,用几句中文对话完成。

开始前先说明网络条件:Claude Code 默认连 Anthropic 官方的模型,需要能访问国际网络。如果你现在还没有可用的国际网络(这正是要搭代理的原因),可以先把 Claude Code 接到国产模型上,比如 DeepSeek、Kimi、GLM、Qwen 这类提供 Anthropic 兼容接口的服务,设好 ANTHROPIC_BASE_URL 和对应的 API Key 就能用。本文这类 SSH 连服务器、改配置、排查报错的任务,国产模型也能完成。也可以直接换一个原生支持国产模型的 Agent 工具,比如 OpenCode 接 DeepSeek,或者 DeepSeek 官方的 DeepSeek Harness(dsh),把下文每一步“我说的话”原样发给它即可。代理搭好之后,再切回官方模型也可以。

这篇是视频的图文版,方便你照着做。每一步都写了三样东西:

  • 我说的话:直接对 Agent 说的原话,你可以照抄
  • 它做了什么:Agent 实际执行的操作
  • 你可以核对的点:如果不用 Agent、想手动来,或者想检查它做得对不对,看这里

文中的 IP、UUID、Reality 密钥、short ID、Hysteria2 密码和证书指纹都是为本文单独生成的示范值,与录屏里的服务器无关。照着做时请全部换成你自己生成的,不要直接用文中的密钥和密码。

最终效果

  • 服务端:一台 AWS EC2(Ubuntu,t3.micro),跑 sing-box,同时提供两个节点
    • VLESS + Reality(TCP 443),不需要域名
    • Hysteria2(UDP 443),自签证书
  • 服务器加固:BBR 拥塞控制、fail2ban 防 SSH 爆破
  • 客户端:Clash Verge,国内直连、广告拦截、其余走代理,两个节点可切换

整个过程中,我手动操作的只有 AWS 控制台里的开机器和改安全组。

想更进一步:这两步也可以交给 Agent。本地装好 AWS CLI,用 aws configure 配好访问密钥,就可以直接对它说“帮我开一台 Ubuntu 的 t3.micro,安全组放行 TCP 22、TCP 443 和 UDP 443”,开实例、建密钥对、改安全组都由它用命令完成,全程不用打开控制台。但是配置IAM和密钥对于小白用户来说可能有点复杂,本期视频没有采用。

原理:客户端和服务端

一套代理有两个角色:

  • 客户端跑在你的电脑或手机上,把应用发出的流量拦下来、加密后发出去
  • 服务端是海外的一台服务器,解密请求,替你访问目标网站,再把结果加密传回来

客户端软件基本都开源免费。服务端有两种来源:买现成的商业节点,或者自己租 VPS 搭。

商业节点省心,但通常是一群人共用一个 IP,容易被各种服务风控,隐私也不在自己手里。自建是一个人独享 IP,协议、端口、分流规则全由自己定。自建过去最大的门槛是配置复杂,有了 Agent,这个门槛基本没了。

准备工作

需要两样东西:

  1. 一台海外云服务器。本文用 AWS EC2,其他 VPS 也一样
  2. 一个能执行命令的编码 Agent。本文用 Claude Code,本地终端里运行。其他codex,opencode,deepseek dsh 等Agent 智能体也可以。

技术栈:

组件 选择 说明
代理核心 sing-box 服务端
协议 1 VLESS + Reality 走 TCP,不需要域名,伪装成访问一个真实大站
协议 2 Hysteria2 走 UDP(QUIC),某些网络下更快,和 VLESS 互为备份
客户端 Clash Verge 内核是 mihomo,macOS / Windows / Linux 都有

在 AWS 开一台 EC2

登录 AWS 控制台 → EC2 → 启动实例:

  1. 系统:Ubuntu
  2. 实例类型:t3.micro(免费套餐档位,跑代理够用)
  3. 密钥对:新建一个,类型选 ED25519,把 .pem 私钥下载到本地(这就是等会儿交给 Agent 的钥匙)
  4. 网络设置 / 安全组:勾选 允许 SSH 和 允许 HTTPS

第 4 步记住,HTTPS 放行的只是 TCP 443。后面加 Hysteria2 的时候,这里会埋一个雷。

启动后记下实例的公网 IP。

第一步:把钥匙交给 Agent

我说的话:

帮我配置连接我的 aws 服务器,key 在 download 下面,服务器 IP 是 203.0.113.10

它做了什么:

  • 在 ~/Downloads 里找到 .pem 文件,挪到 ~/.ssh/,改成 600 权限
  • 在 ~/.ssh/config 里给服务器起了个别名 yt-demo
  • 自己连一次,确认能登录

你可以核对的点: ~/.ssh/config 里多了这样一段:

Host yt-demo
    HostName 203.0.113.10
    User ubuntu
    IdentityFile ~/.ssh/yt-demo.pem

另开一个终端,ssh yt-demo 能直接登录进去,就说明这一步没问题。从这以后,后面的活基本只需要动嘴。

第二步:安装 sing-box

我说的话:

帮我在服务器安装 sing-box

它做了什么:

  • 先用 uname -m、/etc/os-release 探测系统和 CPU 架构
  • 没有去 GitHub 手动下载安装包,而是接上了 sing-box 官方的 APT 软件源,以后 apt upgrade 就能跟着系统一起升级
  • 装完列出了下一步:写配置文件、设开机自启、在安全组放行端口

你可以核对的点: 官方 APT 源的安装方式(以 sing-box 官方文档 为准):

sudo mkdir -p /etc/apt/keyrings
sudo curl -fsSL https://sing-box.app/gpg.key -o /etc/apt/keyrings/sagernet.asc
sudo chmod a+r /etc/apt/keyrings/sagernet.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/sagernet.asc] https://deb.sagernet.org/ * *" \
  | sudo tee /etc/apt/sources.list.d/sagernet.list
sudo apt-get update
sudo apt-get install sing-box

sing-box version

第三步:服务端配置 VLESS Reality + 开启 BBR

配置 VLESS Reality

我说的话:

配置 VLESS-Reality,端口 443

VLESS Reality 需要准备四样东西:一个 UUID、一对 Reality 密钥、一个 short ID,以及一个伪装目标站点。以前挑伪装站点最麻烦,得自己一个个拿 curl 去试。

它做了什么:

  1. 挑伪装站点:在服务器上把微软、苹果、火狐插件站(addons.mozilla.org)几个候选挨个测了一遍,看谁支持 TLS 1.3 和 HTTP/2、延迟又低,最后选中了 addons.mozilla.org
  2. 生成密钥:UUID、Reality 密钥对、short ID 全部用 sing-box 自带命令生成
  3. 写配置:写入 /etc/sing-box/config.json,先用 sing-box check 校验,再重启服务
  4. 外部探测:从本地探测 443 端口是否能连通
  5. 交付:给出一整张参数表,外加一条可以直接导入客户端的 vless:// 分享链接

你可以核对的点:

测伪装站点是否合格(要求:能协商 TLS 1.3,支持 HTTP/2,延迟低):

curl -so /dev/null --tlsv1.3 --http2 \
  -w '%{http_version}  connect=%{time_connect}s\n' \
  https://addons.mozilla.org

生成需要的参数:

sing-box generate uuid              # UUID
sing-box generate reality-keypair   # PrivateKey 放服务端,PublicKey 给客户端
sing-box generate rand 8 --hex      # short ID

服务端配置大致是这个结构(后面加 Hysteria2 时会再加一个 inbound):

{
  "log": { "level": "warn" },
  "inbounds": [
    {
      "type": "vless",
      "tag": "vless-in",
      "listen": "::",
      "listen_port": 443,
      "users": [
        { "uuid": "9bf3fe50-b240-4431-b355-52000d20c45e", "flow": "xtls-rprx-vision" }
      ],
      "tls": {
        "enabled": true,
        "server_name": "addons.mozilla.org",
        "reality": {
          "enabled": true,
          "handshake": { "server": "addons.mozilla.org", "server_port": 443 },
          "private_key": "SKrVhmmC4Dl-IV0tVUQqND8vW8o7j0bmS59s8zuYBVI",
          "short_id": ["d0902526d9e9ea13"]
        }
      }
    }
  ],
  "outbounds": [
    { "type": "direct", "tag": "direct" }
  ]
}
sudo sing-box check -c /etc/sing-box/config.json
sudo systemctl enable --now sing-box
sudo systemctl restart sing-box

开启 BBR

我说的话:

检查一下 bbr 是否开启,sing-box 设置一下开机自启

它做了什么:

  • 发现服务器默认用的是老的 cubic 拥塞控制算法,换成了 BBR,改善跨国线路丢包时的吞吐
  • 确认 sing-box 已经设为开机自启
  • 改完没有直接交差,而是主动重启了一次服务器,等机器重新上线后,再确认 BBR 仍然生效、sing-box 也自己起来了

你可以核对的点:

# 开启 BBR(写进 sysctl.d,重启后仍然有效)
echo -e "net.core.default_qdisc=fq\nnet.ipv4.tcp_congestion_control=bbr" \
  | sudo tee /etc/sysctl.d/99-bbr.conf
sudo sysctl --system

# 验证
sysctl net.ipv4.tcp_congestion_control   # 应输出 bbr
systemctl is-enabled sing-box            # 应输出 enabled

第四步:选一个本地客户端

服务端就绪后,本地装一个图形化客户端。可以按自己习惯选:

  • Clash Verge Rev(本文使用,在 GitHub Releases 下载)
  • v2rayN
  • sing-box 官方客户端(SFM / SFA / SFI)

第五步:让 Agent 写客户端配置,并在本地联调

我说的话:

我本地使用 clash verge,给我一个本地的配置

以前最常见的翻车点,是手抄公钥、UUID 的时候漏了一个字符。这次不会,因为服务端刚刚就是它配的,所有参数都还在它的上下文里。

它做了什么:

  • 写好一份 Clash(mihomo)格式的 yt-demo-clash.yaml
  • 分流规则一并写好:局域网和国内直连,广告拦截,其余走代理,国内外 DNS 分开解析
  • 写完先自己验证了一遍:用 Clash Verge 自带的 mihomo 内核直接把这份配置跑起来,测出口 IP 正是我们的服务器,访问 YouTube 约 1.3 秒返回 200

导入 Clash Verge:

订阅 → 新建 → 类型选 Local → 选中这个 yaml 文件 → 保存。代理组里就会多出 yt-demo 节点,延迟两百多毫秒。开启系统代理,打开浏览器搜一下 Google,秒开。

你可以核对的点: 配置的关键部分(最终版,已包含第七步加的 Hysteria2 节点):

mixed-port: 7897
mode: rule
ipv6: false

geodata-mode: true
geo-auto-update: true

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  nameserver:
    - https://dns.alidns.com/dns-query
    - https://doh.pub/dns-query
  nameserver-policy:
    "geosite:geolocation-!cn":
      - https://1.1.1.1/dns-query
      - https://8.8.8.8/dns-query

proxies:
  - name: yt-demo
    type: vless
    server: 203.0.113.10
    port: 443
    uuid: 9bf3fe50-b240-4431-b355-52000d20c45e
    network: tcp
    udp: true
    tls: true
    flow: xtls-rprx-vision
    servername: addons.mozilla.org
    client-fingerprint: chrome
    reality-opts:
      public-key: I1K-2DVvKABMnBwW-dnr6Y06f1m9spNipAoLhpqy3Ck
      short-id: d0902526d9e9ea13

  - name: yt-demo-hy2
    type: hysteria2
    server: 203.0.113.10
    port: 443
    password: ciVyTAZwk6qsexHDke4CcM0K
    sni: addons.mozilla.org
    alpn:
      - h3
    # 自签证书,用指纹锁定,不是无条件跳过校验
    fingerprint: 0e64804c1e0bdfdc1630560a32f9fd255119e82772e97d55b92663af61672944

proxy-groups:
  - name: Proxy
    type: select
    proxies:
      - yt-demo
      - yt-demo-hy2
      - DIRECT

rules:
  - GEOSITE,private,DIRECT
  - GEOIP,private,DIRECT,no-resolve
  - GEOSITE,category-ads-all,REJECT
  - GEOSITE,cn,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,Proxy

几个值得注意的地方:

  • 客户端用的是 Reality 的 公钥,私钥只留在服务器上
  • nameserver-policy 让国外域名走 Cloudflare / Google 的 DoH,国内域名走阿里 / 腾讯的 DoH,避免 DNS 污染也避免国内站点解析到海外 CDN
  • fake-ip 模式下 DNS 响应更快,局域网、NTP 这类域名可以按需加进 fake-ip-filter

第六步:配 fail2ban,并修掉一个默认配置的坑

节点一上公网,扫描器很快就会来尝试 SSH 爆破。

我说的话:

服务器做一下防护,配置一下 fail2ban

坑在这里: 新版 Ubuntu 附带的 OpenSSH 把登录认证拆到了单独的 sshd-session 进程里,SSH 登录失败的日志也改由它来写。但 fail2ban 自带的 sshd 过滤器在读 systemd journal 时,只匹配 _COMM=sshd,一条失败记录都抓不到。fail2ban 照常运行、status 也显示正常,实际上装了等于没装。

这个坑只在新版 Ubuntu 上出现,录视频时我用的是 AWS 默认的最新版26.04,所以踩到了。开机器时选低一点的版本,比如 Ubuntu 22.04 或 24.04,一般就能避开。

它做了什么:

  • 装完 fail2ban 没急着交差,而是去查"到底是哪个进程在写 SSH 认证失败日志",发现了上面这个问题
  • 改掉默认的匹配规则,再拿服务器上已有的失败记录试跑一遍,全部识别出来
  • 定好封禁规则:10 分钟内输错 3 次封 1 小时,屡教不改的直接封一周
  • 把我自己的公网 IP 加进白名单,免得把自己关在门外

你可以核对的点:

先确认你的系统是不是也有这个问题:

# 能看到失败记录,说明日志是 sshd-session 写的
sudo journalctl _COMM=sshd-session | grep -i "invalid user\|failed" | tail -5
# 这条如果几乎没有输出,就说明默认规则会漏掉它们
sudo journalctl _COMM=sshd | grep -i "invalid user\|failed" | tail -5

修正过滤器的 journal 匹配(.local 文件会覆盖同名 .conf,升级时不会被冲掉):

# /etc/fail2ban/filter.d/sshd.local
[Init]
journalmatch = _COMM=sshd + _COMM=sshd-session

封禁策略:

# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 198.51.100.20

[sshd]
enabled  = true
backend  = systemd
findtime = 10m
maxretry = 3
bantime  = 1h

# 一天内被封过多次的 IP,直接封一周
[recidive]
enabled  = true
findtime = 1d
maxretry = 3
bantime  = 1w
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd   # 看 Total failed 是否在增长

第七步:再加一个 Hysteria2 节点,以及 AWS 安全组的坑

VLESS 走 TCP,Hysteria2 走 UDP(基于 QUIC),在一些网络环境下更快,两个节点也能互为备份。它们都用 443 端口,但一个是 TCP、一个是 UDP,互不冲突。

我说的话:

再加一个 Hysteria2 节点

它做了什么:

  • 生成一张自签证书,确认 sing-box 服务用户有权限读取
  • 服务端加 Hysteria2 inbound,客户端 yaml 加 yt-demo-hy2 节点,并用证书指纹锁定(而不是简单地跳过证书校验)

服务端新增的 inbound:

{
  "type": "hysteria2",
  "tag": "hy2-in",
  "listen": "::",
  "listen_port": 443,
  "users": [
    { "password": "ciVyTAZwk6qsexHDke4CcM0K" }
  ],
  "tls": {
    "enabled": true,
    "alpn": ["h3"],
    "certificate_path": "/etc/sing-box/hy2.crt",
    "key_path": "/etc/sing-box/hy2.key"
  }
}

自签证书和指纹:

sudo openssl req -x509 -nodes -days 3650 \
  -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
  -keyout /etc/sing-box/hy2.key -out /etc/sing-box/hy2.crt \
  -subj "/CN=addons.mozilla.org"

# 客户端 fingerprint 字段要的 SHA-256 指纹(去掉冒号、转小写)
openssl x509 -in /etc/sing-box/hy2.crt -noout -fingerprint -sha256 \
  | cut -d= -f2 | tr -d ':' | tr 'A-F' 'a-f'

连不上:包根本没进服务器

配置写完一测,超时。

这里 Agent 的处理方式值得说一下:它没有在服务器上乱改配置,而是一边从本地往服务器发 UDP 探测包,一边在服务器上抓包:

# 服务器上
sudo tcpdump -ni any udp port 443

# 本地
echo test | nc -u -w1 203.0.113.10 443

结果服务器一个包都没收到。包连服务器的网卡都没到,问题就不在 sing-box,而在云厂商那一层。

还记得开机器时安全组只勾了 SSH 和 HTTPS 吗?HTTPS 放行的只是 TCP 443,UDP 从来没有放行过。VLESS 走 TCP 所以一直能通,Hysteria2 走 UDP 就被安全组直接丢掉了。

它直接列出了要加的那条规则,这一步需要自己去控制台操作:

EC2 → 实例 → 安全 → 安全组 → 编辑入站规则 → 添加规则:

类型 协议 端口 来源
自定义 UDP UDP 443 0.0.0.0/0

放行之后:测速和 UDP 缓冲区

放行后再测,通了。它顺手给两个节点做了测速:

节点 下载速度
VLESS Reality 约 5.5 MB/s
Hysteria2(调整前) 约 0.5 MB/s
Hysteria2(调大 UDP 缓冲区后) 约 2.1 MB/s

Hysteria2 一开始慢了十倍,它查出来是服务器的 UDP 接收缓冲区太小,QUIC 在高带宽下会被这个上限卡住,把上限从 4 MB 调到 16 MB 后提升到了 2.1 MB/s:

echo -e "net.core.rmem_max=16777216\nnet.core.wmem_max=16777216" \
  | sudo tee /etc/sysctl.d/99-udp-buffer.conf
sudo sysctl --system
sudo systemctl restart sing-box

测速结果和线路、时段关系很大,这组数字只代表我录制时的那台 t3.micro,不代表两个协议的普遍差距。

一个小细节:Clash Verge 用的是副本

Clash Verge 导入本地配置时,会复制一份到它自己的配置目录,之后改原来的 yaml 文件是不会生效的。Agent 发现了这一点,顺手把新节点同步进了 Clash Verge 实际在用的那份副本。

最后在 Clash Verge 的代理组里,yt-demo 和 yt-demo-hy2 两个节点并排可选。

附:完整服务端配置(由客户端配置反推)

VLESS 和 Hysteria2 的两个 inbound 合在一起,就是服务器上 /etc/sing-box/config.json 的完整结构(示范)。客户端和服务端的参数是成对的,拿着上面那份 Clash 配置就能对出服务端每个字段:

Clash 客户端字段 sing-box 服务端字段 说明
port: 443 listen_port: 443 两个节点同端口,一个 TCP 一个 UDP
uuid users[].uuid 两边必须一致
flow: xtls-rprx-vision users[].flow 两边必须一致
servername tls.server_name + reality.handshake.server 伪装目标站点
reality-opts.short-id reality.short_id[] 服务端可以放多个
reality-opts.public-key reality.private_key 成对生成:私钥只放服务端,公钥给客户端。私钥无法从公钥反推,所以本文重新生成了一对示范密钥
password(hysteria2) users[].password 两边必须一致
alpn: [h3] tls.alpn Hysteria2 固定用 h3
sni + fingerprint certificate_path / key_path 客户端只锁定证书指纹,证书和私钥文件在服务器上
{
  "log": { "level": "warn", "timestamp": true },
  "inbounds": [
    {
      "type": "vless",
      "tag": "vless-in",
      "listen": "::",
      "listen_port": 443,
      "users": [
        { "name": "yt-demo", "uuid": "9bf3fe50-b240-4431-b355-52000d20c45e", "flow": "xtls-rprx-vision" }
      ],
      "tls": {
        "enabled": true,
        "server_name": "addons.mozilla.org",
        "reality": {
          "enabled": true,
          "handshake": { "server": "addons.mozilla.org", "server_port": 443 },
          "private_key": "SKrVhmmC4Dl-IV0tVUQqND8vW8o7j0bmS59s8zuYBVI",
          "short_id": ["d0902526d9e9ea13"]
        }
      }
    },
    {
      "type": "hysteria2",
      "tag": "hy2-in",
      "listen": "::",
      "listen_port": 443,
      "users": [
        { "name": "yt-demo", "password": "ciVyTAZwk6qsexHDke4CcM0K" }
      ],
      "tls": {
        "enabled": true,
        "server_name": "addons.mozilla.org",
        "alpn": ["h3"],
        "certificate_path": "/etc/sing-box/hy2.crt",
        "key_path": "/etc/sing-box/hy2.key"
      }
    }
  ],
  "outbounds": [
    { "type": "direct", "tag": "direct" }
  ]
}

改完配置后先校验再重启,并在 AWS 安全组里同时放行 TCP 443 和 UDP 443:

sudo sing-box check -c /etc/sing-box/config.json && sudo systemctl restart sing-box
sudo ss -tulnp | grep 443   # 应同时看到 tcp 和 udp 两行

总结

回头看整个流程:

步骤 我做的 Agent 做的
开服务器 AWS 控制台开 EC2 —
SSH 一句话 挪密钥、改权限、写 SSH config、验证登录
安装 一句话 探测系统、接官方 APT 源安装
VLESS Reality 一句话 测伪装站点、生成密钥、写配置、外部探测、给分享链接
BBR 一句话 开 BBR、设自启、重启验证
客户端 一句话 + 导入 写 Clash 配置和分流规则、用 mihomo 内核实测
fail2ban 一句话 发现 sshd-session 的坑、修规则、回放验证、加白名单
Hysteria2 一句话 + 改安全组 自签证书、抓包定位安全组问题、调 UDP 缓冲区、同步客户端

除了在控制台开机器、改安全组,其余配置一行都没手敲。

比"省事"更值得说的是途中那两个坑:fail2ban 装了等于没装、UDP 被安全组丢掉。这两个都属于"表面上一切正常"的问题,以前自己配很可能根本发现不了。Agent 能发现,是因为它每做完一步都会去验证,而不是做完就交差。

几条使用建议:

  • 给 Agent 的权限要心里有数。它能 SSH 到你的服务器执行任意命令,建议用一台专门的新机器,别拿跑着生产业务的服务器来试
  • 关键结论自己核对一下。本文每一步的"你可以核对的点"就是为此准备的
  • 密钥、UUID、密码别外泄。分享截图或配置时记得打码

如果你对这套配置模板或者 Agent 驱动的运维方式感兴趣,欢迎留言交流。

关于

关注我获取更多资讯

月球基地博客公众号二维码,扫码关注获取更多 AI 与编程资讯
📢 公众号
月球基地博客作者个人微信二维码,扫码交流 AI 与编程话题
💬 个人号
使用 Hugo 构建
主题 Stack 由 Jimmy 设计