多租户数据平台的权限该怎么建:三条威胁模型、四条设计原则

0 阅读6分钟

数据平台和普通业务系统最大的不同:用户可以在平台的计算引擎里跑代码——SQL、UDF、Notebook、JAR 作业。这决定了权限不能做成「前端藏按钮 + 接口加注解」,它是个架构问题:边界放在哪一层、异常时倒向哪一边、约束由谁作准。这篇以可私有化部署的数据平台「我的数据空间」(datastudiohappy.cn)为例,讲多租户权限体系的完整建法。

一、威胁模型:先想清楚防什么

三条主线,缺一条,设计就会漏一块:

1. 越权读写。 A 空间的用户不该碰到 B 空间的数据——且这条要在「用户能跑任意代码」的前提下依然成立。

2. 绕过平台、直连引擎。 鉴权全在控制面,而引擎端口在内网可达——原生 JDBC 直连过去,控制面那套判断一行都不会执行。

3. 降级路径静默放行。 解析失败、异常分支若默认「放过去」,那么解析不了的语句——往往正是最危险的那类——反而走了权限最松的路。

好的设计要把这三条变成结构上无从发生,而不是靠一段判断挡住。

二、原则一:数据面隔离——控制面鉴权不等于隔离

控制面鉴权是「判断谁能干什么」:带身份调 API,查 ACL,决定放不放行。它必要,但前提是所有访问都流经这道判断。而数据平台的现实是:作业和 Notebook 在引擎里执行,引擎拿存储凭证直接访问对象存储——凭证若对整个数据湖有权,代码一旦跑进引擎,控制面判过的 ACL 就全部作废。

所以机密性的硬边界必须沉到数据面:

多租户隔离:控制面判权,数据面强制,即便应用层被绕过存储层 403 仍然成立

「我的数据空间」的落地方式:

  • 每空间一把存储小账号,只对本空间的存储前缀有读写权;
  • 表(而非库)作为存储访问单元:逻辑库可以跨空间共享,但每张表的物理位置按建表者的空间前缀分配——小账号的权限范围与空间边界严丝合缝;
  • 跨空间读 = 显式授权:把被授表的真实前缀加进读者空间小账号的访问策略,撤权即时失效,上层授权与底层存储权限永远联动一致;
  • 平台维护作业(快照过期、文件整理等跨空间治理)保留大账号,但只跑平台固定代码、无用户代码入口——大账号加可信代码是安全的,大账号加用户代码才是洞。

跨空间授权在产品里是一个显式动作,授给哪个空间、什么权限,一目了然:

读空间授权:把一张湖表的读权限显式授予另一个空间,存储侧策略随之联动下发

效果:即便应用层鉴权被完全绕过——直连引擎、跑任意代码——存储层 403 依然成立,最坏也只触到本空间本就授权的数据。引擎侧逐表 SQL 鉴权仍保留,但定位是纵深防御,守存储前缀表达不了的细粒度(owner 才能改表结构、列级隐私字段);承重墙在存储层。

三、原则二:单一权威来源——别让每个模块各判一套

同一条约束,若网关判一次、作业提交判一次、引擎侧再判一次,各写各的,迭代几轮后一定漂移成互相矛盾——矛盾处就是缝隙。

正确做法:每条横切约束只有一个权威判定组件,其余模块引用它。「我的数据空间」里,权限判定以 ACL 真值表为唯一口径,重建存储策略、判断可读可写,全部从这一张表推导;哪些数据源可以被引擎直查,由目录类型定义作唯一真值,前端、后端、引擎同引一处。改约束时,权威源与所有引用处一起改。

产品形态上也一样:成员角色、访问令牌、系统账号凭据托管收在同一处空间管理,授权湖表时存储侧策略联动下发——上层权限与底层存储权限永远同源:

空间管理:成员与角色、访问令牌、凭据托管集中管理,密钥密文展示,授权湖表联动下发存储侧策略

四、原则三:fail-closed——功能可以降级,安全不能

一个功能因解析失败而不可用,用户会抱怨,但数据不会泄露;一个安全边界因解析失败被跳过,用户毫无感知,数据已经出去了。两者的代价完全不对等,所以异常分支的默认动作必须是保守拒绝:

  • 解析失败不等于安全通过:解析不出涉及哪些表的语句,默认拒,而不是「放行让引擎自己说」;
  • 缺关键上下文即拒:引擎会话没带可信的空间标识,就拒绝建立会话,而不是当作没有限制;
  • 成员校验前置:「是不是这个空间的成员」在所有路径最前面无条件执行,不藏在「解析成功」的分支里;
  • 身份令牌兜底:令牌识别异常时,退到能力最窄的身份,而不是最宽。

还有一条职责归属:SQL 语法与执行的权威是引擎,不是网关的尽力解析。 网关解析只做尽力拦截,绝不作唯一鉴权闸——两个解析器对同一条语句的理解一定有分歧,分歧处就是绕过点。真正的强制要么落在引擎侧,要么沉到存储层。

五、原则四:凭证不落盘

进程的启动参数、命令行都能被读出来;编码不是加密。凭证的正确到达方式是运行时懒加载:作业启动时不预置任何存储或数据库凭证,只带一把一次性令牌;要访问数据时凭令牌回调平台内部接口现取,凭证只驻进程内存,不进命令行、不进配置快照。令牌签发即冻结归属空间的能力集,运行中的作业无法给自己提权;即便泄露,半径也限定在单个作业、单个空间。

六、两条通用提醒

  • 表级鉴权天然拦不住「不产生表目标」的操作(反射调用、加载自定义函数)。要么明确拒掉这类原语,更根本的是执行身份换成空间小账号——逃逸半径收敛在本租户;
  • 删表横跨元数据与数据两个平面,每个平面都要有唯一的拦截点,只守一边一定有绕过路径;底层存储自带原生权限机制的,尽量借力,别在上层重造一套判断。

收束:一道自检题

四条原则没有一条在讲「怎么加权限模块」:数据面隔离决定承重墙沉在存储层还是飘在网关;单一权威来源决定约束会不会随迭代漂移;fail-closed 决定异常时倒向拒绝还是放行;凭证不落盘决定攻击面里有没有明文密钥。

最后一道自检题:假设所有应用层鉴权都被绕过——用户直连引擎、跑起任意代码——还有什么拦住他跨租户读数据? 答案应该是存储层。这正是「我的数据空间」(datastudiohappy.cn)把隔离沉到数据面的原因。


这套权限体系是「我的数据空间」的一部分——一套可私有化部署的数据平台,支持 OEM 合作。产品介绍:datastudiohappy.cn/,交流合作 QQ:1559851993。