快速开始
仓库是一个 monorepo:server/ 为 Go 控制面,admin-web/ 为 React 管理后台,website/ 为静态官网,shared-core/ 为 Rust 共享核心,clients/ 为五端骨架。
# 已内置便携工具链(Node 24 / Go 1.27)
./scripts/dev.sh # 启动 API + 官网,http://localhost:8080
./scripts/dev-admin.sh # 管理后台开发模式,http://localhost:5173
./scripts/build.sh # 构建后台与 server 二进制
./scripts/test.sh # Go 单测 + Node 模拟客户端端到端测试
管理后台路径为 /admin/,默认开发令牌为 dev-admin-token,通过环境变量 OPENLINK_ADMIN_TOKEN 修改。
服务端当前使用 JSON 文件持久化(server/data/state.json),生产环境建议切换 PostgreSQL,接口层已按可替换 Store 设计。
匿名身份
账号不是服务端"注册"出来的,而是由客户端本地密钥推导:
- 身份密钥:Ed25519,首启本地生成,私钥永不出设备。
- 账号 ID:
usr_ + base32(sha256(identity_pubkey)[:5]),8 位小写字母数字。 - 隧道密钥:WireGuard X25519,可随节点轮换,与账号身份解耦。
- 设备指纹:客户端提交的随机设备标识,服务端加盐哈希后存储,仅用于防滥用。
建档接口是幂等的:同一个身份密钥重复请求会返回同一个账号,不会重复分配 IP 或扣减配额。
API 与签名
所有需要身份的操作都通过请求头传递签名,不使用 Cookie 或会话 Token:
X-Account-ID: usr_xxxxxxxx
X-Identity-Key: <base64 Ed25519 public key>
X-Timestamp: <unix seconds>
X-Nonce: <随机串,≥8 字符>
X-Signature: <base64 Ed25519 signature>
签名原文(| 分隔,严格匹配):
匿名建档: openlink-anon-v1|{account_id}|{identity_key}|{wg_key}|{platform}|{device_fingerprint}|{timestamp}|{nonce}
获取配置: openlink-config-v1|{account_id}|{timestamp}|{nonce}
账号状态: openlink-me-v1|{account_id}|{timestamp}|{nonce}
轮换密钥: openlink-keyrotate-v1|{account_id}|{new_wg_key}|{timestamp}|{nonce}
时间戳窗口为 ±5 分钟,Nonce 15 分钟内不可重复。服务端验签失败会累计风控指标。
接口一览
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/account/anonymous | 幂等匿名建档,返回 WireGuard 配置 |
| GET | /api/v1/config | 签名获取当前最优节点配置 |
| GET | /api/v1/account/me | 签名查看账号、流量与限速 |
| POST | /api/v1/key/rotate | 轮换隧道公钥 |
| GET | /api/v1/nodes | 公开节点状态 |
| GET | /api/v1/announcement | 当前活动公告 |
| POST | /api/v1/feedback | 匿名反馈 |
| POST | /api/v1/node/traffic | 节点上报流量(X-Node-Token) |
| GET | /api/v1/node/peers | 节点拉取 Peer 列表(X-Node-Token) |
| GET | /api/v1/admin/* | 管理接口(X-Admin-Token) |
最小签名示例(Node.js)
import { generateKeyPairSync, sign, createHash } from 'node:crypto';
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const der = publicKey.export({ type: 'spki', format: 'der' });
const raw = der.subarray(-32);
const idKey = raw.toString('base64');
const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
// base32(sha256(raw)[:5]) → accountId = 'usr_' + ...
const ts = Math.floor(Date.now() / 1000);
const nonce = crypto.randomUUID();
const message = `openlink-config-v1|${accountId}|${ts}|${nonce}`;
const signature = sign(null, Buffer.from(message), privateKey).toString('base64');
节点接入
每个节点部署 WireGuard 与 node-agent。控制面通过管理接口登记节点;node-agent 定时拉取 Peer 列表并按返回结果增删对账,同时采集增量流量上报。仓库内置 Go 版 agent(server/cmd/node-agent),支持 OPENLINK_DRY_RUN=1 预演:
cd server
go build -o ../dist/openlink-node-agent ./cmd/node-agent
OPENLINK_API_BASE=https://api.hlutharyea.com \
OPENLINK_NODE_ID=tokyo-1 \
OPENLINK_NODE_TOKEN=<节点令牌> \
OPENLINK_DRY_RUN=1 ../dist/openlink-node-agent
手工排查时可直接调用接口:
curl "http://control-plane:8080/api/v1/node/peers?node_id=tokyo-1" \
-H "X-Node-Token: <节点令牌,后台可复制>"
# 返回 {"node": {...}, "peers": [{"wg_public_key": "...", "assigned_ip": "10.64.0.2"}]}
# 以此列表为准:新增缺失 Peer,删除已不在列表中的 Peer
wg show wg0 dump | awk '{print $1, $6, $7}'
# pubkey, rx_bytes, tx_bytes(需自行计算与上次的增量)
curl -X POST http://control-plane:8080/api/v1/node/traffic \
-H "X-Node-Token: <节点令牌,后台可复制>" \
-H "Content-Type: application/json" \
-d '{
"node_id": "tokyo-1",
"online_peers": 42,
"peers": [{"wg_public_key": "...", "rx_bytes": 1048576, "tx_bytes": 524288}]
}'
流量统计只信任节点上报,不接受客户端自报。账号的日软上限、观察期限速由控制面计算并随配置下发。
每次上报同时充当节点心跳:超过 5 分钟没有上报的节点会被自动标记离线,其上账号迁移到其他在线节点;节点恢复上报后自动回到在线。可用 OPENLINK_NODE_STALE 调整阈值,设为 0 关闭巡检。
自托管部署
生产环境建议使用独立控制面与多地域节点:
- 控制面:1 台 2C2G 云主机,运行 API + 官网;当前版本使用单实例 JSON 持久化,多实例前需替换为 PostgreSQL。
- 节点:每地域 2 台以上,开启 IP 转发,NAT 出口使用 nftables。
- 入口:Nginx 或 Cloudflare 终止 TLS,转发
/api与/admin,传递X-Forwarded-For,并设置OPENLINK_TRUSTED_PROXIES只信任回源代理地址。 - 环境变量:
OPENLINK_ADMIN_TOKEN、OPENLINK_HASH_SALT、OPENLINK_DATA_DIR、OPENLINK_DEV=0。
# 示例:构建并启动
./scripts/build.sh
cd server
OPENLINK_DEV=0 \
OPENLINK_ADMIN_TOKEN="$(openssl rand -hex 24)" \
OPENLINK_HASH_SALT="$(openssl rand -hex 24)" \
../dist/openlink-server.exe
上线前务必:更换管理员令牌与哈希盐、启用 HTTPS、限制管理后台来源 IP、将节点令牌纳入密钥管理,并配置监控告警。
客户端开发
共享核心位于 shared-core/(Rust):负责身份生成、签名、配置构建。各端通过 FFI 或原生实现复用同一套签名规范。
clients/ios:SwiftUI + NetworkExtension,XcodeGen 工程描述。clients/android:Kotlin + Compose + VpnService + WireGuard 库。clients/harmonyos:ArkTS + Stage 模型 +@ohos.net.vpnManager。clients/macos:SwiftUI + System Extension。clients/windows:Tauri 2 + React + WireGuard NT。
各目录 README 包含构建前置条件、签名配置与当前完成度。
安全与合规
本项目是技术实现参考,不构成对任何地区网络接入服务的合规承诺。部署与分发前请自行确认当地法律、应用商店政策与运营商要求。
- 不记录浏览目标、DNS 查询与连接明细。
- 原始 IP 与设备标识仅以加盐哈希保存,且仅用于防滥用。
- 管理员接口必须走 HTTPS,并限制访问来源。
- 禁止将本项目用于攻击、欺诈或其他违法用途。