在 Apple Silicon Mac 上用命令行连清华 LeadSec PowerVPN 后面的服务器。 隧道由我们自己打过补丁的 strongSwan 建立,原生 arm64,不需要安装官方 PowerVPN 客户端。 第一次由你本人在终端里构建、安装、写配置、往服务器装好 SSH 公钥,确认能 SSH 上去;之后的日常命令可以全部交给 coding agent。
powervpn run thu21 -- cmd 你的用户身份
├─ Portal 登录、选资源(和官方客户端走同一套认证,不绕过登录)
├─ 通过 VICI 让 charon 建 IKEv1 和隧道
└─ 作为 ssh 的 ProxyCommand 转发 ──► 只有目标 IP 走隧道
powervpn-native-helper root LaunchDaemon,安装一次
├─ 常驻运行打过补丁的 charon(strongSwan 6.0.7)
├─ VICI 代理逐帧检查命令与配置字段;charon 的原始 socket 只供 root 访问
└─ 控制接口:查状态、为安装时授权的目标加 /32 路由、删掉它
- 默认路由、DNS 和 Surge 都不动,只有你配置的目标 IP 走隧道。
- helper 不执行任何调用方给的命令或路径,控制 socket 只对安装它的用户开放。
- 正常断开会核对路由、SA、连接配置、共享密钥和 Portal 登出。异常退出后的恢复只证明本地对象已清理;Portal 登出无法确认时保留原记录,后续连接前归档到
~/.local/state/powervpn/native-recovery-*.json。 - 清理由会话记录中的 backend 决定;修改
targets.json不会清除另一后端的未完成清理。没有 backend 字段的旧记录按 vendor 处理。
-
arm64 Mac,macOS 14 或更新。
-
Homebrew。它会顺带装好 Xcode Command Line Tools,编译只需要这个,不需要完整的 Xcode。
-
你自己的 Portal 账号,以及要连的服务器 IP、SSH 用户名,和它在 Portal 里对应的资源名。
-
这个 SSH 账号的密码(只在第一次往服务器装公钥时用一次)。之后都是公钥登录。
1. 安装
brew install hhh2210/powervpn/powervpnHomebrew 在本机从源码编译 CLI、root helper 和打过补丁的 strongSwan 6.0.7,约几分钟。 想直接从源码构建,见文末「从源码构建」。
2. 写账号密码
mkdir -p ~/.config/powervpn
cat > ~/.config/powervpn/credentials.env <<'EOF'
PORTAL_USERNAME=你的账号
PORTAL_PASSWORD=你的密码
EOF
chmod 600 ~/.config/powervpn/credentials.env权限不是 0600 时命令会直接拒绝运行。密码只从这个文件读,不会出现在命令行参数、日志或报告里。
3. 写要连的服务器
把尖括号里的内容换成你自己的,再执行:
cat > ~/.config/powervpn/targets.json <<'EOF'
{
"portalOrigin": "https://<Portal 地址>:<端口>",
"backend": "native",
"targets": {
"lab-a": { "host": "<服务器 IPv4>", "user": "<SSH 用户名>", "resource": "<Portal 里的资源名>" }
}
}
EOF
chmod 600 ~/.config/powervpn/targets.jsonlab-a是你给这台机器起的名字,后面所有命令都用它。portalOrigin就是你在浏览器里登录 VPN 门户的地址。resource必须和 Portal 资源列表里的名字一字不差。backend不写时默认是旧的官方 helper 方案,所以这里要写"native"。- 可以写多台;同一时间只连其中一台。换另一台之前先
powervpn down,否则会报connected_to_other_target。
4. 安装 native 后端(只需要一次 sudo)
sudo "$(brew --prefix)/bin/powervpn" native install它只装我们自己的东西:/Library/PrivilegedHelperTools/com.hhh2210.powervpn-native/ 和一个 LaunchDaemon。
看到 install: native backend running 就好了。
安装时会把 targets.json 的目标 IP 固定到 root 拥有的路由白名单。以后新增或修改目标 IP,需重新执行安装命令。
装好之后,常驻的 root 进程只运行复制进系统目录、校验过的副本,从不执行 Homebrew 目录(当前用户可写)里的文件。
安装这一步本身和任何 sudo 命令一样,信任你自己账户下的文件。
5. 配 SSH
在 ~/.ssh/config 里给每个目标加一段,名字和 targets.json 里的一致:
Host lab-a
HostName <服务器 IPv4>
User <SSH 用户名>
ControlMaster auto
ControlPath ~/.ssh/cm-%C
ControlPersist 8h
HostName 和 User 必须和 targets.json 对上,端口用默认的 22,ControlPath 要放在 ~/.ssh/ 下。
不要在这里写 ProxyCommand,powervpn 会自己注入。
6. 把 SSH 公钥装到服务器上
powervpn run 和 powervpn up 在后台启动 SSH,没法输入密码,所以服务器必须能用公钥登录。
下面的命令假设密钥是默认路径的 Ed25519 密钥 ~/.ssh/id_ed25519。还没有密钥就先生成一把:
ssh-keygen -t ed25519已经有别的密钥(比如 ~/.ssh/id_rsa)也可以直接用:把下面命令里的路径换成你的 .pub 文件。
ssh 会自动尝试 id_rsa、id_ecdsa、id_ed25519 这些默认文件名;其他路径要在 Host lab-a 里写 IdentityFile。
然后用一次密码,把公钥追加到服务器的 ~/.ssh/authorized_keys:
powervpn ssh lab-a -- 'umask 077; mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_ed25519.pub- 它会先问是否信任服务器的主机密钥(输入
yes;记下之后,后台的run才连得上),再问服务器上的 SSH 密码。 - 还没有隧道或 SSH master 时,
ssh-copy-id lab-a连不上:~/.ssh/config里没有ProxyCommand,只有powervpn会注入它,所以不会自动建立 VPN。 - 每台服务器装一次;几台服务器共用同一个家目录时,装一次就够。
- 密钥设了 passphrase 时,先用
ssh-add --apple-use-keychain ~/.ssh/id_ed25519(换成你的私钥路径)把它加进 ssh-agent。后台的 SSH 同样没法输入 passphrase。
7. 自检并连一次
powervpn doctor # 检查本机这边,应显示 ready;它不检查 SSH 登录
powervpn run lab-a -- hostname第二条能打印出服务器的主机名,就说明 Portal 登录、隧道和公钥登录全都通了。 这和 agent 用的是同一条命令:这里能过,交给 agent 也能用。
想长时间挂着(比如给 VS Code / Cursor Remote-SSH 用):
powervpn up lab-a # 建立后台会话
ssh lab-a # 普通 ssh 和 Remote-SSH 都复用这条会话
powervpn down # 用完断开,并核对路由、SA 都已清理自检通过之后,Agent 只需要一条命令:
powervpn run lab-a -- 'nvidia-smi -L'run 会复用已有的会话(一条命令约 0.1 秒),没有就自动建一个(约 1.5 秒)。
遇到 Portal 资源列表偶发不完整这类瞬时失败,它会自己重试。多个 run 可以同时跑,共用同一条会话。
把 skills/powervpn/SKILL.md 装进 agent 的 skill 目录(Claude Code 是 ~/.claude/skills/powervpn/),
它就知道该怎么用 run,什么时候自己恢复,什么时候该来找你。
退出码:
PowerVPN 自己出错时,stderr 最后两行固定是 powervpn run: <原因> 和 next: <下一步>。
没有这两行,说明 75/77 是远端命令自己返回的。
- 先跑
powervpn doctor,它会指出第一个要修的问题和下一步。 - native 登出会用同一会话做前后只读探测:只有确认已失效才报告完整清理。网关的 logout HTTP 302 不会被跟随,也不直接视为成功。若探测明确仍有效,自动重连被阻止;只有本人在 Portal 结束该会话后,才使用
powervpn recovery clear --json --acknowledge-portal-logout。这个显式确认保留原始未验证回执,不用于自动重试。 native_portal_unavailable/native_resource_unavailable:Portal 传输或目录暂不可用,run有限重试后返回 75,不误报密码错误。native_helper_unavailable/native_charon_unavailable:helper 没装或没在跑,按next:行重新安装 native 后端。run报ssh_auth_rejected(退出码 77):服务器不接受 ssh 递上去的任何一把密钥。后台 SSH 没法输入密码或 passphrase, 原样重跑不会好。powervpn doctor不检查 SSH 登录,它显示 ready 也不代表能登录。- 公钥还没装到服务器上:按第 6 步装。
- 密钥设了 passphrase:按第 6 步最后一条把它加进 ssh-agent。
- 公钥装了、也在 ssh-agent 里,但 agent 里密钥太多,服务器在试到正确那把之前就断开(
Too many authentication failures): 在Host lab-a里写IdentityFile <你的私钥路径>和IdentitiesOnly yes,只递这一把。第 6 步的安装命令遇到同样的报错时也这样处理。
run报ssh_host_key_rejected(退出码 77):known_hosts里没有这台服务器的主机密钥,或者它变了。- 还没记下:
powervpn ssh lab-a -- true,核对指纹后输入yes。 - 变了:
next:会写明旧记录在哪个文件的第几行。这取自 ssh 自己报的Offending … key in <文件>:<行号>,所以自定义的UserKnownHostsFile也不会指错。 先确认服务器确实换过密钥,删掉那一行,然后同上。 ~/.ssh/config里写了StrictHostKeyChecking yes时 ssh 不会提示,要自己把核对过的密钥加进known_hosts。~/.ssh/config里写了LogLevel QUIET时,powervpn ssh不会显示 ssh 的原因;排查期间先改成LogLevel ERROR。
- 还没记下:
- 其他 SSH 启动失败仍报
session_start_failed(75,第一行是SSH master did not start),可以原样重跑; 反复出现时用powervpn ssh lab-a -- true看 ssh 自己怎么说。 - 日志(需要 sudo 读):
/var/log/powervpn-native-helper.log、/var/log/powervpn-native-charon.log。 - 升级:
brew upgrade powervpn。只有 helper 或 strongSwan 运行时变了才需要再 sudo 一次;powervpn native status或powervpn doctor会告诉你,并给出要执行的命令。 - 卸载:
sudo "$(brew --prefix)/bin/powervpn" native uninstall,再brew uninstall powervpn。
LeadSec 网关说的是带私有扩展的 IKEv1:Quick Mode 里要带一段 ADDRULE 载荷描述资源,
Main Mode 第一个包还要带 NEW CLIENT 这个 Vendor ID。缺了后者,网关照样完成 IKE、建隧道、
转发入向流量,就是不给虚拟 IP 回 ESP,表现为能连上却收不到任何回包。
补上之后对照实验 6/6 对 0/6,随后连续 10/10 都能 SSH 上去。
补丁和清单在 patches/strongswan-6.0.7/,过程和数据见 docs/evidence/native-strongswan-backend.md。
不写 backend 或写 "backend": "vendor" 时,powervpn 改为驱动官方 PowerVPN 装的 x86 特权 helper。
它需要先装官方客户端,断开时证明不了清理干净,会进入隔离和 recovery 流程。
只作为回退保留,见 docs/vendor-backend.md。
不用 Homebrew 时,先装构建依赖,再用仓库里的脚本:
brew install autoconf automake libtool gettext bison gperf pkgconf openssl@3
git clone https://github.com/hhh2210/powervpn-cli.git
cd powervpn-cli
scripts/native/build_runtime.sh # 拉 strongSwan 6.0.7,打补丁,编译,约 1–2 分钟
swift build -c release --arch arm64
mkdir -p ~/.local/bin && cp .build/release/powervpn ~/.local/bin/ # 或任何在 PATH 里的目录
sudo scripts/native/install.sh # 代替 powervpn native install;升级后同样重跑卸载用 sudo scripts/native/uninstall.sh。
powervpn-cli 是 MIT。native 后端运行的是 strongSwan 6.0.7(GPL-2.0-or-later)加 patches/strongswan-6.0.7/ 的补丁,
带一份 OpenSSL libcrypto(Apache-2.0)。两者的许可证和源码出处随运行时安装在
/Library/PrivilegedHelperTools/com.hhh2210.powervpn-native/runtime/share/licenses/。
swift build --arch arm64
scripts/hermetic_swift_test.sh --no-parallel # swift test,但读不到本机真实的 PowerVPN 状态Sources/
PowerVPNCore/ VICI、helper 协议、路由与清理判定
PowerVPNPortal/ Portal 登录、凭据、cookie、资源目录
PowerVPNProduct/ run/up/ssh/down 的编排;NativeTunnel 是 native 后端
PowerVPNCLI/ 命令行入口
PowerVPNNativeHelper/ root helper(charon 守护、/32 路由)
PowerVPNNativeTrial/ native 后端的实验驱动
patches/strongswan-6.0.7/ charon 补丁序列
scripts/native/ runtime 构建、安装、卸载
skills/powervpn/ 给 agent 的 skill
docs/ 协议取证、设计和历史记录
main 是唯一的长期分支,功能分支通过 PR 合进来,发布用 tag(见 docs/branching.md)。
