为什么使用ABAC
一般提到授权,我们就会想到角色(role)。什么样的用户拥有什么样的角色可以怎么操作什么样的资源,这是我们普遍使用的权限系统的模型。这里的角色实质上是包含了一组用户操作资源的规则集合。一旦角色被创建,也就意味着有一组资源操作集合被固化起来,除非去修改角色,否则这个规则集合是不会变的。 比如在一个图书馆的图书管理系统中,所有读者都可以查看书籍及其不同的分类,图书管理员编辑图书信息,管理层可能要看一些图书借阅报表等。这些都可以给读者和图书管理员分配不同的角色就可以实现。在一个较大的图书馆,图书管理员可能分布在不同楼层或者管理不同类别的图书,所以我们需要创建多个图书管理员的角色,比如: 二楼图书管理员,或经济类图书管理员等。更大的图书馆可能会有出版社区分的管理员或者代理人员,因此会有更多基于出版社和图书分类的角色。随着该图书馆的不断扩大,业务职责越来越精细,会继续不断的创建新的角色,比如对于一个出版社在一个楼层的一个特定分类的按照出版时间进行不同角色管理,就会有 “出版社数 x 楼层数 x 楼层图书分类数 x 出版时间分类数” 的角色创建,如此下去,整个系统就会面临“角色爆炸”的窘态。
上述描述的就是基于角色的访问控制,简称RBAC(Role-Based Access Control)它他是一种静态的访问控制模型,主要用于一些基本的简单授权系统中。那如果我们需要更细粒度的访问控制,而又不想面临“角色爆炸”,基于属性的访问控制,简称ABAC(Attribute-Based Access Control)可以是一种选择。在ABAC的模型中没有角色的概念,也就没有“角色爆炸”,它是通过定义一系列的访问规则,利用当前用户操作相关资源的属性,动态的决定用户有没有权限操作该资源。当前著名的AWS IAM就是使用ABAC管理访问控制的,也称PBAC(Policy-Based Access Control)。
ABAC模型和框架
在ABAC的概念模型中,主要包含:
- 访问者(Subject):指操作的发起者,可能是一个用户,或一个系统。
- 受访者(Object):指操作的接受者,也就是访问目标资源,例如用户数据等。
- 操作(Operation):即对受访者的操作,常见的增删改查等。
- 属性(Attribute):指一组来自访问者,受访者,和操作相关的数据,也包含一些环境上下文数据。
- 策略(Policy):指可以基于属性动态生成访问控制结果的预定义的一组规则。
但ABAC毕竟只是个模型,要真正实现它,最好依赖于一种成熟的框架。XACML(eXtensible Access Control Markup Language 可扩展的访问控制标记语言)是一个标准的ABAC框架,它详细的定义了ABAC中的每一个概念和实现方式。它定义的ABAC系统主要包含4个组件:
- 策略执行点(PEP):最终执行策略结果的地方,决定要不要继续访问资源。
- 策略决策点(PDP):通过策略和属性信息决定有无权限进行操作的地方。
- 策略信息点(PIP):为执行决策提供额外属性的地方,可以是一个或者多个额外系统。
- 策略管理点(PAP):管理和配置策略的地方。
其直接关系如下图:
ABAC的实现
有了XACML的框架支持,对于ABAC的实现,我们可以按需裁减。首先我们看下系统的结构图,这样更明确下我们ABAC系统的地位和职责。
我们可以把MyABAC 看作访问者与被访者之间的一个层,类似于apiGateway 或BFF,也可以看作是受访者的代理,类似于nginx等。暂且不管它是什么,他的职责就是拦截或者转发请求。明确了这一点之后, 我们用前后端的例子将其具象化:(这里的frontend 可以是一个UI,或一个服务等)
有了具象的实例,随后就可以按照XACML的框架,迭代式的完成相对应的组件。
迭代一: PEP
核心的功能在于PEP拦截和转发来自前端的请求。所以这个版本的MyABAC有两个基本功能,1: 判断请求权限,2: 转发或拒绝请求。对于拒绝请求,可以直接返回401, 对于转发请求,我们可以创建一个ProxyServer 完整的转发整个请求。但是对于判断请求权限,要怎么处理,根据什么判断呢?ABAC中的属性就是我们判断的数据源,目前我们能拿到的数据仅仅来自于请求本身或者当前环境的上下文(Context),所以我们用一段静态逻辑从请求本身获取一些属性,用一个简单的判断进行验证。为了能适应在不同的请求当中,可以这段获取请求属性的逻辑抽象成为一个组件,权且叫它为extractor
. 那么我们就有了第一个版本:
迭代二: PEP + PDP
在上一个版本中我们已经实现了MyABAC 对请求的转发,和简单的基本判断。接下来可以逐渐丰富请求的判断。按照XACML的机构,PEP仅仅负责对鉴权结果的执行,对于鉴权真正的逻辑应该在PDP中执行。所以PDP中一个很重要的组件就是决策器(Decision Maker),我们选择’Permit’和’Deny’ 代表决策的结果。这样就可以在PEP 中设置一个PDP client, 将Extractor拿到的属性传递给PDP, 然后将PDP的结果给Validation来判断是否转发请求。PDP 拿到 PEP 传递的属性值之后不再是简单的判断,它将通过一系列的规则(Rule) ,每个规则都有自己的匹配条件(Condition)和结果(Effect), 不同的规则按一定的关系进行分组,每一组我们称之为一个策略(Policy). 每一个策略里按照一定的算法(R****ule-combining Algorithm)将规则结果归结为一个结果(Effect). 至此,PDP将有两个基本部分,决策器也按照策略的组合算法(Policy-combining algorithm)将结果归结, 返回给 PEP。如下图:
在具体的实现中,PDP和PEP可以是一个系统中的不同组件直接调用,也可以部署为不同的服务,使用HTTP请求。在MyABAC为了更好的微服务化,我们将PEP和PDP部署为两个系统,使用HTTP请求。
迭代三: PEP + PDP + PAP
上个迭代中,MyABAC 有了PDP 和 策略,但是对于策略还仅仅局限在一堆代码逻辑中,如何更好的管理这些策略,如何让策略和人(即系统的管理员或运维人员)合理的管理和交互,这便是PAP的主要职责。策略和规则中有一定的的逻辑组合关系来组成相对应满足的条件,对于它的定义,XACML 有一套比较复杂的基于XML的逻辑语言,但这里我们将其简化,借鉴AWS cloudformation 的语言结构,构建了一个自己的基于YAML的配置语言,姑且称之为 MPL(MyAbac Policy Language)。将每一个策略简化成为可读性较好的yaml文件,那么PAP的职责就是使用UI或者编辑器(Editor) 对yaml文件的维护。而PDP中将会有一个编译器(MPL Compiler) 将PAP 完成的策略文件,编译成为PDP可以使用的策略。这样在以后的操作过程中,仅仅通过 PAP 就可以对策略进行改变。
有了MPL的帮助,策略文件看起来也就清晰简单了。 下图前边是XACML的策略文件, 后边是MyABAC的策略文件。
迭代四: PEP + PDP + PAP + PIP
有了PDP做鉴权,PEP执行结果,PAP管理策略,看起来似乎已经完成了,但是当真正投产的时候,就会发现仅仅依靠从请求中拿到的少量属性来做真正的鉴权是远远不够的,就拿上面图书管理系统来说,怎么知道一个管理员具体的职责在几楼哪个类别,怎么知道图书的信息?一般的请求当中仅仅只有一个图书的编号和管理员的编号,更多信息就需要到对应系统中请求,而这些能够提供更过信息来支持PDP做出合理判断的服务或者产品,我们称之为PIP。也就说 PIP 并不是 MyABAC 的内在组件,而是一个外部的第三方组件。这些服务可能是基于http Restful的接口,也可能是一个GraphQL的接口或者其他类型的接口。但是为了能够让 PDP 与这些PIP正确的调用,PIP 必须遵循 PDP 制定的协议,这样 PDP 才能合理传递个 PIP 必要的信息,从而获取想要的信息,比如通过管理员的编号或者管理员服务的楼层号码和服务的图书类型编号。这一切都封装在PDP 中的 PIP Client组件里。这样决策器(Decision Maker) 不仅拥有从PEP中extractor中的属性,而且可以通过PIP拿到更多想要的属性值,同时每个属性值的变化会及时反馈到PDP中,动态的参与了最终结果决断。从而达到了更细粒度的访问控制。同样我们也可以将PAP从MyABAC里分离出来,管理多个系统的策略。比如我们可能部署多个MyABAC来保护不同的服务,这样系统中的策略是不一样,但是可以使用同一个中心化PAP来管理多个系统的策略文件。
迭代五: 未来的计划
通过上面的迭代,一个比较完整的ABAC的MVP已经完成。由于在原始的请求中添加了额外的工作量,比如额外的请求,包括PEP到PDP的请求、PDP到PIP的请求等,要核对很多的策略,所以性能问题是MyABAC不能忽视的问题。再次迭代中将使用懒加载,缓存的机制来优化MyABAC的性能。同时此产品对XCAML的框架做了大量的裁决,只采纳一些基本概念和组件,所以要关注用户的使用和需求反馈,逐步的引入更多概念来不断的完善产品。
后记
相对于RBAC,ABAC的概念更广,实现更复杂。如果把角色(role)当作唯一的属性,那么RBAC就是一个ABAC的特列。对ABAC和RBAC的选择要基于当前业务的需求。不要将简单的问题复杂化,也不能把复杂的问题看的太过简单。MyABAC的出现,将系统中的鉴权部分完全从系统中抽离出来,让系统的设计者和开发者更加关注在具体的业务实现上。作为一个完全自管理的产品,可以以边车(sidecar)的形式部署在每个系统中,甚至可以部署在一套系统中不同层级(layer),比如BFF层,API层或数据库层。 但同时也增加了系统部署的复杂性,我们要合理的选择和取舍。
目前,MyABAC已经在公司多个系统中使用,根据用户的反馈已经引入了一些新的功能,比如对义务(Obligation)的支持等。在部署中我们将MyABAC封装称为一个docker image,这样用户就不会过多的关注它的具体实现,而仅仅通过一些简单的参数配置就可以完成对MyABAC的引入。
参考:
https://en.wikipedia.org/wiki/Attribute-based_access_control
http://docs.oasis-open.org/xacml/3.0/xacml-3.0-core-spec-os-en.html