一、先搞懂OceanBase权限的两个核心层级
很多用OceanBase做业务的同学,尤其是刚接触分布式数据库的开发者,一开始会觉得它的权限体系很绕——一会要建租户,一会要建用户,还得给各种权限,一不小心就踩坑。其实本质上,OceanBase的权限分两个最核心的层级,先把这俩搞明白,后面的风险分析才不会乱。
1.1 租户级权限:相当于数据库的“大房东”权限
OceanBase是多租户架构,说白了就是一个大的数据库集群,可以拆成很多个独立的“小数据库”,每个小数据库就是一个租户。租户级权限,就是给这个“小数据库”整体的权限,比如能不能给这个租户加内存、改存储配额,能不能删这个租户,能不能在这个租户下建数据库、表这些。
举个例子,你在OceanBase集群里建了一个叫shop_tenant的租户,用来存电商业务的数据,那租户级权限就是控制shop_tenant这个整体的权限,比如谁能给shop_tenant加10G内存,谁能删shop_tenant,谁能在shop_tenant里建新的业务表。
1.2 用户级权限:相当于租户里的“租客”权限
租户建好后,不能直接用,得在租户里建用户,用户级权限就是给这个租户下的某个具体用户的权限,比如这个用户能不能读某个表、能不能改某个表的数据、能不能给别的用户授权。
还是用刚才的shop_tenant租户,你在里面建了一个叫shop_dev的开发用户,用来给开发团队做测试,那用户级权限就是控制shop_dev这个用户的权限,比如shop_dev能不能读订单表、能不能改商品表、能不能给新的测试用户授权。
二、为什么会出现权限交叉绑定的情况
正常情况下,租户级权限和用户级权限是分开管的,不会有交叉,但实际业务里,很多开发者为了方便,会把这俩绑在一起,慢慢就出问题了。
2.1 业务场景催生的交叉绑定
最常见的场景有两种:
第一种是小团队管理,比如电商团队只有3个人,既要管租户的资源(比如给shop_tenant加内存),又要管业务数据(比如查订单),为了少建用户,就把租户级的权限和用户级的权限给了同一个用户,比如给shop_dev这个用户同时开了“改租户内存”和“查订单表”的权限。
第二种是临时需求,比如上线前要临时调整租户的存储配额,同时还要查上线前的业务数据,为了省时间,就给某个测试用户同时开了租户级的调整权限和用户级的查询权限。
2.2 权限交叉绑定的具体表现
最典型的表现就是:同一个OceanBase用户,同时拥有租户级权限和用户级权限。
比如你建了一个叫admin_user的用户,给它的权限是:
- 租户级:ALTER TENANT(修改租户配置)、DROP TENANT(删除租户)
- 用户级:SELECT ON shop_tenant..(查询
shop_tenant下所有库表)、UPDATE ON shop_tenant..(修改shop_tenant下所有库表) 这就是典型的权限交叉绑定。
三、权限交叉绑定的安全风险:用实际案例说清楚
光说风险太抽象,我们拿一个真实的踩坑案例来拆解,这个案例是我之前帮一家电商公司排查问题时遇到的,很有代表性。
3.1 案例背景
某电商公司的OceanBase集群有一个shop_tenant租户,用来存电商业务数据,里面有订单表shop_db.order、商品表shop_db.goods、用户表shop_db.user。
团队里有一个叫test_user的测试用户,测试团队之前提了两个需求:
- 测试上线前要临时调整
shop_tenant的存储配额(需要租户级的ALTER TENANT权限); - 测试团队要查
shop_tenant下所有业务表的数据(需要用户级的SELECT权限)。 运维同学为了方便,直接把这两个权限都给了test_user,没有做拆分,具体的权限配置如下(先给大家看配置的代码,后面拆解风险):
3.1.1 权限配置代码(技术栈:OceanBase SQL)
-- 先创建测试用户test_user,密码为Test@123456
CREATE USER test_user IDENTIFIED BY 'Test@123456';
-- 给test_user分配租户级权限:修改租户配置、查看租户状态
GRANT ALTER TENANT, SHOW TENANT ON *.* TO test_user;
-- 给test_user分配用户级权限:查询shop_tenant下所有库表
GRANT SELECT ON shop_tenant.*.* TO test_user;
这个配置看起来很方便,测试同学用一个账号就能完成两个需求,但实际上藏着大风险。
3.1.2 风险一:误操作导致租户级故障
测试同学用test_user账号调整存储配额的时候,不小心输错了参数,把shop_tenant的CPU配额调得极低,导致整个租户的业务系统卡了10分钟,影响了线上用户的正常访问。
为什么会出这个问题?因为test_user同时有租户级权限和用户级权限,测试同学在做数据查询的时候,不小心碰了租户级的配置,而且他对租户级的操作不熟悉,输错了参数。
如果权限是拆分的,测试同学只有用户级的查询权限,没有租户级的修改权限,就不会出这个问题。
3.1.3 风险二:权限扩散导致数据泄露
测试团队有新同学加入,老测试同学用test_user账号给新同学授权,他想给新同学开查询权限,结果不小心把租户级的权限也授权给了新同学:
-- 老测试同学用test_user账号给新同学授权,不小心选了所有权限
GRANT ALL PRIVILEGES ON *.* TO new_test_user;
新同学拿到权限后,不仅能查所有业务数据,还能修改租户的配置,甚至能删除shop_tenant租户。后来新同学不小心把租户的内存配额调到了0,导致整个租户下线了20分钟,损失了几万块的订单。
3.1.4 风险三:权限审计困难
OceanBase的权限审计是分租户级和用户级的,租户级的操作日志存在集群的审计库,用户级的操作日志存在租户的审计库。如果同一个用户同时有两个层级的权限,审计的时候就要同时查两个地方的日志,不仅麻烦,还容易漏。 比如上面的案例,测试同学的误操作日志,一部分在集群的审计库(租户级的修改操作),一部分在租户的审计库(用户级的查询操作),排查问题的时候要同时找两个地方的日志,花了整整2个小时才找到原因。
四、怎么避免这些风险:具体的解决方法
知道了风险,就要有对应的解决方法,下面是我总结的几个实用方法,都是经过实际业务验证的。
4.1 拆分权限:租户级和用户级分开建用户
最核心的方法就是把租户级权限和用户级权限分开,建两个不同的用户,一个管租户级操作,一个管用户级操作。
还是用刚才的案例,我们把原来的test_user拆成两个用户:
tenant_admin_user:只给租户级权限,用来管租户的配置;test_user:只给用户级权限,用来查业务数据。 具体的配置代码如下:
4.1.1 拆分后的权限配置(技术栈:OceanBase SQL)
-- 1. 创建租户管理用户tenant_admin_user,密码为Tenant@123456
CREATE USER tenant_admin_user IDENTIFIED BY 'Tenant@123456';
-- 只给租户级权限,不给任何用户级权限
GRANT ALTER TENANT, SHOW TENANT ON *.* TO tenant_admin_user;
-- 2. 创建测试用户test_user,密码为Test@123456
CREATE USER test_user IDENTIFIED BY 'Test@123456';
-- 只给用户级权限,不给任何租户级权限
GRANT SELECT ON shop_tenant.*.* TO test_user;
这样拆分后,测试同学用test_user账号查数据,就算输错命令,也碰不到租户级的配置;租户管理用tenant_admin_user账号,就算操作失误,也碰不到业务数据,风险就小很多。
4.2 最小权限原则:权限只给刚好需要的
除了拆分用户,还要遵循最小权限原则,就是给用户的权限只给刚好能完成工作的,不能多给。 比如测试同学只需要查订单表的数据,就不要给他整个租户的查询权限,只给订单表的查询权限:
-- 只给test_user查询shop_db.order表的权限
GRANT SELECT ON shop_tenant.shop_db.order TO test_user;
这样就算test_user的权限泄露,也只能查订单表,不能查商品表、用户表,数据泄露的范围就小很多。
4.3 定期审计权限:及时清理无效权限
还要定期做权限审计,比如每个月查一次OceanBase的权限配置,看看有没有用户同时有租户级和用户级的权限,有没有权限给多了的情况。 具体的审计代码如下:
4.3.1 权限审计代码(技术栈:OceanBase SQL)
-- 查所有同时拥有租户级权限和用户级权限的用户
SELECT
u.user_name,
u.host,
GROUP_CONCAT(DISTINCT p.privilege_type) AS privileges
FROM
oceanbase.__all_user u
JOIN
oceanbase.__all_priv p ON u.user_id = p.user_id
GROUP BY
u.user_name, u.host
HAVING
-- 同时存在租户级权限(*.*)和用户级权限(非*.*)
SUM(CASE WHEN p.db_name = '*' AND p.table_name = '*' THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN p.db_name != '*' OR p.table_name != '*' THEN 1 ELSE 0 END) > 0;
这个代码会把所有同时有两个层级权限的用户查出来,然后你就可以针对性地拆分或者清理权限。
五、总结
OceanBase的权限交叉绑定,本质上是为了方便操作而忽略了安全的一种做法,看似省时间,实则藏着很大的风险,小到误操作导致业务中断,大到数据泄露、租户下线,甚至造成经济损失。 要避免这些风险,核心就是三个方法:第一,拆分权限,把租户级和用户级的权限分给不同的用户;第二,遵循最小权限原则,只给刚好需要的权限;第三,定期审计权限,及时清理无效或者不合理的权限。 对于分布式数据库的权限管理,一定要严谨,不能为了一时的方便,给业务埋下安全隐患。
评论
围绕“OceanBase权限模型细化:租户级别与用户级权限交叉绑定的安全风险审计”参与讨论