209 lines
24 KiB
Markdown
209 lines
24 KiB
Markdown
# 访问控制与入网信任链
|
||
|
||
这份文档讲谁能做什么(账户、能力、API key)和谁能让节点入网(passkey 到节点的信任根)。它是 [security.md](security.md) 的展开:security.md 讲缺口和威胁,这里讲现在怎么做。以已实现的为主,没做的在最后一节列出。
|
||
|
||
概要:登录和授权分开。登录证明「你是谁」,授权只看能力;改变信任(操作员集合)只认 passkey 的签名,并且这个签名由节点自己验证,Backend 只搬运,伪造不了。
|
||
|
||
## 1. 总览
|
||
|
||
```text
|
||
两个根,各管一摊
|
||
根 A:CI(代码) 发布公钥写在安装脚本、宿主机更新器的本机配置和 Backend 里
|
||
└─ CI 签名的发布清单 → 镜像 digest → 节点跑的二进制;安装脚本的 SHA-256 也记在清单里,并公布在发布页
|
||
假设:CI(Woodpecker、Gitea、镜像仓库、发布密钥)可信;CI 被攻破等于能给所有节点签恶意更新
|
||
根 B:你的 passkey(运维)
|
||
└─ 操作员集合(passkey 签,版本递增;创世指纹在安装时固定到节点)
|
||
├─ 操作员的 passkey 公钥
|
||
├─ 服务器密钥:Backend 的身份密钥(节点据此认服务器,链路用应用层加密)
|
||
└─ 域名(rp、origins)
|
||
|
||
添加节点:创建加入码不需要 passkey、不需要触碰,一个码可以装任意多台。命令里只有公开的值:
|
||
面板地址、角色、一次性码、创世指纹、属主(passkey 凭证 id)、安装脚本的哈希(命令先核对哈希再执行)
|
||
节点注册:向面板取操作员集合链,从命令里的创世指纹验起,属主必须是集合里的凭证;任何一步不对就不注册、不交出密钥;
|
||
通过后写 trust.json(创世指纹、链、属主),之后只认从这个创世出发的新版本
|
||
节点和 Backend 之间:集合里登记了服务器密钥之后,节点的认证挑战要求服务器用它签名;两边各出一把临时 ECDH 密钥,
|
||
认证签名覆盖双方的临时公钥,之后控制链路每一帧都是 AES-256-GCM(每个方向一把密钥、严格递增的计数器)。
|
||
所以 TLS 在 Cloudflare 或被冒充的证书那里终止,中间人没有服务器密钥,既冒充不了服务器,也读不了、改不了这条链路
|
||
密码登录和 passkey 登录的会话拿同样的能力,授权只看能力;passkey 只用在节点自己会验证的事上(现在是操作员集合的签名,以后是 extension 会话和加密数据)。
|
||
Backend 自己拦自己没有意义,所以没有提权窗口
|
||
```
|
||
|
||
没有采用「成员证明」(每个节点一条 Grant、Ticket、Binding 的入网链):被攻破的 Backend 往名册里塞节点,拿不到比 Backend 已经有的更多的东西(监测数据本来就是明文给 Backend 的;敏感的 extension 靠节点属主,不靠名册),成本却是每个节点一次触碰和一套证明格式。操作员集合和创世固定已经在,以后要叠加不用重装节点。
|
||
|
||
发布密钥可以换:见 [deployment.md](../ops/deployment.md) 的「换发布密钥」;回滚由清单的 `seq` 防住(更新器只收比已安装更大的)。面板信任哪些发布密钥(官方的,加上可选的自有公钥)在管理端「系统 → 信任 → 可信资产来源」。
|
||
|
||
## 2. 名词
|
||
|
||
| 名词 | 含义 |
|
||
| --- | --- |
|
||
| 账户 | 一个人,有用户名和密码,可以注册 passkey |
|
||
| 能力(capability) | 一项具体权限,如 `fleet.write`。端点声明自己需要哪项能力 |
|
||
| 会话 | 登录得到的状态,拿到全部能力(见第 3 节) |
|
||
| 操作员集合 | 节点固定的、带版本的 passkey 公钥列表,加上服务器密钥和域名,由操作员 passkey 签名 |
|
||
| API key | 一串随机令牌,只带创建时勾选的能力;明文只在创建时显示一次 |
|
||
| 属主(controllers) | 一个节点绑定的一组 passkey:只有它们能授权这个节点上的 extension(web ssh 等)。管理员没有这份权力,见第 9 节 |
|
||
|
||
## 3. 能力
|
||
|
||
| 组 | 能力 | 说明 | 信任类 |
|
||
| --- | --- | --- | --- |
|
||
| 展示 | `public.read` | 看隐藏线路;站点私有时看展示页 | |
|
||
| 节点 | `fleet.read` | 看 Agent、Probe、线路、加入代码 | |
|
||
| | `fleet.write` | 改名、标签、可见性、手填地址、线路开关、断开会话 | |
|
||
| | `fleet.enroll` | 生成加入代码 | |
|
||
| | `fleet.revoke` | 吊销或删除节点 | 是 |
|
||
| 任务 | `tasks.read` · `tasks.write` | 任务模板、覆盖、发起测试 | |
|
||
| 数据 | `data.read` · `data.write` | 原始块;清理 | |
|
||
| 设置 | `settings.read` · `settings.write` | 站点设置、保留策略、重启计划 | |
|
||
| 更新 | `update.read` · `update.confirm` | 看 Backend 更新和发布清单、确认更新、管理可信的发布密钥 | |
|
||
| 令牌 | `keys.read` · `keys.manage` | 列出、创建、撤销 API key | |
|
||
| 账户 | `accounts.read` · `accounts.manage` | 账户 | 管理是 |
|
||
| 运维 | `events.read` · `audit.read` · `ops.metrics` | 事件;审计日志;`/metrics` | |
|
||
| 集成(预留) | `data.export` · `webhooks.read` · `webhooks.manage` | 批量拉数据、外部回调订阅,见第 6.4 节 | |
|
||
|
||
- 一个能力只在一个地方声明:`Capability` 常量(`MoShiro.Backend.Application`),端点用 `.Requires(Capability.X)`;`Principal.Has(capability)` 是唯一的判断入口,会话和 API key 都走它。写蕴含读(`fleet.write` 带 `fleet.read`)。
|
||
- 对账测试:所有 `/api/admin` 端点必须声明能力,没声明测试就失败;OpenAPI 的每个操作带 `x-moshiro-capability`。
|
||
- 信任类是界面上特别标出的(吊销、账户管理),授权和其他能力一样靠 API key 或会话。真正由 passkey 签名保护的是操作员集合,那是节点自己验证的。
|
||
- 不能越权:创建 API key 时勾选的能力必须是创建者当前拥有能力的子集。
|
||
- Backend 更新确认:更新清单由 CI 的发布密钥签名(面板登记时验签),`seq` 防重放;确认只是让 Backend 有序退出并备份数据库,拉哪个镜像由 Quadlet 单元里写死的仓库和标签决定,面板决定不了。
|
||
|
||
## 4. 登录与会话
|
||
|
||
| 登录方式 | 说明 |
|
||
| --- | --- |
|
||
| 密码 | PBKDF2;认领向导创建第一个管理员时设置 |
|
||
| passkey | WebAuthn,只支持 ES256,attestation 一律当 `none`;可以登录,也能给操作员集合签名(节点验证) |
|
||
|
||
- 两种方式的会话拿同样的能力。passkey 不是 Backend 用来拦操作的一道门,只用在节点自己会验证的事上,见第 5 节。
|
||
- 认领向导(没有默认账户):面板里还没有任何用户时,打开 `/admin/` 进向导——用后端日志里的认领码(或 `MoShiro__ClaimCode`)创建第一个管理员(用户名和密码自己填),注册第一个 passkey,再用它给操作员集合签名建立信任根。谁先打开页面谁就能当管理员,所以要认领码;创建完码作废、入口关闭(`POST /api/admin/auth/setup` 之后返回 409)。后两步是引导不是门槛,可以稍后补;不注册 passkey、不建立信任根,就添加不了 Agent 和 Probe,只能进面板做基础管理,这是架构如此。之后新增的 passkey 不会自动成为操作员:要由现有操作员在信任页看清「新增了哪些」再签新版本,节点才认。
|
||
- 建议每个账户至少注册两个 passkey(手机加硬件密钥)。
|
||
|
||
### 4.1 passkey 登录的实现
|
||
|
||
- 流程:登录页「使用 Passkey 登录」→ `POST /api/admin/auth/passkey/options` 取挑战 → 浏览器 `navigator.credentials.get`(可发现凭证,不用输用户名)→ `POST /api/admin/auth/passkey/login` 提交断言 → 建立会话(Cookie,或跨域前端用 `return_session`)。注册:已登录用户在「系统 → 用户 → 我的 Passkey」添加,`POST /api/admin/auth/passkeys/options` 加 `POST /api/admin/auth/passkeys`;每个用户最多 10 个。
|
||
- 校验:挑战在内存里,一次有效、5 分钟过期,并且区分注册和登录、绑定用户;`rpIdHash`、UP 和 UV 标志、`clientDataJSON` 的 type、challenge、origin、ES256 签名;签名计数器只增不减(两边都是 0 的同步型 passkey 不检查)。登录失败一律 401,不区分原因,和密码登录共用限流。
|
||
- 验证断言的纯函数是 `Fleet.Identity.WebAuthnAssertion`(只用 BCL,节点验证操作员集合也用它);注册里的 CBOR 和 attestation 解析在 Backend(`WebAuthnRegistration`),节点不带 CBOR。
|
||
- 依赖方(RP):`MoShiro__WebAuthnRpId`(缺省取 `PublicUrl` 的主机,没有就取请求的主机)和 `MoShiro__WebAuthnOrigins`(缺省取 `PublicUrl` 的来源)。passkey 绑定在这个域名上,改了会让已注册的 passkey 失效。只有和后端同站的页面能用 passkey 登录,管理端在「添加其他后端」(跨域)时不显示这个入口。
|
||
- 会话记录登录方式(`password` 或 `passkey`),只用于审计和显示;API key 不是用户,不能注册 passkey。
|
||
- 测试:`SoftwarePasskey` 在测试里模拟认证器走完整的注册和登录,覆盖错误的挑战、来源、RP、签名、缺少 UV、挑战重放、计数器回退、只能管理自己的 passkey。
|
||
|
||
## 5. passkey 用在哪里(没有提权窗口)
|
||
|
||
原则:passkey 不是 Backend 用来拦操作的一道门(Backend 被攻破时,它自己做的拦截也会被一起绕过),而是节点或密钥持有者需要它才能验证、解密的东西。
|
||
|
||
| 事 | 谁验证 | 要 passkey |
|
||
| --- | --- | --- |
|
||
| 建立、更新操作员集合(信任根) | 节点验链 | 是(现在唯一做了的) |
|
||
| extension 会话(web ssh 等) | 节点验 passkey 签名的临时密钥 | 是(还没做) |
|
||
| 读加密存储的数据 | 私钥持有者 | 是(还没做) |
|
||
| 吊销或删除节点、账号、API key、加入码、设置 | 只有 Backend | 否,只看能力 |
|
||
|
||
- 这些「只有 Backend」的事靠能力限权:API key 只带需要的能力。密码泄露的后果是这些能力被滥用,改不了信任根,也用不了以后的 extension。
|
||
- 唯一的风险是往账号里塞一把 passkey 再等人签名:信任页的提案会列出新增和移除的凭证,签名之前要看一眼是不是自己加的;签了才算授权。
|
||
|
||
## 6. API key
|
||
|
||
- 创建时勾选能力,只能是创建者当前拥有能力的子集;可设有效期。明文只在创建时显示一次,列表显示能力数、最后使用时间、过期时间。预设:只读、运维、接入(`enroll`)、全部。
|
||
- 创建界面的能力用分组复选框:全选(带半选状态)、组内全选、反选、搜索;预设按钮一键套用。
|
||
- 列表(API key、加入代码、节点、审计日志)都支持多选和全选做批量操作。
|
||
|
||
### 6.1 `enroll` key:自动化添加节点
|
||
|
||
它就是一把带 `fleet.enroll` 的普通 API key,用 `POST /api/admin/join-tokens` 创建加入码,响应里直接给出安装命令(带创世指纹、属主和脚本哈希,没有任何私钥)。创建码不是信任动作,所以不需要 passkey;CI 和脚本照常工作。信任在两处:命令里的创世指纹和属主由面板生成,节点注册时固定;以后改属主和集合要操作员 passkey 签名。
|
||
|
||
### 6.2 CI 登记发布清单不需要令牌
|
||
|
||
CI 把签名清单 POST 给 `POST /api/release/offer`,面板用发布公钥验签,没有令牌,所以清库、重装、还没有 passkey 时照常工作,也没有长期令牌可偷。旧清单重放由 `seq` 递增挡住。
|
||
|
||
### 6.3 预留:外部 webhook 回调与数据拉取
|
||
|
||
后面要让外部系统能订阅事件、触发回调、拉数据。接口和权限先留(`data.export`、`webhooks.*` 标预留),实现放后面;全部发生在 Backend 里,节点零开销。订阅只能订阅创建者自己读得到的事件;投递是带 HMAC 签名的 `POST`,至少一次、指数退避;目标 URL 要做 SSRF 限制(只放行公网地址,不跟随重定向到内网,有超时和大小上限)。这些都属于 Backend 的集成配置,不改变节点信任,但每次增删订阅都进审计。
|
||
|
||
## 7. 归属与审计
|
||
|
||
- 加入代码记 `created_by`,节点记 `added_by`;管理页的 Agent、Probe 详情显示「由 xxx 于 xxx 添加」。
|
||
- 审计日志(`GET /api/admin/audit`):所有非 GET 的管理操作和登录自动记录(时间、账户、登录方式、动作、对象、详情)。写不进去只记日志,不让已完成的操作报错。保留 365 天,只追加。
|
||
|
||
## 8. 入网信任链的实现要点
|
||
|
||
- 语句是 `key=value` 文本,第一行是带版本的标题,签名覆盖整段文本;WebAuthn 的挑战是语句的 SHA-256。库在 `Fleet.Identity` 的 `OperatorTrust`(操作员集合的编码、解析、链验证)和 `SecureChannel`(服务器证明和链路加密),都是只用 BCL 的纯函数,节点和 Backend 共用。
|
||
- 创世:第一次建立信任根时,把账号下所有 passkey 和这个面板的服务器密钥登记成一个操作员集合,passkey 对它签名;创世语句的 SHA-256 就是命令里的 `MOSHIRO_GENESIS`。之后加删 passkey 或换服务器密钥要签新版本(上一版里的凭证签),页面会提示;签名更新不会让节点重装。
|
||
- 操作员集合版本随期望状态(`trust_chain`)下发,节点从固定的创世验起,版本比现有的新才采用并保存。
|
||
- 节点注册:`fleet-enroll --genesis … --controllers …`:先 `POST /api/agent/v1/enroll/trust` 取链,验过(创世指纹、签名、属主在集合里)才把密钥交出去;之后写 `trust.json`。没有命令里的指纹(`--genesis`)就不固定,节点按旧方式工作。
|
||
- 服务器身份和安全链路:`ChallengeParams` 带服务器的临时公钥和签名(`server_eph`、`server_sig`),`AuthParams` 带节点的临时公钥(`node_eph`),节点签名的用途是 `auth2`,覆盖双方的临时公钥;通过后两边用 HKDF 派生每个方向一把 AES-256-GCM 密钥,每帧 `计数器(8) ‖ 密文 ‖ 标签(16)`,计数器严格递增,明文帧、改动、重放、乱序一律断开。集合里没有服务器密钥的节点(旧节点)保持明文,不会被降级成放行。
|
||
- 服务器密钥存在 Backend 的设置里(`server.identity_key`):它防的是面板被冒充,不是面板被攻破;面板被攻破时它也泄露,那时要由 passkey 签一个换掉它的新集合版本。集合里没有这个面板的服务器密钥的版本,Backend 不收(否则固定了它的节点就认不出这个面板了)。
|
||
- 弱点:创世指纹放在安装命令里,命令本身要从可信渠道拿到(管理端复制)。缓解见第 10 节(静态签名脚本、按 digest 拉镜像),并在信任页展示指纹供核对。
|
||
|
||
## 9. 节点属主与 extension 授权
|
||
|
||
extension(web ssh、文件、抓包,见 [security.md](security.md))现在都不实现,Agent 和 Probe 一样。这类能力连管理员都不信任:管理员能删,不能读,也不能用。所以入网时就给每个节点固定「属主」,之后不用重装节点。
|
||
|
||
已经实现的部分
|
||
|
||
- 属主 `controllers` 是一组 passkey 凭证 id,必须是操作员集合里的凭证。由安装命令带来,节点注册时写进 `trust.json`,Backend 之后改不了。
|
||
- 每个 Agent 和 Probe 都必须有属主,至少一把——而且这是节点自己的要求,不是 Backend 的要求:节点启动时检查本地固定的属主,没有(或者属主的 passkey 已经不在操作员集合里)就不运行(共用 `Fleet.Hosting` 里的 `OwnerGuard`;配置 `fleet.require_owner`,`MOSHIRO_ALLOW_NO_OWNER=1` 放行)。面板被攻破也改不了。
|
||
- 添加节点时选属主:`POST /api/admin/join-tokens` 的 `controllers: [credential_id…]`,不给就是操作员集合里的全部凭证;给了就必须非空且都在集合里。还没有信任根或一把属主都没有时,面板提前拒绝创建加入码(400,字段 `controllers`),这只是尽早失败,真正的强制在节点;`MoShiro__RequireOwner=false` 可以关掉这个提前检查(只给没有信任根的老部署)。界面里是必选的「属主(passkey)」多选列表,只是选,不用触碰。
|
||
- 安装端也强制:`install.sh`、`install.ps1`、容器入口缺了 `MOSHIRO_GENESIS` 或 `MOSHIRO_CONTROLLERS` 就不装;`fleet-enroll --genesis` 不带 `--controllers` 也拒绝。
|
||
- 逆推出绑定关系:节点握手时把本地固定的属主(passkey 凭证 id)报给面板(hello 的 `owners`),面板顺着凭证 id 反查出它绑定在哪个账户的哪把 passkey 上,显示在「系统 → 版本」的「属主」列(对不上任何已注册 passkey 的显示「未知凭证」)。上报只是自述,真正的授权由节点自己校验 passkey 签名。
|
||
|
||
预留的设计(还没做)
|
||
|
||
- 谁来强制:节点本地,不是 Backend。 web ssh 这类会话要不要加密、要不要验证,由 Agent 和 Probe 自己要求。让 Backend 决定,被攻破的 Backend 一句「这次不用加密」就绕过去了。节点本地强制:没有操作员的现场证明不启动会话;会话密钥只和证明里的那把密钥协商,不和 Backend 转来的公钥协商;加密字段缺失就拒绝,不降级;能开哪些 extension、哪些 passkey 能授权,只看本节点属主。
|
||
- 证明的方向是反的:节点先出题(nonce 加它的临时公钥,用节点身份密钥签名),属主的浏览器把 nonce 和自己的临时公钥放进 passkey 断言的挑战里;节点验断言来自本节点属主、要求用户验证、nonce 是自己刚出的且没用过。Backend 仍然管谁有资格发起请求、审计、限流、转发密文。
|
||
- 交互式会话(终端、ssh)用现场签名的临时 ECDH 密钥协商,passkey 只签名授权,不参与解密,所以不支持 PRF 的 passkey 也能用,还有前向保密;之后双向是带序号的 AEAD。静态数据(抓包文件、会话录像)需要事后解密,才用属主记录里的接收者公钥加密(来自 WebAuthn PRF 或浏览器里不可导出的密钥)。
|
||
- 删除和使用分开:admin 能删节点、卸载 extension,读不到也用不了。改属主要现有属主的 passkey 签名(`ControllerUpdate`),账户被删、角色被改都改不了某个节点的属主。属主是 passkey 凭证,不是账户;一个人丢了全部属主 passkey,这个节点上的 extension 就没人能用了,所以添加时建议绑两把以上。
|
||
- 数据边界:监测数据(延迟、丢包、吞吐、事件、主机资源汇总)是明文,Backend 要做分析和展示;extension 会话和原始 payload 端到端加密,Backend 只存转发密文。读取权不是能力,是私钥。
|
||
|
||
## 10. 安装的供应链
|
||
|
||
一行安装要执行 Backend 吐出的脚本、拉仓库里的镜像。Backend 或仓库被攻破,就能在新节点上执行任意命令。收束办法,优先级最高的是第一跳的哈希核对:
|
||
|
||
① 脚本是静态产物,哈希固定
|
||
|
||
- 不在服务端做字符串替换。CI 构建时算出 `install.sh`、`install.ps1` 的 SHA-256,写进发布清单(发布密钥签名),同时公布在 Gitea 发布页——这条渠道独立于 Backend,这是哈希核对有意义的前提:哈希若和脚本从同一处来,攻击者两个一起换就行。
|
||
- 参数(代码、面板地址)通过命令行环境变量传入,代码因此不出现在 URL 和反向代理日志里。
|
||
- 管理端给的命令把「下载、核对、执行」合成一行,哈希核对不通过就不执行:
|
||
|
||
```text
|
||
f=$(mktemp) && curl -fsSL https://panel/install -o "$f" && echo "<sha256> $f" | sha256sum -c - && MOSHIRO_SERVER=… MOSHIRO_ROLE=… MOSHIRO_CODE=… sh "$f"
|
||
```
|
||
|
||
macOS 用 `shasum -a 256 -c`,Windows 用 `Get-FileHash` 比较,不通过就 `throw`。命令里的 `<sha256>` 由 Backend 填入,信任页同时标出它与 CI 签名的发布清单是否一致;严格的人对照 Gitea 发布页一次即可(脚本是静态的,哈希在一个版本内不变)。
|
||
|
||
② 镜像按 digest 拉取
|
||
|
||
- 脚本向面板取已签名的清单(和更新用的是同一份,发布密钥签名、`seq` 防回滚),用脚本里内置的发布公钥验签,再按清单里的 digest 拉镜像(`repo@sha256:…`)。Backend 改不了 digest,最多让节点装到一个旧的合法版本。
|
||
- 下载压缩包安装的人,用 Release 里签名的 `SHA256SUMS` 核对(见 [deployment.md](../ops/deployment.md))。
|
||
|
||
③ 容器安装:Podman 系用 Quadlet(默认),Docker 用 compose
|
||
|
||
- 管理端的容器安装是:建目录,把参数写进本地 `.env`(面板地址、一次性码、创世指纹、属主,都是公开的值),下载面板提供的静态 compose 文件、核对 SHA-256,`compose up -d`。compose 文件是静态的(和安装脚本一样嵌在 Backend 里,也作为 Release 资产发布),镜像是 `:latest` 或 `.env` 里固定的版本,镜像地址由 CI 渠道决定,不由面板决定,面板被攻破也改不了;Backend 第一次部署时没有面板,就从 Release 资产下载。
|
||
- 代价:客户端不再验发布签名,更新(`compose pull && compose up -d`)靠 TLS 加「注册表可信」这个假设;想要更严,在 `.env` 里把 `MOSHIRO_VERSION` 固定到 digest,或者用宿主机更新器(清单有发布签名,不健康自动回滚)。详见 [deployment.md](../ops/deployment.md)。
|
||
|
||
④ 其余
|
||
|
||
- 角色(Agent 还是 Probe)、镜像仓库地址仍由代码决定,但镜像身份由签名清单决定。
|
||
- macOS 的 Developer ID 签名和公证、Windows 的 Authenticode 需要证书,目前没有做。
|
||
|
||
## 11. 运行中的二进制不做校验
|
||
|
||
曾经做过「节点二进制绑定」:节点握手时自报正在运行的二进制的 SHA-256,Backend 在第一次握手时绑定,之后对不上就标红或拒绝。已经移除。
|
||
|
||
原因:哈希和清单都是节点自己报的。一个被换进来、故意撒谎的二进制可以照抄原来的哈希和清单,Backend 分辨不出——上报者就是被怀疑的对象。这一层只能抓到「文件被换了、但程序本身没撒谎」的情形,却让人误以为有保障,还要求运维宿主机更新器、状态卷和每个节点的清单。得不偿失。
|
||
|
||
「这个资产是我们的」靠安装时的来源证明,那里的哈希来自另一个信道,伪造不了:
|
||
|
||
- 安装脚本(SHA-256 记在签名的清单里,并公布在发布页),按清单里的 digest 拉镜像(`repo@sha256:…`);
|
||
- 下载压缩包的人用签名的 `SHA256SUMS` 核对(验签用 `packaging/update/release-key.pub.pem`);
|
||
- 节点的更新由宿主机更新器用本机公钥验签,只认本机配置的镜像仓库。
|
||
|
||
要让运行中的二进制真的无法伪造,必须有容器之外的东西背书:宿主机更新器持有一把节点进程读不到的密钥,对「当前运行的镜像 digest + Backend 的挑战」签名,由节点转发;容器里的进程被换掉也伪造不了,除非攻破宿主机。这一步没有做(一键容器安装的节点也没有宿主机更新器)。
|
||
|
||
## 12. 还没有的与局限
|
||
|
||
- 还没有:吊销表;任务和配置内容的操作员签名;节点向别人证明自己运行的就是官方二进制(见第 11 节);extension 本身、属主的 `ControllerUpdate`、接收者的实际加密(属主只是被固定在节点本地,没有行为);API key 的 IP 白名单(本部署经 Cloudflare、frp 和 Caddy,后端看到的来源地址是代理,需要先有「可信代理」配置才做得对);OIDC 登录。
|
||
- WebAuthn 只验 ES256 和 attestation = `none`,不验设备证明。
|
||
- 某个节点的属主 passkey 全部丢失,这个节点上的 extension 就没人能授权,需要重装节点并换属主。
|
||
- 删除一把操作员 passkey 只是让它不能再签新版本;服务器密钥换新要靠签一个新集合版本,忘了签会让固定了集合的节点认不出面板(fail closed)。
|
||
- `enroll` key 是持有者凭证,谁拿到令牌,谁就能在它的有效期内创建加入码,必须按密码对待。
|