Files
OxideJS-New/docs/server-security-boundary.md
T

115 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 持久 server 安全边界
本文档把持久 server 子系统(`crates/oxide_cli/src/server/`)的安全边界定稿为四个口径:
认证模型、绑定地址、token 生命周期、防滥用。整体姿态是双闸门:loopback 绑定加本地
文件权限,token 是连接认证的唯一凭据,不经网络分发。文中实现对照一律用函数名与
常量名,具体数值以代码为准。
## 认证模型
server 的认证门在连接首帧:任何连接必须先发 `Auth` 请求帧且其中 token 与 server 的
共享 token 匹配,server 才写回 `AuthOk` 并进入主循环;首帧非 `Auth` 或 token 不匹配
时直接断连、不回帧,不向攻击者泄露差异。门在 `handle_connection`(`server.rs`),
判等函数 `tokens_match`(同文件)逐字节异或累加,避免逐字节短路的时序侧信道;长度
不等直接判不等(生产 token 长度固定 64 位十六进制)。
token 由 `generate_token`(`sidecar.rs`)生成:读 `/dev/urandom` 32 字节转 64 位小写
十六进制(256 位熵);非 Linux 宿主回落时间加进程号混合熵(本宿主恒为 Linux,回落臂
仅保编译)。
token 分发有两条路径。持久 server 经 `write_exclusive` 把 token 写入 sidecar 文件
(写入后显式置 0600,仅属主可读写),控制客户端从 sidecar 读回 token 完成认证。
`--rm` 独立模式不写 sidecar,token 打印到前台标准输出,客户端经 CLI 旗标注入。
交接路径:新 server 接管时先读旧 sidecar 的 token,先发认证帧再发 yield 请求;旧
版本 server 无 token(空串)时跳过认证帧,保证升级路径可用(`yield_and_takeover`,
`server.rs`)。探活连接(只建连接不发帧)在认证门读首帧得 EOF 直接退出,不触发
任何错误路径。
## 绑定地址:loopback 与 0.0.0.0
缺省绑定地址是 loopback(127.0.0.1):`ServerConfig` 的 `bind_addr` 字段缺省取
`WELL_KNOWN_BIND_ADDR`,CLI 的 `--host` 旗标缺省 127.0.0.1、`--port` 旗标缺省取
well-known 端口 `WELL_KNOWN_PORT`(4242)。安全模型假设 loopback 加本地文件权限
(sidecar 0600)双闸门,token 本身不经网络分发。
`--host 0.0.0.0` 可以配置出 0.0.0.0 绑定,本文档明确定稿该姿态为不受支持:它把
eval 端点暴露到网络,token 需经带外安全渠道分发,不在支持范围内。理由是双闸门
(loopback 加文件权限)同时失效,防护责任转移到带外分发渠道,而该渠道由使用方
自备、本项目不提供。
遗留 Unix socket 臂的门控是 socket 文件权限(操作系统用户权限),缺省姿态同样是
只接受本机连接。
## token 生命周期
token 生命周期与 server 进程同长:启动时(认领 sidecar 之前)生成,写入 sidecar
(0600),有效期等于 server 生命周期,无轮换机制;关闭序列末尾删除 sidecar 文件
即 token 销毁。
交接时新 server 生成自己的新 token,旧 token 仅在 yield 握手阶段使用一次。`--rm`
模式启动时生成、打印到标准输出、随进程退出销毁,不写入任何文件。
## 防滥用口径
防滥用共四条口径,外加执行上限:
- 并发连接上限:`MAX_CONCURRENT_CONNECTIONS`(64,`server.rs`),`accept_loop` 派生
连接线程前查在途计数,超限连接立即关闭(不发协议帧,客户端表现为连接建立随即
被关)。
- 有界背压:每 worker 的任务通道为有界 `sync_channel`(`QUEUE_CAPACITY`,
`workers.rs`,容量 8),满则背压;路由在锁内克隆发送端、锁外投递,防满队列
持锁阻塞串行卡死。
- 帧大小上限:`MAX_FRAME_BYTES`(1 MiB,`protocol.rs`),超长帧按协议约定关连接。
- 读超时:持久 server 30 秒、`--rm` 模式取空闲超时(`RM_DEFAULT_IDLE_TIMEOUT`,
缺省 30 秒),半开连接不永久占线程。
执行上限:eval 请求带 `max_steps` 步数上限;控制请求不占 worker,不被长 eval 阻塞。
## 分片安全边界
远程分片(test262 runner 的 `--remote-supervise` 监督者与 `--worker` 执行半区)
的安全边界定稿为三个口径,与 server 子系统同型双闸门:loopback 绑定加 token
认证门。与 server 的差别在威胁模型与分发渠道:worker 执行不可信测试内容,
威胁对象是远程指派者而非本机邻居;token 不经网络分发,靠前台打印加带外注入。
### 缺省绑定地址
`--remote-supervise` 端点只允许 loopback 绑定:用户只给端口(裸端口或
`:port` 形态)时解析期补缺省 127.0.0.1;显式非 loopback 地址(0.0.0.0、
局域网 IP)在解析期报错拒绝,bind 处另有双保险拒绝非 loopback。该姿态比
server 的「0.0.0.0 可配置但不受支持」更严:server 的威胁模型是本机邻居,
worker 执行不可信测试内容,非 loopback 绑定会把窗口指派面(含本地路径
执行)暴露到网络,防护责任无法转给使用方。端点只接受 `[IP]:port` 形态,
主机名不解析(解析期无法验证 loopback)。
### worker 认证 token
监督者启动时生成 64 位十六进制 token(读 `/dev/urandom` 32 字节,非 Linux
回落时间加进程号混合熵)并打印到前台 stderr;worker 进程经
`OXIDE_TEST262_WORKER_TOKEN` 环境变量取得(带外注入,与 server `--rm`
模式同型:token 不经网络分发)。worker 连接后首帧必须是 `Auth` 事件帧,
监督者逐字节异或累加判等(长度不等直接判不等,避免逐字节短路的时序
侧信道),匹配才写回 `AuthOk` 命令帧并进窗口循环;不匹配或首帧非 `Auth`
直接断连、不回帧,不向攻击者泄露差异。读首帧带 30 秒读超时,半开连接
不永久占线程。
token 生命周期与监督者进程同长:启动时生成、进程退出即销毁,无轮换机制。
### worker 无特权口径
worker 是最小单向面:
- 只能发协议内事件帧(`Auth` / `Heartbeat` / `Fail` / `Scene` / `Done`),
未知类型在解析层拒绝,连接按死亡处理;
- 连接数受 worker 数上限约束(`--remote-supervise` 的 worker 数旗标),
每窗口一条连接;
- 不读监督者本机文件:窗口路径清单随 `Assign` 帧携带,worker 不读监督者
本机临时文件;
- 无文件系统写权限面:worker 不写监督者侧任何文件,失败记录与现场都经
帧回传。
## 扩展位
本文档定稿持久 server 现状与远程分片安全边界(见上「分片安全边界」一节)。