干货:一文看懂Apache Ranger | 凌云时刻
发布日期:2021-06-30 18:31:06 浏览次数:4 分类:技术文章

本文共 28531 字,大约阅读时间需要 95 分钟。

凌云时刻 · 技术

导读:Apache Ranger来源于XA Secure公司。2013年,XA Secure在加利福尼亚成立,专门做Hadoop生态的安全管控。2014年,Hortonworks收购了XA Secure,之后将XA Secure以新项目Apache Ranger贡献给了Apache软件基金会。Ranger进入了Apache孵化器项目。2017年3月,Ranger成为Apache的顶级项目之一。本文的介绍已2.1.0版本为基准。

作者 | 初晗

来源 | 凌云时刻(微信号:linuxpk)

1. 架构

Ranger属于C/S架构。其中Server端Ranger Admin提供授权策略的管理服务。用户可通过Web UI对用户、角色、组、授权策略进行变更。同时,这些管理能力也会通过REST API对外暴露。Ranger的Client端也就是plugin插件,通过REST API与Ranger Admin进行交互,定时拉取最新的权限策略并更新到plugin的缓存仓储中。对于Hadoop生态中不同的系统,Ranger提供了不同的plugin插件,目前已经覆盖了Hive、HDFS、Yarn、HBase、Kafka、Kudu、Solr等17类系统。每个插件实现了对应系统的访问控制相关的扩展接口,在特定的逻辑处理和模型转换之后,最终会对plugin通用common层的服务进行调用,包括权限管理、用户管理、角色管理、组管理、鉴权等。其中鉴权时,会对缓存仓储中的策略进行匹配。 

在扩展性方面,集成额外的系统,只需要为其实现相应的plugin即可。

2. 权限模型

 2.1 表结构

Ranger的持久化模型和领域模型是一一对应的关系。以下按照资源(service)和权限(policy)两部分来介绍。

 2.1.1 service

(1)service def 

在Ranger中,各类资源首先会按照service def来划分。一个service def代表一个类型,比如HDFS、HBASE、KUDU、KAFKA等。 

(2)service config def 

service config def约束了某个service def所需要的配置,这些配置可以设定为必须的或者可选的,可以设置默认值等。例如HIVE类型的config包含:username、password、jdbc.driverClassName、jdbc.url等。 

(3)service 

对应service def的一个具体的实例,也就是相应service def类型下的一个具体的集群。 

(4)service config map 

service config map包含具体的service集群的配置信息,这些配置信息受service config def的约束。 

(5)service version info 

service version info记录对应service的各种版本信息。某个service下,每当policy、role或者tag发生变更,都会更新service version info中相应的版本号。而plugin定时从Ranger Admin下载policy和role时都会携带版本信息,仅在版本变化的情况下,下载最新的policy和role并更新到plugin的缓存中。 

(6)resource def 

resource def描述的是某个类型service包含哪些类型的资源。比如HIVE类型的server包含的资源类型有:database、table、udf、column等。 

(7)context enricher def 

context enricher def定义了某个类型service的enricher。在鉴权等请求发生时,通过资源可以找到对应的service def,从而匹配到对应的enricher,再通过enricher补全请求的上下文信息。例如tag enricher会为请求添加相应资源的tag信息(下文会具体介绍tag)。 

(8)datamask type def 

datamask type def定义了一些对数据进行mask处理的方式。在权限策略中,如果配置了对应类型中的某个datamask,则会在满足权限策略的前提下对数据进行mask处理。例如对于HIVE类型的某个service集群,可以配置select操作的datamask策略为MASK_DATE_SHOW_YEAR,这样在HIVE查询到数据后,会对数据中的Date进行mask处理,只显示year。 

(9)policy condition def 

policy condition def定义了某个类型的service可以使用哪些策略条件类型。例如,为solr类型的集群添加权限策略,可以设置ip-ranger条件,也就是在请求ip符合规定条件的前提下,权限策略生效。 

(10)access type def 

access type def定义了某个类型集群的资源的访问方式都有哪些。比如对于HIVE类型的集群,可以通过select、update、create、drop等方式访问资源。 

(11)access type def grants 

access type def grants表示某个access type def所隐含的能力。比如HIVE类型集群资源的all访问方式,隐含着select、update、create、drop等访问方式。 

(12)enum def 

enum def与service config def配套使用。如果某个service def的配置项定义是enum类型,则该配置项的具体enum类型必须在该service def的enum def中定义。 

(13)enum element def 

enum elemet def与service config map配套使用。对于某个具体的service集群,如果其某个配置项是enum类型,则该配置项的可枚举值必须在enum elemet def中定义。

 2.1.2 policy

(1)policy 

一个policy就是一个service下的一个权限策略。分为三种类型: 

  • 访问控制策略:描述了主体(user、role、group)在什么条件下(condition)可以怎样访问(access)什么资源(resource)。 

  • 数据隐藏策略:描述了主体(user、role、group)在什么条件下(condition)怎样访问(access)什么资源(resource)的时候进行什么样的数据隐藏(datamask)。每个类型service可以选择的datamask在上面提到的datamask type def中定义。 

  • 行级过滤策略:描述了主体(user、role、group)在什么条件下(condition)怎样访问(access)什么资源(resource)的时候进行什么样的行数据过滤。

(2)policy label 

可以为每个policy添加一个或多个标签。标签仅用于标识,不影响实际的权限管理和鉴权逻辑。在搜索策略时,可指定标签作为筛选条件。 

(3)policy item 

一个policy可以包含多条policy item。每条policy item包含除了resource之外的其他元组信息。比如访问控制策略的policy item,包含user、role、group、permission这些信息,其中user、role、group属于主体,permission就是访问方式(access type)。每个policy下所有的policy item作用的资源都是一样的。 

Ranger中,虽然定义了policy item的表结构,但数据并未持久化到policy item表中。整个policy的内容,转换成json大字段存储在policy表的policy_text字段中,包括策略作用的resource和每一条policy item的具体内容。

 2.2 授权和撤权

授权和撤权变更的都是访问控制策略。

 2.2.1 Ranger Admin

授权和撤权的一种方式是通过Ranger Admin进行,在Ranger Admin上为某个service添加、修改或删除访问控制policy。

操作的方式包括: 

  • Web UI

  • REST API 

Ranger Plugin会定时从Ranger Admin拉取最新的policy。

 2.2.2 Ranger Plugin

每个类型的plugin实现了对应系统开放的访问控制接口。通过该接口进行授权和撤权,会进入Ranger Plugin的common模块,调用RangerBasePlugin的grantAccess和revokeAccess方法进行授权和撤权:

public void grantAccess(GrantRevokeRequest request, RangerAccessResultProcessor resultProcessor);public void revokeAccess(GrantRevokeRequest request, RangerAccessResultProcessor resultProcessor);

而授权和撤权会通过REST API在Ranger Admin执行: 

  • 对于授权请求,会先检查是否已存在对应resource的访问控制类型的policy。如果存在,则将授权信息更新到policy中;如果不存在,则为service新建policy。 

  • 对于撤销权限,先查找对应resource的访问控制类型的policy。如果存在,则根据撤权请求中的use、role、group,从policy的各个policy item中提取出已有的access,再将这些access与撤权请求中的access做减法;如果不存在,则什么也不做。

 2.3 鉴权

鉴权逻辑主要在plugin中,将主体对resource的访问与plugin缓存的访问控制策略进行匹配,从而判别主体是否可以访问目标resource。 

对于访问控制policy,policy item分为4种类型:

allow:允许配置的主体对资源进行指定的访问。例如:下图中,主体为admin用户,permission为select,表示允许admin用户对资源进行select访问。

allow exception:通常配合allow使用,从allow的配置中进行排除。例如:下图中,allow条件允许public组对资源进行select访问,而allow exception条件从中排除了test用户对资源的select访问。也就是说,如果test用户属于public组,仍然不能对资源进行select操作,而public组中的其他用户则可以。

deny:拒绝访问。例如:下图中的拒绝条件配置,表示拒绝admin用户对资源进行Drop操作。

deny exception:配合deny使用,从deny的配置中进行排除。例如:下图中,deny条件表示拒绝public组对资源进行Drop操作,而deny exception条件从deny条件中排除了test用户对资源的select访问。换句话说,如果test用户属于public组,其对资源进行的Drop操作并没有被拒绝,如果存在额外的允许条件,则可以进行相应操作。

除了4种访问控制policy item类型之外,还有一个“deny all else”开关与allow条件配合使用。对于某个allow条件,如果开启了“deny all else”,则在allow条件没有匹配的情况下,直接拒绝访问。如果关闭了“deny all else”,则会继续匹配其他的policy item,有可能会匹配到其他的allow条件并允许访问。 

下图描述了具体的Ranger鉴权流程,在整个流程中,4个访问控制policy item类型与“deny all else”开关组合使用。4个policy item类型的优先级为:deny exception > deny > allow exception > allow。

3. 功能介绍

 3.1 Tag

上面介绍权限模型时,提到了tag。在Ranger中,一个tag标签代表了一个policy集合。每个service可以关联一个tag,从而间接拥有了tag的policy。在模型层面,tag是一个特殊的service类型(service def),一个具体的tag标签就是一个service实例,所以tag的policy管理方式与service的policy管理方式是相同的。

鉴权时,会先检查对应的service是否存在tag policy,如果存在,则使用tag policy进行匹配决策。如果tag policy产生了决策结果,会根据tag policy与service policy的优先级,判断是否进一步进行service policy的匹配决策。 

简单来说,通过tag标签的方式,可以简化多service之间共性policy的管理。比如,多个service的policy集合存在着相同的部分,则可以将这些相同的部分提取出来定义为一个tag,然后将这些service关联到这个tag上,这样就不需要为每个service单独维护相同的policy,对tag policy进行变更即可作用到这些service上。

 3.2 Label

Ranger允许为每个policy添加多个不同的label标签,每个label都是policy本身信息的一种标志。Ranger的policy查询接口,可以将label作为过滤条件,查询出具有指定label信息的policy。

 3.3 Audit

Ranger提供了audit审计的能力,记录资源的访问信息、各plugin的状态信息以及Web的session信息等,并且可以配置将审计信息记录在哪个介质中。目前Ranger支持5种审计存储介质:DB、HDFS、log4j、Kafka、Solr。同时,Ranger Admin Web UI可以查看已记录的审计信息,便于跟踪用户的访问。

 3.4 Security Zone

Security Zone安全域为Ranger带来了隔离管理的能力。可以将service下的resource添加到指定的zone中,也可以在service下添加指定zone的policy。鉴权时,如果目标资源属于某个zone,则使用该zone的policy进行决策,否则使用default zone的policy进行决策。一个资源只能属于一个zone,每个zone都需要单独设置管理员,从这个角度看,zone的概念类似于租户。

 3.5 Row Filter、Data Mask

Ranger支持Row Filter和Data Mask。Row Filter是指行级过滤,也可以叫做行权限,将权限管理的粒度控制到行级别。通过配置Row Filter类型的policy,可以控制主体在访问资源时,可以访问哪些行。Data Mask是指数据隐藏。通过配置Data Mask类型的policy,可以控制主体在访问资源时,对数据进行怎样的隐藏处理。 

目前Ranger仅对Hive支持Row Filter和Data Mask,因为Hive开放了Row Filter和Data Mask的扩展接口。Hive集成Ranger后,在数据查询时,会根据Ranger中配置的Row Filter和Data Mask类型的policy对查询HiveQL进行改写,查询结果中仅包含主体可访问的行,并且进行了必要的数据隐藏处理。对于Row Filter的改写,是在HiveQL中拼接where条件;对于Data Mask的改写,是在HiveQL中使用函数替换column。因此Ranger的Data Mask类型必须是Hive能够支持的函数,例如:mask、mask_hash、mask_show_first_n()等。 

可以看到,通过Row Filter和Data Mask机制,可以做到更细粒度的敏感数据保护。

4. 代码解读

在plugin层面,以Hive Plugin为例。

 4.1 授权/撤权

Hive开放的访问控制扩展接口可关注两个: 

1. org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthorizerFactory

用于生成具体的HiveAuthorizer实现。 

2. org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAuthorizer

用于权限管理、鉴权、角色管理等。

Ranger Hive Plugin的实现类分别为:

1. org.apache.ranger.authorization.hive.authorizer.RangerHiveAuthorizerFactory

在创建HiveAuthorizer时,返回RangerHiveAuthorizer实现

2. org.apache.ranger.authorization.hive.authorizer.RangerHiveAuthorizer

Ranger实现的权限管理、鉴权、角色管理等逻辑。

在调用grantPrivileges/revokePrivileges进行授权/撤权时,先将Hive模型转换为Ranger模型,然后调用RangerHivePlugin组件:

public void grantPrivileges(List
hivePrincipals, List
hivePrivileges, HivePrivilegeObject hivePrivObject, HivePrincipal grantorPrincipal, boolean grantOption) throws HiveAuthzPluginException, HiveAccessControlException { RangerHiveAuditHandler auditHandler = new RangerHiveAuditHandler(); try { List
outputs = new ArrayList<>(Arrays.asList(hivePrivObject)); RangerHiveResource resource = getHiveResource(HiveOperationType.GRANT_PRIVILEGE, hivePrivObject, null, outputs); GrantRevokeRequest request = createGrantRevokeData(resource, hivePrincipals, hivePrivileges, grantorPrincipal, grantOption); hivePlugin.grantAccess(request, auditHandler); } catch(Exception excp) { throw new HiveAccessControlException(excp); }}

RangerHivePlugin是RangerBasePlugin的子类,会在初始化阶段生成一些Hive特有的配置参数,核心逻辑就像3.2.2中提到的那样,在RangerBasePlugin公共模块中。RangerBasePlugin会将授权/撤权请求通过REST API发送到Ranger Admin进行处理:

public void grantAccess(GrantRevokeRequest request, RangerAccessResultProcessor resultProcessor) throws Exception {    boolean isSuccess = false;    try {        RangerPolicyEngine policyEngine = this.policyEngine;        if (policyEngine != null) {            request.setZoneName(policyEngine.getMatchedZoneName(request));        }        getAdminClient().grantAccess(request);        isSuccess = true;    } finally {        auditGrantRevoke(request, "grant", isSuccess, resultProcessor);    }}

对于授权,先检查是否存在对应resource的policy。如果存在,进入策略更新处理逻辑:

RangerPolicy policy = getExactMatchPolicyForResource(serviceName, resource, userName);if(policy != null) {    boolean policyUpdated = false;    policyUpdated = ServiceRESTUtil.processGrantRequest(policy, grantRequest);    if(policyUpdated) {        policy.setZoneName(grantRequest.getZoneName());        svcStore.updatePolicy(policy);    } else {        throw new Exception("processGrantRequest processing failed");    }} else {    // ...}

其中ServiceRESTUtil.processGrantRequest方法的作用是将授权请求合并到已有的policy中。根据授权请求构建一个新的policy,然后调用processApplyPolicy方法进行新、老policy的合并:

static public boolean processGrantRequest(RangerPolicy policy, GrantRevokeRequest grantRequest) {    boolean policyUpdated = false;        //Build a policy and set up policyItem in it to mimic grant request    RangerPolicy appliedPolicy = new RangerPolicy();    RangerPolicy.RangerPolicyItem policyItem = new RangerPolicy.RangerPolicyItem();    policyItem.setDelegateAdmin(grantRequest.getDelegateAdmin());    policyItem.getUsers().addAll(grantRequest.getUsers());    policyItem.getGroups().addAll(grantRequest.getGroups());    policyItem.getRoles().addAll(grantRequest.getRoles());    List
accesses = new ArrayList
(); Set
accessTypes = grantRequest.getAccessTypes(); for (String accessType : accessTypes) { accesses.add(new RangerPolicy.RangerPolicyItemAccess(accessType, true)); } policyItem.setAccesses(accesses); appliedPolicy.getPolicyItems().add(policyItem); processApplyPolicy(policy, appliedPolicy); policyUpdated = true; return policyUpdated;}static public void processApplyPolicy(RangerPolicy existingPolicy, RangerPolicy appliedPolicy) { processApplyPolicyForItemType(existingPolicy, appliedPolicy, POLICYITEM_TYPE.ALLOW); processApplyPolicyForItemType(existingPolicy, appliedPolicy, POLICYITEM_TYPE.DENY); processApplyPolicyForItemType(existingPolicy, appliedPolicy, POLICYITEM_TYPE.ALLOW_EXCEPTIONS); processApplyPolicyForItemType(existingPolicy, appliedPolicy, POLICYITEM_TYPE.DENY_EXCEPTIONS);}

processApplyPolicyForItemType是合并逻辑的核心处理方法。处理过程包括以下几个步骤:

  1. 从新policy中,按照user、role、group的维度,分离出各自的policy item

  2. 从老policy中,分离出新policy中user、role、group对应的policy item

  3. 将(1)和(2)中的新、老polity item按照user、role、group的维度进行合并

  4. 将(3)中合并的结果添加回老policy中

static private void processApplyPolicyForItemType(RangerPolicy existingPolicy, RangerPolicy appliedPolicy, POLICYITEM_TYPE policyItemType) {    List
appliedPolicyItems = null; switch (policyItemType) { case ALLOW: appliedPolicyItems = appliedPolicy.getPolicyItems(); break; case DENY: appliedPolicyItems = appliedPolicy.getDenyPolicyItems(); break; case ALLOW_EXCEPTIONS: appliedPolicyItems = appliedPolicy.getAllowExceptions(); break; case DENY_EXCEPTIONS: appliedPolicyItems = appliedPolicy.getDenyExceptions(); break; default: LOG.warn("processApplyPolicyForItemType(): invalid policyItemType=" + policyItemType); } if (CollectionUtils.isNotEmpty(appliedPolicyItems)) { Set
users = new HashSet
(); Set
groups = new HashSet
(); Set
roles = new HashSet
(); Map
userPolicyItems = new HashMap
(); Map
groupPolicyItems = new HashMap
(); Map
rolePolicyItems = new HashMap
(); // Extract users, groups, and roles specified in appliedPolicy items extractUsersGroupsAndRoles(appliedPolicyItems, users, groups, roles); // Split existing policyItems for users, groups, and roles extracted from appliedPolicyItem into userPolicyItems, groupPolicyItems, and rolePolicyItems splitExistingPolicyItems(existingPolicy, users, userPolicyItems, groups, groupPolicyItems, roles, rolePolicyItems); // Apply policyItems of given type in appliedPolicy to policyItems extracted from existingPolicy applyPolicyItems(appliedPolicyItems, policyItemType, userPolicyItems, groupPolicyItems, rolePolicyItems); // Add modified/new policyItems back to existing policy mergeProcessedPolicyItems(existingPolicy, userPolicyItems, groupPolicyItems, rolePolicyItems); compactPolicy(existingPolicy); }}

如果授权请求中的resource对应的policy不存在,则为service新建policy:

RangerPolicy policy = getExactMatchPolicyForResource(serviceName, resource, userName);if(policy != null) {    // ...} else {    policy = new RangerPolicy();    policy.setService(serviceName);    policy.setName("grant-" + System.currentTimeMillis()); // TODO: better policy name    policy.setDescription("created by grant");    policy.setIsAuditEnabled(grantRequest.getEnableAudit());    policy.setCreatedBy(userName);        Map
policyResources = new HashMap
(); Set
resourceNames = resource.getKeys(); if(! CollectionUtils.isEmpty(resourceNames)) { for(String resourceName : resourceNames) { RangerPolicyResource policyResource = new RangerPolicyResource((String) resource.getValue(resourceName)); policyResource.setIsRecursive(grantRequest.getIsRecursive()); policyResources.put(resourceName, policyResource); } } policy.setResources(policyResources); RangerPolicyItem policyItem = new RangerPolicyItem(); policyItem.setDelegateAdmin(grantRequest.getDelegateAdmin()); policyItem.getUsers().addAll(grantRequest.getUsers()); policyItem.getGroups().addAll(grantRequest.getGroups()); policyItem.getRoles().addAll(grantRequest.getRoles()); for(String accessType : grantRequest.getAccessTypes()) { policyItem.getAccesses().add(new RangerPolicyItemAccess(accessType, Boolean.TRUE)); } policy.getPolicyItems().add(policyItem); policy.setZoneName(grantRequest.getZoneName()); svcStore.createPolicy(policy);}

对于撤权,同样会先检查是否存在对应resource的policy。如果不存在,则什么也不做。如果存在,则进入策略更新处理逻辑:

RangerPolicy policy = getExactMatchPolicyForResource(serviceName, resource, userName);if(policy != null) {    boolean policyUpdated = false;    policyUpdated = ServiceRESTUtil.processRevokeRequest(policy, revokeRequest);    if(policyUpdated) {        policy.setZoneName(revokeRequest.getZoneName());        svcStore.updatePolicy(policy);    } else {        throw new Exception("processRevokeRequest processing failed");    }}

其中ServiceRESTUtil.processRevokeRequest的作用是从已有policy中将撤权请求相关的内容删除,删除过程包括以下几个步骤:

  1. 根据授权请求构造新的policy

  2. 从新policy中,按照user、role、group的维度,分离出各自的policy item

  3. 从老policy中,分离出新policy中user、role、group对应的policy item

  4. 按照user、role、group维度,从(2)中老policy item中,删除(1)中新policy item的access

  5. 将(4)中删除的结果合并回老policy中

static public boolean processRevokeRequest(RangerPolicy existingRangerPolicy, GrantRevokeRequest revokeRequest) {    boolean policyUpdated = false;    //Build a policy and set up policyItem in it to mimic revoke request    RangerPolicy appliedRangerPolicy = new RangerPolicy();    RangerPolicy.RangerPolicyItem appliedRangerPolicyItem = new RangerPolicy.RangerPolicyItem();    appliedRangerPolicyItem.setDelegateAdmin(revokeRequest.getDelegateAdmin());    appliedRangerPolicyItem.getUsers().addAll(revokeRequest.getUsers());    appliedRangerPolicyItem.getGroups().addAll(revokeRequest.getGroups());    appliedRangerPolicyItem.getRoles().addAll(revokeRequest.getRoles());    List
appliedRangerPolicyItemAccess = new ArrayList
(); Set
appliedPolicyItemAccessType = revokeRequest.getAccessTypes(); for (String accessType : appliedPolicyItemAccessType) { appliedRangerPolicyItemAccess.add(new RangerPolicy.RangerPolicyItemAccess(accessType, false)); } appliedRangerPolicyItem.setAccesses(appliedRangerPolicyItemAccess); appliedRangerPolicy.getPolicyItems().add(appliedRangerPolicyItem); List
appliedRangerPolicyItems = appliedRangerPolicy.getPolicyItems(); if (CollectionUtils.isNotEmpty(appliedRangerPolicyItems)) { Set
users = new HashSet
(); Set
groups = new HashSet
(); Set
roles = new HashSet<>(); Map
userPolicyItems = new HashMap
(); Map
groupPolicyItems = new HashMap
(); Map
rolePolicyItems = new HashMap
(); // Extract users, groups, and roles specified in appliedPolicy items extractUsersGroupsAndRoles(appliedRangerPolicyItems, users, groups, roles); // Split existing policyItems for users, groups, and roles extracted from appliedPolicyItem into userPolicyItems, groupPolicyItems and rolePolicyItems splitExistingPolicyItems(existingRangerPolicy, users, userPolicyItems, groups, groupPolicyItems, roles, rolePolicyItems); for (RangerPolicy.RangerPolicyItem tempPolicyItem : appliedRangerPolicyItems) { List
appliedPolicyItemsUser = tempPolicyItem.getUsers(); for (String user : appliedPolicyItemsUser) { RangerPolicy.RangerPolicyItem[] rangerPolicyItems = userPolicyItems.get(user); if(rangerPolicyItems!=null && rangerPolicyItems.length>0){ if(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()], tempPolicyItem.getAccesses()); if(!CollectionUtils.isEmpty(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].getAccesses())){ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(revokeRequest.getDelegateAdmin()); }else{ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(Boolean.FALSE); } } if(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()], tempPolicyItem.getAccesses()); rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()].setDelegateAdmin(Boolean.FALSE); } } } } for (RangerPolicy.RangerPolicyItem tempPolicyItem : appliedRangerPolicyItems) { List
appliedPolicyItemsGroup = tempPolicyItem.getGroups(); for (String group : appliedPolicyItemsGroup) { RangerPolicy.RangerPolicyItem[] rangerPolicyItems = groupPolicyItems.get(group); if(rangerPolicyItems!=null && rangerPolicyItems.length>0){ if(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()], tempPolicyItem.getAccesses()); if(!CollectionUtils.isEmpty(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].getAccesses())){ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(revokeRequest.getDelegateAdmin()); }else{ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(Boolean.FALSE); } } if(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()], tempPolicyItem.getAccesses()); rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()].setDelegateAdmin(Boolean.FALSE); } } } } for (RangerPolicy.RangerPolicyItem tempPolicyItem : appliedRangerPolicyItems) { List
appliedPolicyItemsRole = tempPolicyItem.getRoles(); for (String role : appliedPolicyItemsRole) { RangerPolicy.RangerPolicyItem[] rangerPolicyItems = rolePolicyItems.get(role); if(rangerPolicyItems!=null && rangerPolicyItems.length>0){ if(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()], tempPolicyItem.getAccesses()); if(!CollectionUtils.isEmpty(rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].getAccesses())){ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(revokeRequest.getDelegateAdmin()); }else{ rangerPolicyItems[POLICYITEM_TYPE.ALLOW.ordinal()].setDelegateAdmin(Boolean.FALSE); } } if(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()]!=null){ removeAccesses(rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()], tempPolicyItem.getAccesses()); rangerPolicyItems[POLICYITEM_TYPE.DENY_EXCEPTIONS.ordinal()].setDelegateAdmin(Boolean.FALSE); } } } } // Add modified/new policyItems back to existing policy mergeProcessedPolicyItems(existingRangerPolicy, userPolicyItems, groupPolicyItems, rolePolicyItems); compactPolicy(existingRangerPolicy); } policyUpdated = true; return policyUpdated;}

 4.2 鉴权

对于Ranger Hive Plugin来说,鉴权会调用RangerHiveAuthorizer的checkPrivileges方法。

RangerHiveAuthorizer.checkPrivileges在模型转化那之后,调用RangerBasePlugin的isAccessAllowed方法进入策略匹配逻辑:

public RangerAccessResult isAccessAllowed(RangerAccessRequest request, RangerAccessResultProcessor resultProcessor) {    RangerPolicyEngine policyEngine = this.policyEngine;    if(policyEngine != null) {        return policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ACCESS, resultProcessor);    }    return null;}

策略匹配由RangerPolicyEngine负责,通过evaluatePolicies方法,评估访问请求是否被允许。在评估过程中,如果service有tag policy,会先评估tag policy。之后再遍历service policy,根据tag policy和service policy的优先级,决定是使用tag policy的决策结果还是继续进行service policy的评估。如果service没有tag policy,则直接遍历service policy进行评估:

public RangerAccessResult evaluatePolicies(RangerAccessRequest request, int policyType, RangerAccessResultProcessor resultProcessor) {    requestProcessor.preProcess(request);    RangerAccessResult ret = zoneAwareAccessEvaluationWithNoAudit(request, policyType);    if (resultProcessor != null) {        resultProcessor.processResult(ret);    }    return ret;}private RangerAccessResult zoneAwareAccessEvaluationWithNoAudit(RangerAccessRequest request, int policyType) {    RangerAccessResult     ret                 = null;    RangerPolicyRepository policyRepository    = policyEngine.getPolicyRepository();    RangerPolicyRepository tagPolicyRepository = policyEngine.getTagPolicyRepository();    String                 zoneName            = policyEngine.getMatchedZoneName(request.getResource()); // Evaluate zone-name from request    if (StringUtils.isNotEmpty(zoneName)) {        policyRepository = policyEngine.getZonePolicyRepositories().get(zoneName);        if (policyRepository == null) {            LOG.error("policyRepository for zoneName:[" + zoneName + "],  serviceName:[" + policyEngine.getPolicyRepository().getServiceName() + "], policyVersion:[" + getPolicyVersion() + "] is null!! ERROR!");        }    }    if (policyRepository != null) {        ret = evaluatePoliciesNoAudit(request, policyType, zoneName, policyRepository, tagPolicyRepository);        ret.setZoneName(zoneName);    }    return ret;}private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, int policyType, String zoneName, RangerPolicyRepository policyRepository, RangerPolicyRepository tagPolicyRepository) {    final Date               accessTime  = request.getAccessTime() != null ? request.getAccessTime() : new Date();    final RangerAccessResult ret         = createAccessResult(request, policyType);    evaluateTagPolicies(request, policyType, zoneName, tagPolicyRepository, ret);    boolean isAllowedByTags          = ret.getIsAccessDetermined() && ret.getIsAllowed();    boolean isDeniedByTags           = ret.getIsAccessDetermined() && !ret.getIsAllowed();    boolean evaluateResourcePolicies = policyEngine.hasResourcePolicies(policyRepository);    if (evaluateResourcePolicies) {        boolean findAuditByResource = !ret.getIsAuditedDetermined();        boolean foundInCache        = findAuditByResource && policyRepository.setAuditEnabledFromCache(request, ret);        List
evaluators = policyRepository.getLikelyMatchPolicyEvaluators(request.getResource(), policyType); for (RangerPolicyEvaluator evaluator : evaluators) { if (!evaluator.isApplicable(accessTime)) { continue; } if (isDeniedByTags) { if (ret.getPolicyPriority() >= evaluator.getPolicyPriority()) { ret.setIsAccessDetermined(true); } } else if (isAllowedByTags) { if (ret.getPolicyPriority() > evaluator.getPolicyPriority()) { ret.setIsAccessDetermined(true); } } ret.incrementEvaluatedPoliciesCount(); evaluator.evaluate(request, ret); if (ret.getIsAllowed()) { if (!evaluator.hasDeny()) { // No more deny policies left ret.setIsAccessDetermined(true); } } if (ret.getIsAuditedDetermined() && ret.getIsAccessDetermined()) { break; // Break out of policy-evaluation loop } } if (!ret.getIsAccessDetermined()) { if (isDeniedByTags) { ret.setIsAllowed(false); } else if (isAllowedByTags) { ret.setIsAllowed(true); } } if (ret.getIsAllowed()) { ret.setIsAccessDetermined(true); } if (findAuditByResource && !foundInCache) { policyRepository.storeAuditEnabledInCache(request, ret); } } return ret;}

Ranger会为每个policy维护一个RangerPolicyEvaluator, 在RangerPolicyEvaluator中评估policy对访问请求的决策结果。而policy的评估,又是通过匹配policy item进行的。RangerDefaultPolicyEvaluator.getMatchingPolicyItem中的policy item匹配逻辑,体现了3.3中描述的优先级关系:

public void evaluate(RangerAccessRequest request, RangerAccessResult result) {    if (request != null && result != null) {        if (!result.getIsAccessDetermined() || !result.getIsAuditedDetermined()) {            //Evaluate Policy Level Custom Conditions, if any and allowed then go ahead for policyItem level evaluation            if(matchPolicyCustomConditions(request)) {                if (!result.getIsAuditedDetermined()) {                    if (isAuditEnabled()) {                        result.setIsAudited(true);                        result.setAuditPolicyId(getPolicy().getId());                    }                }                if (!result.getIsAccessDetermined()) {                    if (hasMatchablePolicyItem(request)) {                        evaluatePolicyItems(request, matchType, result);                    }                }            }        }    }}protected void evaluatePolicyItems(RangerAccessRequest request, RangerPolicyResourceMatcher.MatchType matchType, RangerAccessResult result) {    RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(request, result);    if (matchedPolicyItem != null) {        matchedPolicyItem.updateAccessResult(this, result, matchType);    } else if (getPolicy().getIsDenyAllElse() && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS) && !request.isAccessTypeAny()) {        updateAccessResult(result, RangerPolicyResourceMatcher.MatchType.NONE, false, "matched deny-all-else policy");    }}protected RangerPolicyItemEvaluator getMatchingPolicyItem(RangerAccessRequest request, RangerAccessResult result) {    RangerPolicyItemEvaluator ret = null;    Integer policyType = getPolicy().getPolicyType();    if (policyType == null) {        policyType = RangerPolicy.POLICY_TYPE_ACCESS;    }    switch (policyType) {        case RangerPolicy.POLICY_TYPE_ACCESS: {            ret = getMatchingPolicyItem(request, denyEvaluators, denyExceptionEvaluators);            if(ret == null && !result.getIsAccessDetermined()) { // a deny policy could have set isAllowed=true, but in such case it wouldn't set isAccessDetermined=true                ret = getMatchingPolicyItem(request, allowEvaluators, allowExceptionEvaluators);            }            break;        }        case RangerPolicy.POLICY_TYPE_DATAMASK: {            ret = getMatchingPolicyItem(request, dataMaskEvaluators);            break;        }        case RangerPolicy.POLICY_TYPE_ROWFILTER: {            ret = getMatchingPolicyItem(request, rowFilterEvaluators);            break;        }        default:            break;    }    return ret;}

END

往期精彩文章回顾

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

转载地址:https://lingyun.blog.csdn.net/article/details/107036641 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:吴翰清的科技史观:有变革的需求,才有技术的诞生
下一篇:Kafka从上手到实践 - 初步认知:MQ系统 | 凌云时刻

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月23日 13时17分47秒