设计深度解析:策略模型、用户标签与多租户隔离实战)
Milvus 行级安全RLS设计深度解析策略模型、用户标签与多租户隔离实战【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus导读行级安全Row Level SecurityRLS让 Milvus 在集合Collection内部提供行级别的细粒度数据访问控制。管理员无需改动应用逻辑与数据模型即可基于用户身份、角色或动态标签为不同调用方隔离可见与可写的数据行。本文以设计文档 20250610-rls_design.md 为主体骨架完整讲解 RLS 的核心能力、策略表达式语法、公开 API 用法与四类典型业务场景并结合仓库内 proto 定义揭示其底层元数据与日志落地机制帮助你基于当前仓库掌握一套可直接评估与落地的多租户、按用户隔离安全方案。一、RLS 概述把访问控制下沉到数据行在传统向量数据库中权限体系通常停留在库、集合或角色这一层某个用户要么能访问整个集合要么完全不能访问。但在多租户 SaaS、文档管理系统等场景里多个数据主体共享同一个集合每个用户只应看到属于自己的那部分行——这类诉求无法靠集合级权限表达。Milvus 的 RLS 设计正是为填补这一空白在集合粒度启用 RLS 后系统会根据用户身份、角色或动态标签实时评估访问策略在查询与写入路径上对数据行做过滤或校验从而实现同一集合、不同人、不同数据视图。其核心价值在于两点应用逻辑与数据结构零侵入过滤规则全部收敛在 Milvus 侧的策略表达式中运行时可控策略与开关可以在不重启、不改代码的前提下动态变更。设计文档给出的核心能力矩阵如下能力说明启用 / 禁用 RLS集合级开关支持运行时动态控制强制 RLS即使超级用户superuser与管理员也受策略约束策略定义支持按用户 ID、角色、字段值或标签定义策略多策略支持同一动作 / 角色组合可配置多条策略用户标签机制利用动态用户元数据实现灵活的访问过滤表达式语言丰富表达式语法支撑复杂访问控制规则二、策略模型与评估语义在进入 API 之前先厘清 RLS 策略Policy的组成要素。设计文档将一条策略抽象为作用于哪些角色、拦截哪些动作、用什么表达式约束一条策略通常包含collection目标集合名策略只对该集合内的数据生效policy_name策略唯一标识用于后续删除与列举roles策略生效的角色列表。除了普通自定义角色外还支持特殊占位符$current_user——它指代当前调用用户本人这一动态身份actions策略覆盖的写 / 读操作集合文档给出query、insert、delete、update四类using_expr查询场景下的数据过滤表达式决定用户能读到 / 匹配到哪些行check_expr变更mutating场景下的数据校验表达式决定用户能否写入 / 修改 / 删除满足某条件的行description可选的人类可读备注。2.1 策略评估规则设计文档在Security Model Notes中明确了三条评估语义OR 组合逻辑同一用户的全部策略按或合并——只要任何一条策略授予访问权操作即被放行因此收紧要靠各策略自身表达式的严格程度而非互斥叠加动作隔离策略按正在执行的具体动作query / insert / delete / update分门别类地进行匹配与求值角色匹配前提调用方必须至少拥有策略 roles 列表中的某一个角色策略才会被纳入其评估集合。2.2 表达式语言要素策略表达式由字段引用、上下文变量、函数与运算符构成字段引用直接使用集合中的字段名参与比较例如user_id、department、tenant_id上下文变量$current_user_name当前用户名、$current_user_tags当前用户的动态标签字典、$current_roles当前用户角色集合函数支持now()、hour()、date()等通用时间函数用于实现时段类规则运算符标准比较、与逻辑AND、OR运算符。需要说明的是上述变量与表达式语法由设计文档定义落地细节如变量在服务端的解析与注入位置以仓库实际实现为准。从元数据模型看using/check 表达式在存储层以纯文本形式完整保留见下文五、源码落地说明表达式由引擎侧解析求值。三、用户标签机制动态用户元数据仅凭用户名只能表达我与我自己的数据行这类一一映射关系要表达我属于哪个部门 / 哪个租户 / 什么密级就需要一套随用户携带的动态元数据。RLS 的 User Tag用户标签机制正是为此设计。运行时上下文中的两个关键变量被用于动态评估策略$current_user_name当前登录用户名$current_user_tags当前用户的标签集合键值对形式的元数据。3.1 设置用户标签管理员或具备权限的调用方可以为用户附加任意数量的标签client.set_user_tags( useruser_abc, tags{ department: engineering, region: us-west-1, tenant: customer_a, security_level: confidential } )3.2 标签管理 API 一览API说明set_user_tags(user, tags)设置或更新用户标签整体覆盖既有标签delete_user_tag(user, key)删除指定用户的某个标签键get_user_tags(user)获取某个用户的全部标签信息list_users_with_tag(key, value)反查拥有指定标签值的用户列表3.3 在策略表达式中引用标签标签在策略中通过$current_user_tags[key]的字典下标语法引用与数据字段进行比较using_exprregion $current_user_tags[region] check_exprsecurity_level $current_user_tags[clearance]值得注意的是标签值可以是字符串之外的类型从仓库元数据定义可以看到标签以 JSON 对象形式存储值类型支持字符串、int64 与 double详见下文五、3.这意味着security_level $current_user_tags[clearance]这类数值比较是可行的。四、RLS 公开 API 设计设计文档围绕开关—策略—查询定义了如下客户端 API以下代码为设计文档给出的调用形态示例最终以对应版本 SDK 实际签名为准。4.1 启用 / 禁用集合的 RLS通过集合属性开关控制 RLS适合在运行时按需打开或关闭# Enable RLS for a collection client.alter_collection_properties( collectionmy_collection, properties{rls.enabled: True} ) # Disable RLS for a collection client.alter_collection_properties( collectionmy_collection, properties{rls.enabled: False} )4.2 强制 RLS对超级用户也生效默认情况下 RLS 只约束普通用户若业务要求连管理员、超级用户也一视同仁可开启rls.forceclient.alter_collection_properties( collectionmy_collection, properties{ rls.enabled: True, rls.force: True # Applies to all users including superusers } )4.3 创建 RLS 策略client.create_row_policy( collectionuser_documents, policy_namelimit_to_user, actions[query, insert, delete, update], roles[$current_user, user_role], using_expruser_id $current_user_name, check_expruser_id $current_user_name, descriptionRestrict users to their own documents )参数速查参数含义collection目标集合名称policy_name策略唯一标识actions策略覆盖操作query、insert、delete、updateroles策略作用角色$current_user、admin或自定义角色using_expr查询时的数据过滤表达式check_expr变更操作时的数据校验表达式description可选人读备注4.4 删除策略client.drop_row_policy( collectionuser_documents, policy_namelimit_to_user )4.5 列举全部策略policies client.list_row_policies(collectionuser_documents) # Example response: # [ # { # policy_name: limit_to_user, # using_expr: user_id $current_user_name, # check_expr: user_id $current_user_name, # roles: [$current_user], # actions: [query, insert, delete], # description: Restrict users to their own documents, # created_at: 2024-01-15T10:30:00Z # } # ]4.6 查询集合的 RLS 状态status client.get_collection_properties( collectionuser_documents, properties[rls.enabled, rls.force] ) # Returns: {rls.enabled: True, rls.force: False}五、源码落地RLS 元数据与持久化设计设计文档侧重于语义与用法翻阅当前仓库可以找到 RLS 在后端的真实落地痕迹印证其在运行时的存在形态。5.1 策略与主体的持久化模型在 root_coord.protorootcoord 元数据面中定义了两类与 RLS 直接对应的信息结构RLSPolicyInfo携带db_id、collection_id、policy_id、policy_name、policy_type、actions、using_expr、check_expr、description——这与设计文档策略模型逐字段对应且绑定到 (database, collection) 维度策略确实以集合为作用域存储RLSPrincipalInfo携带principal_name主体名即用户与tags字段。源码注释特别说明tags是一个值类型为 string、int64 或 double 的 JSON 对象且以不透明字符串形态持久化——Keeping the durable representation opaque preserves tag types and permits future extensions without changing the persisted protobuf schema即保留原始标签类型、且未来扩展不必改动持久化 proto 结构。这解释了前文 3.3 中标签既可比字符串又可比数值的实现基础标签的类型信息在 JSON 表示中被保留。5.2 变更日志WAL消息RLS 与集合 DDL 的一致性RLS 元数据不仅静态存储还需要跟随流式系统有序传播。在 messages.proto 中定义了专门的 RLS 日志消息族AlterRLSMetadataMessageHeader/DropRLSMetadataMessageHeader携带db_id与collection_id用于定位受影响的集合注释明确说明 header 以集合为粒度的资源键与集合 DDL 做序列化保证 RLS 变更与 DDL 的相对顺序AlterRLSMetadataMessageBody携带完整后像complete post-image重放replay时不依赖请求期的名字解析或可变元数据确保下游恢复的确定性RLSPolicyMetadata与RLSPolicyInfo对应的策略后像policy_id、policy_name、policy_type、actions、using_expr、check_expr、descriptionRLSPrincipalMetadata主体标签后像principal_name 标签 JSONDropRLSMetadataMessageBody只携带稳定逻辑身份policy_name 或 principal_name注释指出对已不存在的条目重放删除是无害的空操作successful no-op天然支持幂等。由此可以推断RLS 的元数据变更建策略、删策略、改标签会被编码为消息流事件与集合 DDL 共用同一套一致性通道分发到数据面让查询节点、数据节点在评估策略时读到的是同一份有序的元数据视图。上述推断基于 proto 中消息结构与注释未在本文展开具体执行代码。六、典型业务场景与策略示例场景一用户只能访问自己的数据需求文档管理系统中用户只能查看与修改自己的文档。集合结构集合中包含user_id字段{ user_id: string, document_name: string, content: string, created_at: timestamp }策略利用$current_user与$current_user_name把数据行与调用者本人绑定——using 控制查得到哪些行check 控制能改哪些行client.create_row_policy( collectionuser_documents, policy_nameuser_own_data, actions[query, insert, delete, update], roles[$current_user], using_expruser_id $current_user_name, check_expruser_id $current_user_name, descriptionUsers can only access their own documents )场景二基于角色的分级访问RBAC需求管理员拥有全部访问权经理只能看本部门数据普通用户只能看自己的数据。实现方式是给不同角色各建一条策略叠加后按OR语义形成分级视图普通用户策略只能看到自己client.create_row_policy( collectionemployee_records, policy_nameuser_scope, actions[query, insert, delete, update], roles[$current_user], using_expremployee_id $current_user_name, check_expremployee_id $current_user_name )经理策略部门范围标签驱动client.create_row_policy( collectionemployee_records, policy_namemanager_scope, actions[query, insert, update], roles[manager], using_exprdepartment $current_user_tags[department], check_exprdepartment $current_user_tags[department] )管理员策略全量放行client.create_row_policy( collectionemployee_records, policy_nameadmin_full_access, actions[query, insert, delete, update], roles[admin], using_exprtrue, check_exprtrue )当某用户同时拥有 user 与 manager 角色时两条策略按 OR 组合既满足本部门条件又满足本人条件的数据行均可见同时保留 manager 角色对 update 动作的约束——而 delete 动作仅user_scope与admin_full_access覆盖体现动作隔离语义。场景三多租户数据隔离需求SaaS 场景中租户之间的数据物理共存于同一集合但必须逻辑隔离。核心是把tenant_id字段与用户标签tenant对齐策略client.create_row_policy( collectioncustomer_data, policy_nametenant_isolation, actions[query, insert, delete, update], roles[$current_user], using_exprtenant_id $current_user_tags[tenant], check_exprtenant_id $current_user_tags[tenant] )为租户用户打标签client.set_user_tags( useruser_123, tags{tenant: acme_corp, role: analyst} )租户扩容时只需维护标签与用户的映射关系set_user_tags/list_users_with_tag无需为每个租户重复建集合或复制数据。场景四基于时间窗的访问控制需求非管理员在工作时间之外不可查询敏感文档。这依赖表达式语言中的时间函数client.create_row_policy( collectionsensitive_documents, policy_namebusiness_hours_access, actions[query], roles[$current_user], using_expr(hour(now()) 9 AND hour(now()) 17) OR $current_user_tags[role] admin, check_exprtrue )该表达式把当前小时在 09–17 之间与用户角色是 admin两个条件 OR 起来普通用户受时间窗约束而携带roleadmin标签的用户可随时访问。注意 check_expr 设为true表示该策略不额外限制变更此处 actions 只有 query。七、访问控制层级与绕过语义RLS 的策略评估与谁是调用者强相关设计文档定义了如下访问控制层级默认行为RLS 只约束非超级用户non-superusers强制模式当rls.forceTrue时RLS 对所有人包括超级用户与管理员生效适合对数据隔离有最高合规要求的场景绕过选项超级用户可临时绕过 RLS 以执行维护操作——即强制模式与维护通道并存管理员既能在日常被策略约束又能在排障 / 批量维护时临时放开。八、性能考量与最佳实践性能考量索引利用using_expr 中的字段应尽量命中已建索引如user_id、tenant_id让行级过滤尽可能下沉到索引检索路径而非全量扫描避免过滤成为向量/标量查询的瓶颈表达式复杂度过于复杂的表达式多层嵌套、多次函数调用会抬高每次请求的求值开销可能影响查询时延策略数量单集合上策略过多会拖慢评估速度。在设计上应合并相似规则保持策略集精简。最佳实践最小权限原则先以最严格的策略起步确认可用后再按需放宽避免先放开再收紧的暴露窗口定期审计周期性复查策略清单list_row_policies与用户标签get_user_tags清理过期角色与标签文档化为每条策略保留清晰的 description说明其目的与影响范围上线前充分测试用不同角色、不同标签的用户组合在设计环境中对 query / insert / delete / update 全覆盖验证确认不存在越权或误伤后再进入生产。九、总结RLS 为 Milvus 提供了一套集合内、行粒度的动态访问控制框架以策略 角色 × 动作 × 表达式为核心以用户标签为灵活的动态输入配合rls.enabled/rls.force两个集合级开关实现运行时管控。它特别适合文档私有化访问、部门级 RBAC、SaaS 多租户隔离与时间窗敏感数据管控四类典型场景。从仓库实现看设计文档中的策略与标签模型已经沉淀为 root_coord.proto 中的RLSPolicyInfo/RLSPrincipalInfo绑定 database collection 维度、标签以 JSON 持久化保留类型并配套 messages.proto 中的 RLS 变更日志消息族通过完整后像 幂等删除保证元数据在集群内的一致性传播。若要在实际环境中启用建议以本设计文档的语义模型为准结合你所使用版本的 SDK 中 row policy 与 user tag 的实际接口签名进行落地并在上线前完整走一遍第八节所列的测试与审计流程。本文以 20250610-rls_design.md 为主体整理元数据细节引用自 root_coord.proto 与 messages.protoAPI 示例为设计文档给出的客户端调用形态。【免费下载链接】milvusMilvus is a high-performance, cloud-native vector database built for scalable vector ANN search项目地址: https://gitcode.com/GitHub_Trending/mi/milvus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考