身份、配置与连接
本页说明 ctrl、node 和客户端共同遵守的身份、证书与配置规则。它回答「这个配置项是什么意思」「证书从哪来、多久过期」这类问题。
- 首次部署的操作步骤见安装概览。
kibo的参数与退出码见 kibo CLI 参考。
配置事实源
ctrl 与 node 只读取严格的 TOML:出现未知字段直接报错。完整字段、示例值与约束以
ctrl.example.toml 和 node.example.toml 为准。示例中的值只是示例,不是所有字段的默认值。每个表与键及示例中的注释见 ctrl 配置与 node 配置。
| 配置项 | 说明 |
|---|---|
ctrl [identity].trust_domain | Operator 与 node 身份所属的信任域,小写域名形式。初始化后不能修改 |
ctrl [identity].host | ctrl 两个监听地址共用的主机名或 IP。客户端连接时的主机名必须与它一致;不接受通配符 |
ctrl [api].listen | Workspace API 与 Operator API 的 mTLS 监听地址 |
ctrl [node_api].listen | node 注册、证书签发和 node 主动连接 ctrl 的监听地址 |
ctrl [data_plane].host_suffix | 必填。每个 node 的数据面证书会包含 <node_id>.<host_suffix> |
ctrl [data_plane].default_extra_hosts | 可选。加入所有 node 数据面证书的额外主机名或 IP |
ctrl [storage].root | ctrl 自己的 PKI 与凭证加密密钥所在目录。init 在这里创建 PKI,serve 从这里读取;serve 发现没有 credential-keys/ 下的密钥时生成一个,权限 0600,只属于服务账号 |
node [ctrl].endpoint / proxy | ctrl node API 的 HTTPS 地址,以及可选的正向代理 |
node [data_plane].listen / advertise | 数据面在本机的监听地址,以及 ctrl 告诉客户端使用的对外地址 |
node [storage].root | node 的身份、持久状态、Template 缓存与 VM runtime 的根目录 |
node [[storage.pool]] | 至少声明一个存储池:一个目录,装着 Volume 的块文件、保留的可写层与一个标记文件。池的 path 可以等于 storage.root,也可以在别处(例如 NFS 挂载)。池的容量不在这里声明,见池容量 |
node [runtime].socket_root | node 的 Unix socket 目录。node 不会再在它下面追加 node ID,路径要足够短 |
ctrl [metrics] | 可选。实例指标存储的 remote write 地址、签发写入凭证的私钥文件与 CA,以及 instance_info_labels。没有这一段时不写任何实例指标。node 从 ctrl 取得写入地址与凭证,node 的配置里写了这一段会拒绝启动。见实例指标存储 |
ctrl [traces] | 可选。trace 存储的 OTLP/HTTP 地址、签发 trace token 的私钥文件与 CA,以及 node 数据面的采样比例。没有这一段时不产生 span。node 从 ctrl 取得地址与凭证,node 的配置里写了这一段会拒绝启动。见链路追踪 |
ctrl 与 node [telemetry].listen | 可选。服务自身指标的监听地址,明文 HTTP、不认证,只提供 GET /metrics。不写时不开监听。与 [metrics] 无关,见集群与服务指标 |
- ctrl 的默认配置路径是
/etc/kibotos/ctrl/config.toml,node 是/etc/kibotos/node/config.toml。用--config指定其他绝对路径。 - 证书、CA 和签名私钥保存在受 ctrl 或 node 管理的目录里,不写在 TOML 中。
- node 的 ID 来自注册时得到的客户端证书,不是配置项。
- node 的
[resources]声明这台宿主能给 VM 的 CPU 与内存,即物理量减去宿主自己要留的部分。它不能超过宿主的在线 CPU 数与MemTotal,否则 node 拒绝启动。超卖不写在这里,由 Operator 用kibo node overcommit设置倍数,见超卖倍数。 - Template 缓存上限由
[storage].template_cache_limit声明。缓存需要空间时,按最近使用的先后淘汰运行中的 Instance 没有用到的内容,包括传输中断留下的部分文件。调低上限后,node 启动时淘汰到上限以内;运行中的 Instance 用到的内容保留,淘汰不够时 node 照常启动,新的 Template 准备因容量不足失败,直到这些 Instance 停止。Volume 与保留的可写层的存储容量是每个池在 ctrl 上的容量,用kibo storage-pool set-capacity设置。node 不会自动探测机器容量。 - 不需要 guest 网络时省略
[host_network]。public-internet策略所需的双栈接口、路由和 DNS 配置见 node 配置示例。
身份与注册
ctrl 的初始化顺序是 kibotos-ctrl init → migrate → 本机 enrollment create → serve。
init 不联网、不连接数据库、不监听端口。它创建四样东西:签发客户端证书的 CA、ctrl 服务端 CA、数据面会话凭证的签名密钥、ctrl 服务端私钥。
状态完整时重复执行返回 unchanged;状态不完整或不匹配时失败,不会自动修补。
| 操作 | 执行者 | 结果 |
|---|---|---|
kibotos-ctrl enrollment create --operator <id> --output <path> | ctrl 宿主机管理员(能访问 ctrl 配置、PKI 和数据库) | 为第一位 Operator 签发注册密钥;所有 Operator 身份丢失时也用它恢复 |
kibo enrollment create --operator <id> --output <path> | 已注册的 Operator | 为另一位 Operator 签发一次性注册密钥 |
kibo enrollment create --node <id> --output <path> | 已注册的 Operator | 为指定 ID 的 node 签发一次性注册密钥,可附带额外的数据面主机名 |
kibo context enroll | Operator | 用注册密钥换取 Operator 身份,连同 ctrl 地址保存到 context 目录 |
kibotos-node join --config ... | node 宿主机管理员 | 用注册密钥换取 node 身份,写入 <storage.root>/pki/。不启动 node |
身份规则
- 正式身份的 URI 是
spiffe://<trust-domain>/operator/<id>或spiffe://<trust-domain>/node/<id>。 - node ID 必须是小写域名形式,因为它会成为数据面证书主机名的一部分。
- 是否有权访问由 ctrl 中的注册记录决定。当前已注册的 Operator 拥有集群管理权限,可以调用 Workspace API 与 Operator API。仅信任 ctrl 的服务端证书(例如浏览器)不能获得任何权限。
注册密钥的规则
- 默认有效 1 小时,
--ttl可在 1 分钟到 24 小时之间调整。 - 密钥里包含一把引导私钥和 ctrl 服务端 CA,要当作凭证保管:不放进命令行参数、环境变量、日志或版本库。
- 注册成功后删除密钥文件。
kibo context enroll 与 kibotos-node join 读取密钥的方式相同:
- 不给密钥参数时,在终端无回显地询问。标准输入不是终端时,必须给
--key-stdin、--key-fd <n>(n不小于 3)、--key-file <path>三者之一,且只能给一个。 - 密钥不能通过命令行参数或环境变量传入。
- 密钥必须是一个 token,不超过 16 KiB。
--key-file接受相对路径、绝对路径,以及指向普通文件的符号链接。FIFO、目录和其他特殊文件会被拒绝。- 密钥文件对 group 或 other 可读时,命令打印警告,仍然读取,不修改文件。密钥文件的权限应设为
0600。 - 注册写入的身份目录与私钥文件严格检查权限。
kibo context enroll 与 kibotos-node join 都不会覆盖已有的身份目录。目录中的身份过期或损坏时,先停止使用它的进程、删除目录,再用新的注册密钥注册。
ctrl 宿主机上的 kibotos-ctrl enrollment create 会作废该 Operator 尚未使用的旧密钥。kibo enrollment create 的重放见幂等重放。
服务端证书与数据面证书
ctrl 的服务端证书
ctrl 在 init 时创建服务端 CA,serve 用它为 [identity].host 签发服务端证书。每把注册密钥都携带这个 CA 的证书:
kibo context enroll 与 kibotos-node join 先用它验证 ctrl 的主机名,再发送引导身份;成功后把 CA 保存为身份目录中的 server-ca.pem。
之后访问 ctrl 的两个 mTLS 端口都只信任这个 CA,不读取系统根证书,也不需要公共 DNS、ACME 或修改系统信任库。
node 的数据面证书
数据面私钥由 node 在本机生成,ctrl 根据 node 的注册身份签发证书。证书包含的主机名来自三处,去重后使用:
- ctrl 派生的
<node_id>.<host_suffix>。 - ctrl 配置的
[data_plane].default_extra_hosts。 - 签发该 node 注册密钥时传入的
--data-plane-extra-host。
规则:
- 额外的主机名可以是完整域名或 IP,不接受通配符。任何一个都不能与 ctrl 自己的
[identity].host相同。 - node 配置中
[data_plane].advertise的主机名必须在证书中。 - 派生的主机名不需要真的在 DNS 中注册。只有客户端用它作为连接地址时,才需要能解析。
例如 host_suffix = "nodes.kibotos.trial"、node ID 为 node-a,并在签发密钥时加上 --data-plane-extra-host 127.0.0.1,
证书就同时覆盖 node-a.nodes.kibotos.trial 和 127.0.0.1,单机上的客户端可以直接连 https://127.0.0.1:<port>。
node 把证书保存在 <storage.root>/pki/data-plane/ 下的 leaf.key、leaf.pem 与 manifest.json,启动时校验并复用,需要时重新签发。
修改主机名集合:
- 要修改某个 node 的额外主机名,需要撤销该 node,用新的主机名重新签发注册密钥,删除旧的身份目录后重新
join。 - 修改 ctrl 的
host_suffix或default_extra_hosts只影响之后签发的证书,需要与 node 的advertise配置和证书更新一起安排。
数据面客户端如何验证
kibo 与 SDK 等数据面客户端使用与控制面不同的 TLS 配置:信任系统根证书加上 server-ca.pem,按标准规则校验主机名。
因此客户端既能直接连 node,也能连部署方放在 node 前面的公网 TLS 反向代理。反向代理必须在部署方的信任范围内,并正确转发流式请求与长连接。
这种公网 TLS 终止只适用于数据面。ctrl 的 mTLS 端口始终要求注册身份和 ctrl 自己的服务端 CA。
客户端连接
context 目录
kibo context enroll 把 ctrl 地址、Operator 身份、信任的 CA 以及可选的正向代理保存在一个目录里,这个目录称为 context。之后的每条 kibo 命令都读取它。
- 默认位置是
$XDG_CONFIG_HOME/kibo/context;未设置XDG_CONFIG_HOME时为~/.config/kibo/context。环境变量KIBO_CONTEXT_DIR可以指定另一个绝对路径。 - 目录中有
context.json、当前的identity-<serial>.pem与identity-<serial>.key、control-client-ca.pem和server-ca.pem。 - context 目录已存在时,
enroll拒绝覆盖。需要重新注册时先删除旧目录。 - 全局参数
--endpoint或环境变量KIBO_ENDPOINT只改变本次请求的地址,身份与信任的 CA 仍来自 context。
正向代理
kibo context enroll --proxy 与 node 的 [ctrl].proxy 接受 http://、https://(CONNECT)或 socks5:// 代理,例如 --proxy socks5://proxy.example:1080。
kibo把enroll时给出的代理保存在 context 中,之后的每条命令都使用它,数据面连接也一样。- 显式设置优先。没有设置时按
HTTPS_PROXY、ALL_PROXY、NO_PROXY(及小写形式)选择;访问 loopback 地址不使用环境变量中的代理。 - TLS 验证和 mTLS 在代理隧道内完成。代理地址中的用户名和密码不会出现在输出或日志中。
- 数据面不跟随重定向,只在 TLS 验证成功后发送会话凭证。
续期、撤销与恢复
有效期
| 证书 | 有效期与续期 |
|---|---|
| ctrl 服务端证书 | 30 天,只保存在内存中。剩余不到 10 天时重新签发并原子切换;ctrl 重启时重新签发 |
| Operator 与 node 的客户端身份 | 30 天。剩余不到 10 天时续期:kibo 在访问 ctrl 前自动续期,也可以执行 kibo context renew;node 在运行中自动续期 |
| node 数据面证书 | 30 天。进入续期窗口后带退避重试,期间当前证书继续使用;过期后拒绝新的 TLS 握手 |
| 两个 CA | 不在线轮换。恢复时必须使用原来的状态;更换 CA 需要重新初始化 ctrl,并让所有客户端重新注册 |
- 自动续期失败时,
kibo打印警告,继续使用身份直到它过期。 - 身份过期后无法续期,需要用新的注册密钥重新注册。
撤销
kibo enrollment revoke --operator <id> --yes 或 --node <id> --yes 立即终止该身份,且不可撤回:作废尚未使用的注册密钥,拒绝之后的请求、注册和续期。撤销 node 还会断开它当前与 ctrl 的控制连接。
撤销不影响已有的 Workspace 和进行中的数据面连接,它们按各自的规则继续。只是想让 node 暂停接收新的 Workspace,用 kibo node drain。
要让被撤销的 Operator 或 node 回来,重新签发一把注册密钥,再执行 kibo context enroll 或 kibotos-node join。
备份与恢复
恢复一套部署需要同时匹配的 PostgreSQL 数据、ctrl PKI、node 持久状态和客户端身份,以及版本能互相连接的 ctrl 与 node。不要只删除 ctrl 的 PKI 却保留其他状态。
需要重建一套可丢弃的试用环境时,先停止所有进程,按部署文档清理全部状态,再从空目录初始化。不要手工修改数据库的迁移版本,也不要伪造身份文件。
运行账户与状态目录
| 进程或目录 | 说明 |
|---|---|
| ctrl | 可以用专用的服务账户运行,需要能读取配置、数据库连接串和自己的 PKI |
| node | 必须以 root 运行,负责以 kibotos-vmm 账户启动 VMM |
| VMM | 以系统账户 kibotos-vmm 运行,需要属于 kvm 组并能读写 /dev/kvm |
| node 的存储目录 | 保存独占锁、启动序号、Instance、Template 缓存、VM runtime 和 node 身份。删除它就丢失了 node 的全部本地状态 |
| node 的存储池 | 保存 Volume 的块文件与保留的可写层,可以在存储目录之外(例如 NFS 挂载)。声明它、初始化它的标记文件见安装 node |
| node 的 socket 目录 | 保存进程运行期间的 socket 和 VMM 的临时状态。只有停止 node 并确认没有残留 VMM 后才能删除 |
- 存储目录与 socket 目录的上级目录必须允许
kibotos-vmm进入。 - node 会创建并校验自己的目录。不要用符号链接或放宽私钥权限来绕过校验。
执行权
同一个数据库上只有一个 ctrl 执行:调用 node、运行命令与后台工作、写入数据库。它靠数据库里的一行执行权保证,与连接和数据库会话无关,经连接代理或连接池也成立。
| 情形 | 行为 |
|---|---|
| ctrl 启动时没有别的 ctrl 执行 | 立即执行 |
| 另一个 ctrl 正在执行 | 只回答从数据库读取的请求,写入、node API 与 node 控制连接返回可重试的 unavailable;日志记 another ctrl executes; answering reads only until it stops |
| 执行的 ctrl 正常退出 | 停下全部工作后释放执行权,等着的 ctrl 立即接手 |
| 执行的 ctrl 崩溃或与数据库断开 | 执行权 30 秒后过期,等着的 ctrl 才接手;断开的 ctrl 在过期之前停止执行 |
| 执行的 ctrl 失去执行权 | 它此后的写入全部失败,进程以错误退出,由服务管理器重启后只读等待 |
接手的 ctrl 先把全部 node 标为观测过期,等 node 重新连接并上报完整资源清单后才调度到它上面。
替换 ctrl 时先停止旧的,再启动新的,并等待 node 重新连接并上报完整的资源清单。
版本兼容
只有版本兼容时,node 才能连上 ctrl:
| ctrl 与 node 的版本 | 规则 | 被拒绝时 ctrl 的日志 |
|---|---|---|
都是 v0.1.0 或更高的正式版本或 -rc.N 版本,MAJOR.MINOR 相同 | 版本可以不同,见逐台升级 | |
都是 v0.1.0 或更高的正式版本或 -rc.N 版本,MAJOR.MINOR 不同 | 拒绝连接 | is not in ctrl release line |
其余版本(-alpha.N、-beta.N、未发布构建、v0.0.x) | 源码 revision 必须完全相同 | does not match ctrl release |