多租户物联网平台的数据隔离
一套平台服务多个客户时,隔离做在哪一层决定了后续的扩展成本。本文对比三种隔离粒度,并说明设备接入侧最容易出现的越权路径。
- 多数项目从共享库加租户键开始是合理的,关键是在数据访问层统一注入租户条件。
- 靠代码评审保证每处都带租户条件的做法,在团队变动后迟早会漏。
- 设备接入侧比管理后台更容易出现越权,凭据必须绑定租户并禁止通配符订阅。
- 租户标识应成为 MQTT 主题结构的固定层级,并写进权限规则。
- 定时任务、导出、报表等不经用户请求触发的路径最容易遗漏租户维度。
一套平台同时服务多个客户,是集成商和设备厂商常见的形态。隔离做得不够,一次查询条件写漏就可能让 A 客户看到 B 客户的设备;隔离做得过重,每新增一个客户就要部署一整套环境,运维成本随客户数线性上升。
三种粒度
- 共享库加租户键:所有表带租户标识,查询层强制注入条件。成本最低、扩展最容易,代价是依赖代码纪律;
- 独立 schema:每个租户一套表结构,连接时切换。隔离更强,迁移和备份也更灵活,代价是表结构变更要逐个执行;
- 独立实例:每个租户一套完整环境。隔离最彻底,适合有强监管要求的客户,成本也最高。
多数项目从第一种开始是合理的。关键在于不要把租户条件交给每个业务函数自己记得写 —— 应当在数据访问层统一注入,让漏写在代码层面不可能发生。靠代码评审兜底的做法,在团队变动后迟早会漏。
设备接入侧才是薄弱点
管理后台的权限通常会被认真对待,设备接入侧反而容易被忽略。几条典型的越权路径:
- 设备凭据未绑定租户,设备可以往任意主题发布,于是 A 租户的设备能把数据写进 B 租户的主题;
- 订阅权限未限制通配符,一台设备订阅上层通配主题,就能收到同平台其他租户的下行命令;
- 设备注册接口未校验所属租户,任何人用一套注册凭据即可把设备挂到指定租户名下。
对策是让租户标识成为主题结构的固定层级,并在凭据的权限规则里写死:这台设备只能读写自己租户、自己设备标识下的主题。这也是主题设计要把租户放在靠前层级的原因之一。
容易遗漏的几处
导出功能、报表任务、告警通知、以及各类后台定时作业。这些路径常常绕过了常规的查询层:一个按天汇总所有设备的定时任务,如果没带租户维度,生成的报表就会把各租户数据混在一起。上线前应当专门盘一遍所有不经过用户请求触发的代码路径。
日志和监控同样要注意。把租户标识打进日志便于排障,但日志平台本身的访问权限也要控制,否则隔离在业务层做到了,却在日志层泄露。隔离粒度是架构层面的早期决策,属于平台与系统开发里最先要定的问题之一。
常见问题
多租户物联网平台用哪种隔离方式?
多数情况下共享库加租户键即可,前提是租户条件由数据访问层统一注入而非各业务自行添加。有强监管要求或数据量差异极大的客户可考虑独立 schema 或独立实例,但要接受表结构变更和运维成本随客户数增加。
设备会不会收到其他租户的数据?
如果设备凭据未绑定租户、或允许通配符订阅,就会。正确做法是把租户标识作为主题结构的固定层级,并在设备凭据的权限规则中限定只能读写自身租户与自身设备标识下的主题。
哪些地方最容易遗漏租户隔离?
不经用户请求触发的路径:定时汇总任务、数据导出、报表生成、告警通知,以及日志与监控系统。上线前应当专门盘点这些路径,它们通常绕过了常规查询层的租户注入。
参考资料
- 物联网平台产品文档 · 阿里云
- mosquitto.conf man page · Eclipse Mosquitto