No description
  • Rust 89.5%
  • Shell 10.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-06 12:56:52 +08:00
docs fix: keep automatic IPv6 discovery outside XFRM routes 2026-10-04 22:32:23 +08:00
examples docs: define native ESP mesh architecture and PSK handshake 2026-10-04 20:09:35 +08:00
openwrt Remove deployment records and integration test directory 2026-10-06 12:56:52 +08:00
src fix: keep automatic IPv6 discovery outside XFRM routes 2026-10-04 22:32:23 +08:00
systemd deploy: add systemd service 2026-10-04 23:48:49 +08:00
.gitignore docs: define native ESP mesh architecture and PSK handshake 2026-10-04 20:09:35 +08:00
Cargo.lock feat: add validated mesh configuration and authenticated realm handshake 2026-10-04 20:16:06 +08:00
Cargo.toml build: optimize and strip release binaries for router deployment 2026-10-04 23:00:22 +08:00
LICENSE docs: document deployment, PSK trust and verified lifecycle behavior 2026-10-04 20:46:53 +08:00
README.md Remove deployment records and integration test directory 2026-10-06 12:56:52 +08:00

ip2psec

用 Linux 原生 ESP 建立多点 P2P VPN,借助 Hysteria Realm Server 发现对端和协商会话。外层是 IPv6/ESP,内层支持 IPv4、IPv6 和配置的对端网段。没有 UDP 数据面、IKE、流量中继或长期公私钥文件。

双方定期发送隧道内 ping,维持光猫或防火墙的 ESP 放行状态。daemon 自动处理 realm 重连、对端重启、IPv6 地址变化、连续保活失败和定期换钥。加解密由内核 XFRM 完成。

运行

需要 Linux、root、完整 iproute2 的 XFRM/JSON 支持、IPv4/IPv6 ping、XFRM interface 和 AES-GCM 内核支持。两端应有可互达的 IPv6;本项目不处理 IPv4 NAT 后的 native ESP,也不会回退到 UDP。

cargo build --release
./target/release/ip2psec gen-psk
cp examples/node.toml home.local.toml

将生成的 64 位十六进制 PSK 安全地复制到所有节点的凭据文件。凭据文件和 Git 提交分开管理:

HYSTERIA_REALM_TOKEN=你的realm登录token
IP2PSEC_PSK=gen-psk输出的网络PSK
chmod 600 .env
./target/release/ip2psec check -c home.local.toml
sudo ./target/release/ip2psec --env-file "$PWD/.env" run -c home.local.toml

需要凭据的命令未指定 --env-file 时,只读取当前工作目录的 .env;环境变量优先于文件。gen-psk 和 cleanup 不读取凭据。gen-psk 有意将新密钥输出到 stdout,其余命令不输出会话密钥或凭据。

按 examples/node.toml 配置本节点名字和 VPN 地址,并将 realm.url 改为你使用的 HTTPS realm 服务地址。endpoint 是公网外层 IPv6,与 VPN 本地地址不同;省略时,daemon 每 15 秒根据通往 discovery_target 的路由发现源地址。对端填写名字、用于保活的 vpn_address 和 allowed_ips。vpn_address 必须包含在 allowed_ips 中,双方要互相配置。节点名字和 VPN 地址应在网络中唯一。未指定 endpoint 时,discovery_target 必须在对端路由之外;自动发现也会拒绝使用 XFRM 设备,避免把 VPN 地址当作外层地址。

本地 VPN 地址以 /32 或 /128 分配到节点的共享 XFRM 接口,避免自动生成会与对端路由冲突的连接网段。不同对端的 allowed_ips 不能重叠,也不能包含本地 VPN 地址或 ESP 外层地址。它同时限制去往对端的目的地址和对端可以发送的源地址。配置 LAN 网段时,需要由管理员在两端配置 IP forwarding、LAN 回程路由和防火墙转发。

realm.proxy = "http://127.0.0.1:1082" 可用于 HTTPS 信令,VPN 流量仍直接通过 ESP。每个节点只注册一个 realm,与其对端数量无关。realm token 只用于服务器登录。没有必要给每一对节点创建不同的 token。

Linux 默认直接管理接口地址和路由。OpenWrt 从 /etc/config/network 设置 proto 'ip2psec',凭据保存在同一接口的 realm_token 和 psk 选项中。由一个 netifd 协议脚本启动 daemon、生成配置并管理路由,见 OpenWrt 文档 和 UCI 配置示例。

使用 systemd 的主机可安装 ip2psec.service,程序和配置放在 /opt/ip2psec,安装步骤与清理行为见 systemd 文档。

密钥和服务器信任

使用单独随机生成的网络 PSK,不能从 realm token 派生。节点名字通过带 PSK 的 HMAC 派生为不可猜测的 realm ID。握手使用临时 X25519,HMAC-SHA256 认证节点名字、公网地址、公钥和握手阶段,HKDF-SHA256 派生两个方向的 AES-128-GCM 密钥、salt 和 SPI。会话安装完成后立即销毁用户态临时 DH 私钥。

realm 服务器能看到公网地址、临时公钥和连接时间,能阻断连接,但不知道 PSK 时不能冒充节点或计算流量密钥。临时 DH 提供前向保密。共享 PSK 属于信任组认证,任何持有者都可以冒充组内其他节点;它不提供逐节点权限隔离。握手重传不会重装已经安装的 SA,因此不会以同一 AES-GCM 密钥重置发送序号。新的会话总是使用新的临时密钥。

这是自定义握手协议,使用标准密码学库,尚未经过独立密码协议审计。完整协议、重放行为和密钥派生见 设计文档。

生命周期

每个节点只有一个 XFRM 接口,名称是 psec 加上 id_base 的十六进制形式,所有对端共用这个 if_id。按 id_base + peer_index 为各对端分配独立 reqid 和 policy priority,用 allowed_ips 选择对端并限制入站源地址。VPN 地址只安装一次。该范围必须与已有 VPN 分开,运行中的实例使用逐 ID 文件锁防止范围重叠。已有接口、XFRM ID 或路由冲突会使启动失败,daemon 会清理自己已经创建的对象。不要在运行中重排 peers。

SIGINT/SIGTERM 退出时按 if_id、reqid 和策略选择器核对对象,再按精确身份删除自己的 policy 和 SA,不执行全局 XFRM flush。清理失败时保留带所有权标记的接口,供后续重试。SIGKILL 或掉电后可能留下内核对象;使用原来的配置进行所有权校验和清理:

sudo ./target/release/ip2psec cleanup -c home.local.toml

工作中的隧道在 realm 断线后仍保活。换钥时保留接口和路由,但切换 SA 会有短暂丢包。内核 SA 硬限制是 rekey_secs + 120 秒和每方向 2^30 包;长时间无法协商新会话时,旧会话到期后停止传输。默认每小时换钥。

检查

cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo build

可使用独立随机 realm 名字检查所配置服务器的协议兼容性,该命令不修改网络:

./target/release/ip2psec --env-file .env realm-probe -c home.local.toml