服务器不编译、不联网安装依赖:本地产出静态二进制 + 安装脚本,服务器只解包执行。
全流程集成在 cargo xtask 里。
# 1) 打包 linux/amd64 服务端(默认用 deploy/Containerfile 的固定环境构建;
# 没有容器引擎时自动/显式退回宿主机 rust-lld 交叉:加 --no-container)
cargo xtask package server --platform linux/amd64
# 产物:dist/phantom-server-<version>-linux-amd64.tar.gz (+ .sha256)
# 2) 容器内离线端到端验证(起 alpine:3.18 amd64 容器跑真实二进制 +
# 本地 minihttpd origin,字节对账;不依赖任何被墙目标)
cargo xtask verify server --platform linux/amd64
# 3) 上传 + 安装 + 回显 phantom:// URI
cargo xtask deploy server \
--host root@203.0.113.10 \
--public-host 203.0.113.10 \
--port 443 --proto tcp
# 4) 速度基线 / 解锁断言
cargo xtask speedtest --uri "<URI>" --check-unblock --vps-host root@203.0.113.10
cargo xtask speedtest --loopback # 客户端软件上限(本机 server+client+origin)构建环境说明(deploy/Containerfile):
| 阶段 | 基础镜像 | 作用 |
|---|---|---|
builder |
rust:1.96-alpine3.18 |
交叉编译(rust-lld + self-contained musl CRT,不跑 QEMU 编译);工具链与 crate 走 RsProxy |
artifact |
scratch |
导出裸静态二进制,供 -o type=local,dest=... 提取 |
test-origin |
scratch |
tests/e2e/minihttpd.rs 编出的离线 origin(只用于验证,不进部署包) |
verify |
alpine:3.18 |
在生产同版本的 Alpine 上跑一次 --version/server --help |
runtime |
alpine:3.18 |
可选:可直接运行的 OCI 镜像(本 VPS 不用) |
多环境:--platform linux/amd64(x86_64 服务器)与 --platform linux/arm64
(ARM 服务器/路由器)用同一份 Containerfile。
Alpine 服务器上的安装细节、取 URI、运维命令、以及"为什么最终以 root 运行"
见 alpine/README.md。
本地网络到不了 Google 时,"部署前验证"必须用可控目标,解锁结论留给部署后:
| 阶段 | 目标 | 断言 |
|---|---|---|
| 部署前(离线) | 本地 minihttpd origin 的 /10mb.bin |
经隧道拉取后 sha256 与服务端完全一致 |
| 部署前(离线) | http://www.baidu.com |
经隧道 200/301(证明真实公网中继可用,国内可达目标) |
| 部署后 | https://www.google.com/generate_204 |
HTTP 204 |
| 部署后 | https://www.gstatic.com/generate_204 |
HTTP 204 |
| 部署后 | https://www.cloudflare.com/cdn-cgi/trace |
ip=<服务器IP> loc=HK colo=HKG(出口身份证据) |
| 部署后 | https://www.youtube.com/ |
HTTP 200 |
以上部署后目标均已在这台 HK 服务器上实测通过。cargo xtask speedtest --check-unblock
会一次性跑完并打印结果。
要点:经 SOCKS5 的连接由服务端解析域名(--socks5-hostname),本地 DNS 污染
不影响结果;若浏览器失败而 curl 成功,先用 cdn-cgi/trace 判定是 DNS 还是链路,
再按浏览器 Secure DNS / 系统 DNS 顺序处置。TUN 模式下白名单域名由隧道内
client.dns(默认 8.8.8.8:53)解析,其余域名走 client.dns_direct
(默认 223.5.5.5:53)从物理网卡解析。
deploy/ 目录下的 systemd 资产:
| 文件 | 用途 |
|---|---|
install.sh |
安装预编译二进制 + 准备 systemd 工作目录 + 安装 service 单元(不再在目标机上编译) |
phantom.service |
systemd 单元;ExecStart=/usr/local/bin/phantom server --port 443 --proto tcp |
部署后唯一的"配置文件"是服务端在启动目录下生成的 server.toml:头部带 phantom:// URI 快速链接注释,下方有 bind / cipher / protocol 配置以及内联 [[allowed_clients]] 白名单数组。
服务端会把以下文件写到当前工作目录(CWD):
| 文件 | 用途 | 权限 |
|---|---|---|
server.key |
X25519 私钥(第 1 行公钥,第 2 行私钥) | 600 |
server.toml |
bind / cipher / protocol + 顶部 URI 注释 + [[allowed_clients]] 白名单 |
644 |
install.sh 把工作目录固定为 /var/lib/phantom,并以专用系统用户 phantom 运行。
首次启动时,server 自动生成密钥和 URI,并把它们写到该目录。
sudo systemctl start phantom
# 查看启动日志(应包含 "Phantom server bootstrapped" 摘要):
sudo journalctl -u phantom -f如果是手动运行:
cd /var/lib/phantom
sudo -u phantom phantom-server
# 或在普通用户目录下直接:
cd ~/phantom && phantom serversudo grep '^# phantom://' /var/lib/phantom/server.toml | sed 's/^# //'
# phantom://<服务器公钥base64>@<服务器地址>:443?cipher=auto&proto=tcp#default把这个 URI 分发给客户端:
URI=$(sudo grep '^# phantom://' /var/lib/phantom/server.toml | sed 's/^# //')
phantom client --server "$URI"# 1. 让客户端先生成自己的公钥:
phantom client --print-key # 输出 32 字节 base64 公钥
# 2. 编辑服务端 server.toml,向 `[[allowed_clients]]` 数组追加公钥:
sudo -u phantom tee -a /var/lib/phantom/server.toml >/dev/null <<'EOF'
[[allowed_clients]]
public_key = "<客户端公钥>"
name = "client-laptop"
EOF
# 3. 重启服务端使白名单生效:
sudo systemctl restart phantom客户端在 open 模式下可直连;一旦
[[allowed_clients]]段有非空条目,则只接受白名单中的客户端。
| 标志 | 默认值 | 说明 |
|---|---|---|
--port <p> |
443 | 起始端口;占用时自动 +1,最多重试 10 次 |
--public-host <h> |
自动探测出口 IP | 写入 server.toml URI 注释的 host 部分 |
--cipher <c> |
auto | auto / aes-256-gcm / aes-128-gcm / ascon-128 / chacha20-poly1305 |
--proto <p> |
tcp | tcp / quic |
-i / --interactive |
关闭 | 启用交互式向导(需要 TTY) |
需要 io_uring、精细的拥塞控制、ACME、ACL 等高级特性时,回退到 TOML 配置:
sudo mkdir -p /etc/phantom
sudo cp config/server.toml /etc/phantom/server.toml
# 复用自举模式生成的密钥(也可以用 auto 模式跑一次拿 server.key):
sudo cp /var/lib/phantom/server.key /etc/phantom/server_private
# 编辑 /etc/phantom/server.toml,填入正确的 bind / private_key;如需白名单,向 [[allowed_clients]] 追加条目编辑 /etc/systemd/system/phantom.service:
[Service]
# 注释自举模式,启用 load 模式:
# ExecStart=/usr/local/bin/phantom-server
ExecStart=/usr/local/bin/phantom-server /etc/phantom/server.toml
WorkingDirectory=/etc/phantom
ReadWritePaths=/etc/phantom /var/lib/phantom然后:
sudo systemctl daemon-reload
sudo systemctl restart phantom不推荐混用两种模式:如果同时存在
/var/lib/phantom/server.key和/etc/phantom/server.toml中的private_key,以ExecStart指定的配置为准。
phantom-server 在 auto 模式下依赖 CWD 解析 ./server.key 与 ./server.toml(whitelist 也在 toml 内的 [[allowed_clients]])。
systemd 默认 CWD 是 /,所以单元文件必须显式设置 WorkingDirectory 到可写目录,
并把该目录加入 ReadWritePaths(ProtectSystem=strict 强制只读大部分路径)。
如果需要把端口从默认 443 改成其他值:
sudo systemctl edit phantom
# 写入:
[Service]
ExecStart=
ExecStart=/usr/local/bin/phantom-server --port 8443如果要让 service 以 root 身份运行(仅用于监听 1024 以下端口):
sudo systemctl edit phantom
# 写入:
[Service]
User=root
Group=root# 实时日志
sudo journalctl -u phantom -f
# 最近 100 行
sudo journalctl -u phantom -n 100 --no-pager
# 单元已配置 Restart=always / RestartSec=5:进程崩溃 5 秒后自动拉起自举默认端口 443(可改)。防火墙放行:
# ufw
sudo ufw allow 443/tcp
# firewalld
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload自举模式下:./server.key(CWD 相对路径)。systemd 默认在 /var/lib/phantom/server.key。
权限 600,owner 为 phantom 用户。
自举模式下:./server.toml(CWD 相对路径)。systemd 默认在 /var/lib/phantom/server.toml。
每次启动时覆盖写入;其顶部带 URI 注释,下方有内联白名单数组。
- 备份自举文件:
sudo cp /var/lib/phantom/server.key /etc/phantom/server_private sudo cp /var/lib/phantom/server.toml /etc/phantom/server.toml
- 编辑
/etc/phantom/server.toml,向[[allowed_clients]]追加条目(如需白名单)。private_key必须指向 600 权限的私钥文件。 - 改 service 单元的
ExecStart和WorkingDirectory(见 §2.2)。
需要备份的只有两个文件:
# 备份
sudo tar czf phantom-backup.tgz /var/lib/phantom/server.key /var/lib/phantom/server.toml
# 在新机器上恢复
sudo mkdir -p /var/lib/phantom
sudo tar xzf phantom-backup.tgz -C /
sudo systemctl restart phantom恢复后新机器的 server.toml 会写入新的 host:port(基于新机器的网卡),客户端需要更新 URI 中的 host。
server.toml URI 注释中的 host 部分由服务端自动探测(UDP socket trick 拿到主出接口 IP)。
如果探测到错误的网卡 IP,可以:
- 用
--public-host your.domain.com覆盖 - 或用 TOML 模式手动指定
bind
默认从 443 开始;占用则尝试 444、445、…、452(共 10 次)。 10 次都失败则报错列出尝试范围:
Error: No free TCP port in 0.0.0.0:443..452 (10 attempt(s) all busy): Address already in use
server.toml URI 注释中写入的端口是服务端实际监听的端口(不是 --port 传入的)。
如果看到 :8443 而你传入的是 443,说明 443 被占用,自动递增到 8443 了。
客户端按 URI 中的端口连接即可。
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
journalctl 报 "Permission denied" 写 server.key |
WorkingDirectory 不可写 |
确认 ReadWritePaths 包含该目录;目录 owner 是 phantom |
| 客户端连不上 | URI 中的 host 不可达 | 用 phantom client --server phantom://...@<正确的IP或域名>:443 覆盖;或加 --public-host |
| 客户端握手被拒 | 白名单不含客户端公钥 | 编辑 server.toml 的 [[allowed_clients]] 段追加客户端公钥后 systemctl restart phantom |
| 端口连续占用 10 次 | 端口段被其他服务占满 | 用 --port 改成其他段(如 --port 8443) |
| auto 模式下重新生成密钥(导致客户端失效) | 删除了 server.key |
不要删;备份迁移见 §4.4 |
| 切换到 load 模式后启动失败 | TOML 中 private_key 路径错误 |
用 sudo -u phantom cat <path> 验证文件可读 |
调试模式(更详细日志):
sudo systemctl edit phantom
# 写入:
[Service]
Environment=RUST_LOG=phantom_server=debug,phantom_core=debug
sudo systemctl daemon-reload
sudo systemctl restart phantom
sudo journalctl -u phantom -fsudo systemctl disable --now phantom
sudo rm /etc/systemd/system/phantom.service
sudo rm /usr/local/bin/phantom-server
sudo rm -rf /var/lib/phantom /etc/phantom
# 可选:删除 phantom 系统用户
sudo userdel phantom
sudo groupdel phantom
sudo systemctl daemon-reload