Forward exact session proofs for configured offline backends

This commit is contained in:
layue13 committed 2026-09-28 02:09:54 +08:00
1 parent 672914cdbe
commit 63f3f97038
6 files changed
+196 -21

No files matched your search

+2
View File
@@ -102,6 +102,8 @@ Proxy 为每个插件管理独立的 scope,在停用时自动撤销。项目
代理在 `backendChannel.listen` 启动独立监听器。配置示例见 [moonbridge-channel.example.yml](../proxy-core/src/main/resources/config/moonbridge-channel.example.yml)。每个实例的 `secret` 至少 32 个 UTF-8 字节,`allowedHosts` 限制其广告游戏地址;`allowedNamespaces` 控制发送,`allowedReceiveNamespaces` 控制接收,未配置接收列表时继承发送列表,显式空列表表示禁止该方向的所有消息。跨后端路由同时检查源发送权限与目的接收权限。Channel registration v3 将代理启动 epoch 和 backend registration epoch 同时交给后端。代理用该 backend client secret 为发往该 backendName 的 legacy forwarding handshake 生成短时 HMAC 会话证明;证明绑定两端当前 epoch、backendName、UUID、connectionId、一次性 nonce 和到期时间。后端 `MoonBridgeBackend` 校验它并把会话绑定到实际 Bukkit Player 登录对象。两端身份或 secret 不匹配、registration 未完成或 epoch 已变化时,`BukkitSessionService.find(player)` 返回空值。证明不代表任何 AetherShard 操作。
在 `OFFLINE` 代理模式中,只有显式配置了该 backend 的 session-binding secret 时,代理才会在初始连接或转服时转发由代理派生的 offline UUID 和会话证明;这证明代理连接身份,不表示 Mojang 已认证。没有配置 secret 的 offline backend 仍接收原始握手。静态 backend 或尚未获得正数 registration epoch 的目标不会收到会话证明。
实例的游戏地址必须是代理实际可达的 `tcp://host:port`。Docker 中使用容器服务名,容器间不要使用 `127.0.0.1`。仅将玩家端口公开,控制端口放在私有网络。不要把真实凭据写入镜像或提交到仓库。
HMAC 挑战认证本身不加密链路;跨机器使用私有加密链路或可信 TLS 终止器。每个实例配置独立的实例 ID、后端名称和密钥;重复认证同一实例会接管旧连接,旧连接的迟到回复不能影响新连接。
+1 -1
View File
@@ -4,7 +4,7 @@
## 生命周期和线程
`onLoad(PluginContext)` 获得 `players()`、`servers()`、`commands()`、`permissions()`、`events()`、`messaging()`、专属 `dataDirectory()`、SLF4J `logger()` 、不可变 `settings()` 和本代理进程的 `proxyEpoch()`(每次启动重新生成,与后端会话绑定证明一致)。宿主在所有插件加载后调用 `onEnable()`;插件全部启用成功后、监听器启动前冻结事件订阅并确定唯一权限提供者。事件订阅只能在 `onLoad` 或 `onEnable` 注册,运行期间可以随时撤销。初始化失败会终止启动并回收已创建的注册。
`onLoad(PluginContext)` 获得 `players()`、`servers()`、`commands()`、`permissions()`、`events()`、`messaging()`、专属 `dataDirectory()`、SLF4J `logger()`、不可变 `settings()` 和本代理进程的 `proxyEpoch()`(每次启动重新生成,与后端会话绑定证明一致)。宿主在所有插件加载后调用 `onEnable()`;插件全部启用成功后、监听器启动前冻结事件订阅并确定唯一权限提供者。事件订阅只能在 `onLoad` 或 `onEnable` 注册,运行期间可以随时撤销。初始化失败会终止启动并回收已创建的注册。
事件通过 `PluginContext.events()` 提供的 `dev.moonbridge.api.event.Events` 订阅。所有事件都使用同一 sealed `Event<R>`、`EventListener<E, R>` 和 `Events.subscribe` 方法;监听器返回 `CompletionStage<R>`。无结果事件使用 `Event<Void>`,准入事件返回 `AccessDecision`。数据库客户端、缓存和外部任务的生命周期由插件管理;返回 `CompletionStage` 表达异步结果,避免在回调里 `join()` 等待另一个任务。各连接的准入检查可以并发,插件共享数据必须支持并发访问。