Celium
Celium 是一个自托管的 mesh 组网软件:每台机器拿到一个稳定的虚拟 IP,能在网络允许 的地方直连、在打不通的地方自动走中继,而身份、访问策略和命名都放在你自己控制的 服务器上。
它由三部分组成,边界很清楚:
| 部分 | 谁来实现 | 现状 |
|---|---|---|
| 数据面引擎(WireGuard、打洞、中继、用户态协议栈) | 直接用 Tailscale 的开源 Go 实现(tailscale.com) | 可行性已验证:engine/tsengine 用 Celium 自己的控制面驱动他们的引擎,直连与中继两条路径都跑通;迁移计划见 docs/MIGRATION.md |
| 协调服务器 + 账号体系 | Celium 自己的 | 已完成并测试 |
| 多平台客户端 | Celium 自己的 | 桌面/服务端已可用;移动端待建 |
账号不外包。 没有 OAuth、没有第三方登录,注册就是你自己的服务器上多一行记录。 详见 docs/ACCOUNTS.md。
$ curl -sX POST https://control.example.com/api/register \
-d '{"username":"alice","password":"correct-horse-1"}'
$ curl -sX POST https://control.example.com/api/node-keys \
-H "Authorization: Bearer $TOKEN" -d '{"description":"laptop","preauthorized":true}'
$ celium up --control-url=https://control.example.com --auth-key=celiumkey-...
$ celium status
web-1 macOS direct 198.51.100.7:41641 12s ago 1.2 MB / 340 kB
db-1 Linux relay hkg 3s ago 88 kB / 12 kB
平台
| 平台 | 形态 | 状态 |
|---|---|---|
| Linux | celiumd 守护进程 + celium CLI,真实 TUN 接口 | 可用(需要 root 建接口) |
| macOS | 同上,utun 接口 | 可用 |
| Windows | 同上 | 核心可交叉编译(已验证),TUN 后端待接 |
| Android | gomobile 绑定同一份 Go 核心 + VpnService | 待建 |
| iOS | gomobile 绑定同一份 Go 核心 + NEPacketTunnelProvider | 待建 |
| 鸿蒙 | 需单独评估(HarmonyOS NEXT 无官方 Go 运行时) | 待评估 |
移动端的主要工作量不在 Go,而在平台入口:手机不允许普通 App 建 TUN 设备,必须由 系统的受管组件(Android VpnService / iOS NEPacketTunnelProvider)把文件描述符交给 核心。详见 docs/CLIENTS.md。
桌面/服务端用 Go 原生跑,移动端计划用 gomobile 把同一份核心打包进去——不是 重写一遍协议栈,而是让手机和电脑跑同一套入网、打洞、加密代码,这样"手机登录 Celium 和电脑登录 Celium 可以互联"就是自然的,而不是需要对齐的两套实现。
账号体系(已完成)
Account --拥有--> User --拥有--> Node
| |
| +-- ACL 规则写的就是它
+-- 登录、签发节点密钥、邀请他人
- 第一个注册的账号自动成为管理员(否则部署无法管理)。
- 之后的账号凭邀请码加入(默认一次性,可设次数与有效期)。
- 用户可以为自己名下的机器签发节点密钥,机器用它入网;节点归属于签发者。
- 会话令牌只以 SHA-256 落盘,密码用 bcrypt(cost 12),登录失败按「用户名+来源地址」
限速,改密码/停用账号会立刻注销会话。
完整 API 与安全取舍见 docs/ACCOUNTS.md。
架构
协调服务器只做四件事:认证机器、分配地址、下发策略、充当兜底中继的目录。它不 持有任何能解密业务流量的密钥——被攻破的后果是"策略被篡改",而不是"流量被读取"。 完整设计见 docs/ARCHITECTURE.md。
- 控制通道:HTTPS 上的 HTTP Upgrade + Noise IK 握手,Noise 静态密钥由机器密钥
确定性派生,因此握手本身即完成设备认证;客户端 pin 控制公钥。
- 节点证书:节点密钥由控制密钥签名,对端可以离线验证身份,建连时无需回查服务器。
- 数据面:WireGuard,端到端加密;中继只转发密文。
- 打洞:disco 消息与数据走同一个 UDP socket(NAT 映射属于 socket),端到端认证。
构建与测试
make build # bin/celium、bin/celiumd、bin/celium-control
make test # 单元测试
make e2e # 端到端:真实控制面 + 真实中继 + 真实节点 + 真实流量
纯 Go,无 cgo,无需代码生成。gvisor.dev/gvisor 在 go.mod 中被钉住:更新的版本 里有一个测试文件会让依赖方无法编译,升级前请先确认 go build ./... 仍然通过。
当前状态与限制
已经过端到端验证(tstest/ 中的测试会真的起服务器、中继和节点):
- 自有账号注册/登录/邀请 → 签发节点密钥 → 节点入网 → 跨节点真实 TCP 流量
- 直连打洞成功后自动从"中继"切到"直连"
- 纯中继路径可用(用于双方都在硬 NAT 后的情况)
- ACL 生效:默认拒绝,放行规则真的放行,拒绝规则真的拒绝
- 一个节点下线后,对端地图里立刻标记为离线
- 守护进程先启动、之后
up的路径(这条曾经是坏的)
尚未完成或未验证:
- 引擎迁移:可行性已验证(
engine/tsengine),迁移清单与风险见
docs/MIGRATION.md。在迁移完成前,仓库里两套实现同时在 (自研的与 Tailscale 的),最终要删掉一套。迁移的验收标准是现有端到端套件全部 通过,而不是"能编译"。
- 子网路由与出口节点:路由的宣告、审批、下发已完成,TUN 模式的转发与 SNAT
路径没有被自动化测试覆盖。
- 移动端客户端尚未开始。
- tailnet lock(把可信签名密钥集合钉死)未实现:被攻破的协调服务器可以添加节点。
- 密钥轮换只在节点启动时发生,没有运行中的定时轮换。
- 中继每个区域单点,区域间可互联,但区域内部没有多中继故障转移。
- Windows 的 TUN 后端未实现(用户态数据面在 Windows 上理论可用,未验证)。
- DNS 只在 TUN 模式接管宿主解析器;用户态模式下宿主的 DNS 不动(否则会把整台
机器的解析打死),MagicDNS 由进程内解析提供。
仓库结构
types/ 密钥、控制协议类型、运行时网络视图、偏好
control/controlhttp Noise 加密的控制通道
control/controlclient 客户端:注册、netmap 长轮询、状态折叠
controlplane 协调服务器:注册表、地址分配、ACL 编译、账号、管理 API
derp 中继协议、服务端与客户端
disco 端点发现与打洞
wgengine WireGuard 数据面、netmap 翻译、ACL 执行
wgengine/magicsock 端点管理:直连优先、中继兜底
net/tun 真实 TUN 设备与路由(macOS、Linux)
net/netstack 用户态数据面(gVisor),无需特权
net/dns MagicDNS 与宿主 DNS 配置
net/packet IP 解析与编译后的 ACL
net/socks5 用户态模式的出口代理
ipn/local 本地后端:把控制面、数据面、DNS、ACL 接到一起
ipn/ipnserver CLI 与之通信的本地 API
ipn/state 机器密钥、节点密钥、偏好持久化
cli, cmd 三个二进制
tstest 端到端测试
engine 数据面迁移到 Tailscale 实现的验证与结论
docs 架构、账号、引擎迁移与结论