安全模型
本页说明 DeviceChain 在哪里划定信任边界、目前由什么强制执行每条边界,以及哪些尚未强制执行。已接受的缺口单独列在一节中,便于你据此规划。
报告漏洞请遵循安全政策中的流程。
信任边界一览
| 边界 | 两侧如何区分 | 由谁强制执行 |
|---|---|---|
| 租户与租户 | 每个租户拥有的行和每个消息 subject 都带有租户 | 每次查询上的数据库作用域,以及代理上按租户划分的 subject |
| 设备与设备 | 设备连接只能使用自己的 subject | 代理,通过对平台的授权回调 |
| 人员或程序与 API | 带签名的令牌,在每个服务中校验 | 令牌签名、租户声明和权限 |
| 密钥与读取者 | 密钥被密封,且从不返回 | 实例根密钥下的信封加密 |
| 租户与网络 | 租户提供的目标无法到达内部地址 | 连接建立时的检查 |
| 平台服务之间 | 各自独立的代理和数据库登录 | 部分。 参见尚未强制执行的内容 |
租户
同一组服务为所有租户提供服务,因此隔离在数据和消息中强制执行,而不是依靠独立的 Pod。
- 存储。 租户拥有的表带有租户,中央数据库作用域为每次读取添加租户条件,并为每次写入标记租户。没有租户上下文的租户作用域查询会被拒绝,而不是不加过滤地执行。
- 消息。 subject 按租户划分,格式错误的租户段会在服务发布或订阅处被拒绝。
- API。 请求的租户取自调用方已验证令牌中的租户声明。唯一的例外是服务调用另一个服务:它在请求头中指明租户,仅在服务令牌的签名验证通过后才会采用。
参见多租户。
设备
- 传输。 代理的 MQTT 和 NATS 监听器默认使用 TLS,只能为本地调试而关闭。HTTP 接入由实例 ingress 的 TLS 保护。
- 身份由代理断言。 代理请求平台批准每个连接,平台将该连接绑定到那一台设备自己的 subject。平台对事件所信任的设备身份,是代理投递该事件所用的 subject,而不是载荷中的声明。设备无法向其他设备或其他租户的 subject 发布,也无法读取它们的命令。MQTT 客户端 ID 还必须是设备自己的
{instanceId}:{tenant}:{deviceToken},其后可选地跟上:和设备自选的后缀,因此一台设备无法接管另一台设备的会话。 - 凭据。 设备认证的默认模式是
required:只接受来自已认证设备的事件。在平台的 MQTT 代理上,设备在连接时认证;通过 HTTP 或你自己的代理时,凭据随事件正文传输。凭据认证了某台设备之后,指名其他设备的事件会被拒绝。禁用、轮换或删除凭据会立即拒绝新连接,并结束该设备已打开的会话。参见设备凭据,其中包括撤销多快能影响处理中的事件。 - 静态存储的凭据。
MQTT_BASIC凭据的密码仅以带密钥的单向摘要形式存储,且从不返回。ACCESS_TOKEN凭据目前不同:令牌就是凭据的标识符,以平台可读回的值存储。数据库或备份泄露会暴露所有基于令牌认证的设备的令牌。请相应对待这类备份,并尽可能优先使用MQTT_BASIC。计划在 1.0.0 之前改为仅以摘要形式存储访问令牌。 - 事件正文中的凭据。 在平台的 MQTT 代理上,连接已经认证了设备,因此正文中还携带凭据的事件会被拒绝。过渡性设置
capture.bodyCredential: strip会接受此类事件并移除其中的凭据,使设备群在固件更新期间仍能上报;该设置计划在 1.0.0 之前移除。通过 HTTP 或你自己的代理时,正文就是凭据的传输方式,密码或令牌会在事件放入平台内部事件流之前被密封。在所有传输方式上,为诊断而保留的无法解码消息的副本都会移除其中的凭据。
人员与程序
- 令牌。 登录会签发使用 RS256 签名的访问令牌和刷新令牌。服务根据已发布的密钥集在本地验证,因此没有请求需要等待对登录服务的调用。签名密钥仅由
user-management持有,并在实例根密钥下密封。 - 权限。 角色携带权限,API 在每次调用时检查。成员资格可以限制在某个客户的数据范围内,租户自行管理的成员、邀请和角色,授予的权限不能超过授予者自己拥有的权限。参见治理与配额。
- 单点登录。 租户可以要求通过自己的身份提供商登录。参见单点登录。
- AI 代理。 可选的 MCP 服务器使用调用方自己的令牌调用平台。它自己不持有令牌,因此代理只能读取其用户可读取的内容。
- 滥用限制。 接入有按租户的速率上限,出站连接器有按租户的上限,MCP 服务器限制每个调用方,GraphQL 请求的执行和请求体大小有上限,登录失败尝试有速率限制。GraphQL API 本身没有按租户的请求速率限制。
密钥
租户存储的凭据,例如连接器设置和身份提供商的客户端密钥,保存在加密的密钥存储中。每个值用各自的 AES-256-GCM 数据密钥密封,该数据密钥由实例根密钥包裹,根密钥位于实例的 Kubernetes Secret 中。通过 API 该值只写:使用时在服务器内部解析,从不返回。变更会按谁、何时、哪个句柄进行审计,绝不记录值。
根密钥是密钥存储无法自行恢复的唯一内容。请保留一份托管副本:没有它,恢复后的实例无法解密其密钥,也没有人能登录。参见灾难恢复。密钥存储支持可插拔的密钥提供者,但目前随附的唯一提供者是实例根密钥。
出站流量
Webhook、邮件中继和出站连接器会向租户提供的目标发送数据。每个此类连接都会在建立的那一刻,根据名称实际解析到的地址进行检查,因此在检查与连接之间更改应答的名称无法绕过检查。回环地址、私有地址、运营商级 NAT 地址、链路本地地址和云元数据地址都会被拒绝,被拒绝的目标属于永久失败,而不是重试。运营商可以放行特定的私有地址。chart 的 networkPolicy 是第二层,默认关闭。参见出站连接器。
审计
每个服务都会为其对租户数据所做的每次创建、更新和删除记录一条日志,并包含执行操作的身份;登录服务还会记录登录事件,例如登录、登录失败和单点登录结果。租户的审计页面显示注册表日志,且只显示该租户自己的条目。登录事件通过运营方的管理 API 读取;其他功能区(例如连接器、仪表板和更新)的日志会被记录,但不会显示在审计页面上。
注册表条目与其所描述的变更在同一事务中写入,两者要么一起保存,要么都不保存。如果条目无法写入,该变更不会生效,调用会报告失败,因此变更绝不会在无记录的情况下完成,而报告的失败也绝不对应已保存的变更。登录条目写入失败只会记入日志,不会阻止登录。条目从不被编辑,唯一的例外是删除租户时会清空其条目中的名称。该日志不会自动清理。参见审计。
尚未强制执行的内容
这些缺口已知,并在当前版本中被接受。请据此规划。
- 平台服务目前共用一个代理登录。 所有服务以同一个登录连接代理。该登录被拒绝向只有设备才能写入的 subject 发布,这可防止服务误发设备事件,但它也能管理代理的流,而流可以被配置为写入这些 subject。因此,被攻陷的服务可以为任何租户伪造设备事件、命令响应和设备上报。为每个服务提供各自的登录、并限制在其所需 subject 和流的工作正在进行,计划在 1.0.0 之前完成,发布后本条会改变。在此之前,请把接收设备流量的服务视为平台可信核心的一部分。
- 实例中所有服务共用一个数据库登录。 所有服务以同一个登录访问实例的关系数据库,因此被攻陷的服务可以读写其他功能区的表。数据库中的租户隔离依赖应用的租户作用域,而不是数据库权限。按服务划分的独立角色计划在 1.0.x 版本中提供。
- 访问令牌以可读形式存储,参见设备。计划在 1.0.0 之前改为仅以摘要形式存储。
- 代理主节点切换期间的 MQTT 确认。 默认情况下,如果捕获流更换主节点,代理可能已确认某条 QoS 1 消息却随后丢失它。开启
infrastructure.nats.mqttFailClosed后,确认即表示平台已保存该消息。该设置默认关闭,权衡见可观测性。 - 任何能读取实例 Kubernetes Secret 的人都能读取根密钥。 请相应限制对该 Secret 和集群 etcd 的访问。
- 尚未发布外部渗透测试。
本页不能取代你自己的审查。如果这里的某一条对你的部署构成问题,上文对应的章节会指出控制措施所在的位置。