Open Policy Agent 访问控制模型实战:用 Rego 实现 RBAC、ABAC、AWS IAM 与 XACML 策略 Open Policy Agent 访问控制模型实战用 Rego 实现 RBAC、ABAC、AWS IAM 与 XACML 策略【免费下载链接】opaOpen Policy Agent (OPA) is an open source, general-purpose policy engine.项目地址: https://gitcode.com/gh_mirrors/op/opa导读本文以 OPA 官方文档《Access Control Systems》为核心系统讲解如何将业界主流的四类访问控制模型——基于角色的访问控制RBAC、基于属性的访问控制ABAC、AWS IAM 策略以及 XACML 策略——用 Open Policy Agent 与 Rego 语言重新实现。你将掌握 Rego 中default allow、规则迭代、contains集合规则、in成员判断等核心写法并能在实际项目中把既有策略体系平滑迁移到 OPA实现策略与业务代码的解耦。为什么用 Rego 对比既有访问控制系统对照你已经熟悉的系统来学习新工具是建立认知最快的方式。OPA 官方文档将多种现有策略系统的政策逐一映射到 Rego帮助读者在熟悉的概念框架内理解 OPA 的表达能力。这种以旧带新的方式尤其适合以下几类场景将老系统如 XACML、AWS IAM中的策略迁移到 OPA统一策略管理用 RBAC/ABAC 的既有思维快速设计 Rego 规则理解 OPA 相比传统策略引擎的差异策略即代码、可测试、可版本化。需要注意本文讨论的是访问控制模型层面的对比如果你关心 Rego 与 Go、Java、Python 等编程语言的语法对比请参阅语言对比文档。从源码角度看Rego 之所以适合承载这些模型是因为它天然支持文档中的声明式语法if、contains、in等关键字在 OPA v1 语法中已成为默认关键字无需显式导入见 v1/ast/parser.go#L49-L53。下面的全部示例均使用这套 v1 语法编写。基于角色的访问控制RBACRBAC 的核心思想RBACRole-Based Access Control是目前授权领域最普及的模型。使用 RBAC 做授权只需要登记两类信息哪些用户拥有哪些角色user-role assignments哪些角色拥有哪些权限role-permission assignments。有了这两张映射表RBAC 就能给出授权结论用户被授予其所有角色对应权限的并集。考虑下面的用户/角色分配用户角色aliceengineeringalicewebdevbobhr以及下面的角色/权限分配角色权限资源engineeringreadserver123webdevwriteserver123webdevreadserver123hrreaddatabase456在这个例子中RBAC 给出的授权结论是用户操作资源结论alicereadserver123allow因为alice属于engineeringalicewriteserver123allow因为alice属于webdevbobreaddatabase456allow因为bob属于hrbobreadserver123deny因为bob不属于engineering或webdev用 Rego 实现 RBAC下面用 Rego 完整实现上述 RBAC 策略package rbac.authz # user-role assignments user_roles : { alice: [engineering, webdev], bob: [hr], } # role-permissions assignments role_permissions : { engineering: [{action: read, object: server123}], webdev: [{action: read, object: server123}, {action: write, object: server123}], hr: [{action: read, object: database456}], } # logic that implements RBAC. default allow : false allow if { # lookup the list of roles for the user roles : user_roles[input.user] # for each role in that list r : roles[_] # lookup the permissions list for role r permissions : role_permissions[r] # for each permission p : permissions[_] # check if the permission granted to r matches the users request p {action: input.action, object: input.object} }对这份策略做一次查询例如查询bob能否对server123执行read传入输入文档{ user: bob, action: read, object: server123 }查询命令为data.rbac.authz即整个包名对应的数据文档结果为allow false拒绝。理解这份 Rego 的关键写法default allow : false声明allow的默认值为false。Rego 中未定义即为未定义default保证在没有任何规则满足时返回一个确定的布尔值从而让拒绝成为显式结果。这与 AWS IAM 默认隐式拒绝的语义一致见下文。迭代语法roles[_]、r : roles[_]_是匿名变量x[_]表示遍历集合/数组中的每一个元素。roles[_]一次性遍历用户的所有角色permissions[_]遍历角色的所有权限。Rego 规则体是与逻辑遍历产生的多个候选会通过或语义自动展开——这正是 Rego 把循环 匹配声明式表达的体现。input文档OPA 的每次查询都携带input请求上下文策略通过input.user、input.action、input.object读取请求字段实现策略与请求解耦。RBAC 的职责分离SOD职责分离Separation of Duty, SOD指某些权限组合不应同时被同一个人拥有。例如创建付款的人不应同时拥有审批付款的权限。在 RBAC 语境下SOD 意味着存在一些角色对任何用户都不能被同时分配。例如下面两对角色任意一对同时出现在某用户身上即构成违规create-payment 与 approve-paymentcreate-vendor 与 pay-vendor需要说明的是OPA 的 API 目前还不能在分配角色这个动作上直接拒绝违规分配即不能在写入用户-角色关系时强制 SOD但 OPA 完全可以表达 SOD 约束并查询出所有违规用户。下面的语句是对上文 RBAC 语句的扩展# Pairs of roles that no user can be assigned to simultaneously sod_roles : [ [create-payment, approve-payment], [create-vendor, pay-vendor], ] # Find all users violating SOD sod_violation contains user if { some user # grab one role for a user role1 : user_roles[user][_] # grab another role for that same user role2 : user_roles[user][_] # check if those roles are forbidden by SOD sod_roles[_] [role1, role2] }这段代码引入了两种新语法contains user将sod_violation声明为集合规则。与:的完整定义不同contains允许一条规则为结果集合添加多个元素——每一个满足规则体的user都会被加入集合。some user显式声明存在量词表示存在某个用户。此处通过role1 : user_roles[user][_]和role2 : user_roles[user][_]两次遍历同一用户的角色列表再与sod_roles中的角色对比对即可找出所有同时持有冲突角色的用户。顺带一提本文实现的属于静态 SODstatic SOD只要用户被同时分配两个冲突角色就算违规。动态 SODdynamic SOD允许同一用户持有冲突角色但要求不能在同一笔交易中同时使用它们这超出了本文范围。基于属性的访问控制ABACABAC 的三大组件ABACAttribute-Based Access Control利用请求中用户、对象、动作的属性来做决策包含三个主要组件用户的属性attributes for users对象的属性attributes for objects决定哪些属性组合被授权的逻辑logic。例如用户属性如下alice入职公司 15 年是 trader交易员bob入职公司 5 年是 analyst分析师。对象属性以股票代码为例MSFT在 NASDAQ 交易每股 $59.20AMZN在 NASDAQ 交易每股 $813.64。一条用自然语言表达的 ABAC 策略可能是交易员可以购买 NASDAQ 上总价低于 $2M 的股票拥有 10 年以上经验的交易员可以购买 NASDAQ 上总价低于 $5M 的股票。用 Rego 实现 ABACpackage abac # User attributes user_attributes : { alice: {tenure: 15, title: trader}, bob: {tenure: 5, title: analyst}, } # Stock attributes ticker_attributes : { MSFT: {exchange: NASDAQ, price: 59.20}, AMZN: {exchange: NASDAQ, price: 813.64}, } default allow : false # all traders may buy NASDAQ under $2M allow if { # lookup the users attributes user : user_attributes[input.user] # check that the user is a trader user.title trader # check that the stock being purchased is sold on the NASDAQ ticker_attributes[input.ticker].exchange NASDAQ # check that the purchase amount is under $2M input.amount 2000000 } # traders with 10 years experience may buy NASDAQ under $5M allow if { # lookup the users attributes user : user_attributes[input.user] # check that the user is a trader user.title trader # check that the stock being purchased is sold on the NASDAQ ticker_attributes[input.ticker].exchange NASDAQ # check that the user has at least 10 years of experience user.tenure 10 # check that the purchase amount is under $5M input.amount 5000000 }例如对如下输入查询data.abac{ user: alice, ticker: MSFT, action: buy, amount: 1000000 }第一条规则所有交易员可买 NASDAQ 低于 $2M满足返回allow true。ABAC 在 OPA 中的真正优势在 OPA 中用户和对象并没有任何特殊之处——你可以把属性附加到任何东西上。属性本身可以是任意嵌套的结构化 JSON属性的属性还可以有属性层层嵌套。因为 OPA 从设计之初就面向任意嵌套的 JSON 数据这也是 OPA 核心数据结构的能力所在它能够表达非常富有表现力的 ABAC 策略。同时要注意本例把用户属性、股票属性直接硬编码在策略里这只适合演示。生产环境中更常见的做法是把这类属性数据放进data文档例如通过 bundle 或外部数据加载让策略与数据分离、各自独立更新。Amazon Web Services IAMIAM 策略模型AWS IAM 允许你创建可挂载到用户、角色、组以及选定资源上的策略。你通过编写allow和deny语句来约束哪些用户/角色在什么条件下能/不能对哪些资源执行哪些 API 调用。IAM 的决策规则要点默认情况下所有 API 访问请求都被隐式拒绝即未显式允许即拒绝策略语句可以显式 allow 或 deny如果某个请求同时被 allow 和 deny则总是 deny显式拒绝优先。假设 AWS 中定义了如下客户托管策略customer managed policy并通过attach-user-policyAPI 附加到主体principalalice 上{ Version: 2012-10-17, Statement: [ { Sid: FirstStatement, Effect: Allow, Action: [iam:ChangePassword], Resource: * }, { Sid: SecondStatement, Effect: Allow, Action: s3:ListAllMyBuckets, Resource: * }, { Sid: ThirdStatement, Effect: Allow, Action: [ s3:List*, s3:Get* ], Resource: [ arn:aws:s3:::confidential-data, arn:aws:s3:::confidential-data/* ] } ] }用 Rego 表达 IAM 策略在 OPA 中我们把 AWS 策略中的每条allow语句写成一条独立的 Rego 规则并约定输入文档包含principal、action、resource三个字段package aws default allow : false # FirstStatement allow if { principals_match input.action iam:ChangePassword } # SecondStatement allow if { principals_match input.action s3:ListAllMyBuckets } # ThirdStatement # Use helpers to handle implicit OR in the AWS policy. # Below all of the principals_match, actions_match and resources_match must be true. allow if { principals_match actions_match resources_match } # principals_match is true if input.principal matches principals_match if { input.principal alice } # actions_match is true if input.action matches one in the list actions_match if { # iterate over the actions in the list actions : [s3:List.*, s3:Get.*] action : actions[_] # check if input.action matches an action regex.globs_match(input.action, action) } # resources_match is true if input.resource matches one in the list resources_match if { # iterate over the resources in the list resources : [arn:aws:s3:::confidential-data, arn:aws:s3:::confidential-data/.*] resource : resources[_] # check if input.resource matches a resource regex.globs_match(input.resource, resource) }例如查询data.aws输入如下该请求对应的ec2:StartInstance不在任何允许语句中{ principal: alice, action: ec2:StartInstance, resource: arn:aws:ec2:::instance/i78999879 }三条allow规则均不满足结合default allow : false最终返回allow false拒绝还原了 IAM 隐式拒绝的语义。关键点用 helper 规则处理隐式 OR 与通配符这段示例揭示了一个重要映射关系AWS 策略的Statement数组天然是或语义任一语句匹配即允许Rego 中多个同名规则之间也是或语义——因此每条 Statement 对应一条allow if { ... }规则即可无需显式 OR。而对于 ThirdStatement 内部动作列表/资源列表这种同一语句内部的隐式 OR则拆出principals_match、actions_match、resources_match三个 helper 规则规则体内部用actions[_]遍历列表实现任一匹配即可再把三个 helper 以与关系组合进allow精确对应 AWS 语句中主体验证 ∧ 动作验证 ∧ 资源验证的逻辑。通配符匹配依赖内置函数regex.globs_match。该内置函数的定义见 v1/ast/builtins.go#L1049-L1066它接受两个 glob 风格正则表达式判断二者交集是否匹配非空字符串集合。注意它对正则符号集有限制——只有.、*、、[、-、]和\会被当作特殊符号处理。示例中s3:List.*、arn:aws:s3:::confidential-data/.*正是借助该内置函数实现的通配符动作与资源匹配。XACMLXACML 策略的 XML 形态eXtensible Access Control Markup LanguageXACML可扩展访问控制标记语言是专为表达安全策略设计的标准用属性用户、资源、动作、环境来做出 allow/deny 决策。下面是一条 XACML 策略来自 Curtiss 或 Packard 组织、国籍为美国或英国、工作项目为 DetailedDesign 或 Simulation 的用户被允许访问关于 NavigationSystems 的文档Policy xmlnsurn:oasis:names:tc:xacml:3.0:core:schema:wd-17 PolicyIdurn:curtiss:ba:taa Version1.1 RuleCombiningAlgIdurn:oasis:names:tc:xacml:3.0:rule-combining-algorithm:deny-unless-permit DescriptionPolicy for Business Authorization category TAA-1.1/Description Target / Rule RuleIdRule for NavigationSystems EffectPermit Target AnyOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringNavigationSystem/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:3.0:attribute-category:resource AttributeIdurn:curtiss:names:tc:xacml:1.0:resource:Topics DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf /AnyOf AnyOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringPackard/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/OrganizationID DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringCurtiss/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/OrganizationID DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf /AnyOf AnyOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringGB/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/Nationality DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringUS/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/Nationality DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf /AnyOf AnyOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringDetailedDesign/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/Work-Effort DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf AllOf Match MatchIdurn:oasis:names:tc:xacml:1.0:function:string-equal AttributeValue DataTypehttp://www.w3.org/2001/XMLSchema#stringSimulation/AttributeValue AttributeDesignator Categoryurn:oasis:names:tc:xacml:1.0:subject-category:access-subject AttributeIdhttp://schemas.tscp.org/2012-03/claims/Work-Effort DataTypehttp://www.w3.org/2001/XMLSchema#string MustBePresenttrue / /Match /AllOf /AnyOf /Target /Rule /Policy用 Rego 重写同一条 XACML 策略同一条策略在 OPA 中表达如下。这里假设输入与 XACML 大体一致包含用户、动作和资源的属性。注意permit规则带有 METADATA 注释保留了原始 XACML 策略的PolicyId与描述信息package xacml # METADATA # title: urn:curtiss:ba:taa:taa-1.1 # description: Policy for Business Authorization category TAA-1.1 default permit : false permit if { # Check that resource has a NavigationSystem entry input.resource[NavigationSystem] # Check that organization is one of the options input.user.organization in [Packard, Curtiss] # Check that nationality is one of the options input.user.nationality in [GB, US] # Check that work_effort is one of the options input.user.work_effort in [DetailedDesign, Simulation] }对应的一条典型输入如下查询命令为data.xacml{ user: { name: alice, organization: Packard, nationality: GB, work_effort: DetailedDesign }, resource: { NavigationSystem: true }, action: { name: read } }从 XML 到 Rego 的化简思路对比可见 Rego 版本的表达极为紧凑XACML 中RuleCombiningAlgIddeny-unless-permit的语义在 Rego 中由default permit : false直接承担只有permit规则体全部成立才返回true否则为falseXACML 中AnyOf或/AllOf与的嵌套匹配在 Rego 中分别映射为规则体内部的条件以与连接和使用in集合成员判断实现或匹配每个string-equal的Match从一坨 XML 变成了input.user.organization in [Packard, Curtiss]这样的单行表达式METADATA 注释用于保留策略元数据如title、description这在 OPA 的文档化与发现能力如 annotations 机制中是可被工具读取的。四种模型对比小结模型决策依据关键 Rego 写法默认拒绝RBAC用户 → 角色 → 权限 的映射roles[_]、permissions[_]遍历 default allow : false是RBAC SOD角色对是否冲突contains user集合规则 some user查询违规集合ABAC用户/对象/动作属性组合属性查找user_attributes[input.user] 条件判断是AWS IAMprincipal/action/resource 通配符多规则隐式 OR regex.globs_matchhelper是隐式拒绝XACML属性匹配AnyOf/AllOfin集合成员判断 default实现 deny-unless-permit是这些示例共同展示了 OPA 的一个核心设计取向策略决策默认拒绝一切授权都来自显式规则。无论是 RBAC 的两张映射表、ABAC 的属性矩阵还是 IAM 的 Statement 数组、XACML 的 Rule都可以在 Rego 中以声明式、可测试、可版本化的方式重新表达并且输入文档input统一了不同系统的请求结构差异。延伸阅读想了解 Rego 与 Go/Java/Python 等编程语言的语法对比见语言对比文档完整的 Rego 语言参考见 policy-language.mdOPA 策略测试与调试方式见 policy-testing.mdregex.globs_match等内置函数在 OPA 中的注册与定义见 v1/ast/builtins.go#L1049-L1066完整内置函数清单可在 builtin_metadata.json 中检索本文所有示例的原始出处为 docs/docs/comparisons/access-control-systems.md。【免费下载链接】opaOpen Policy Agent (OPA) is an open source, general-purpose policy engine.项目地址: https://gitcode.com/gh_mirrors/op/opa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考