干货:一文看懂Apache Ranger | 凌云时刻

Posted 凌云时刻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了干货:一文看懂Apache Ranger | 凌云时刻相关的知识,希望对你有一定的参考价值。


凌云时刻 · 技术

干货:一文看懂Apache Ranger | 凌云时刻


干货:一文看懂Apache Ranger | 凌云时刻


导读: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. 架构
干货:一文看懂Apache Ranger | 凌云时刻


干货:一文看懂Apache Ranger | 凌云时刻


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. 权限模型
干货:一文看懂Apache Ranger | 凌云时刻


干货:一文看懂Apache Ranger | 凌云时刻 2.1 表结构


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


干货:一文看懂Apache Ranger | 凌云时刻 2.1.1 service


干货:一文看懂Apache Ranger | 凌云时刻


(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中定义。


干货:一文看懂Apache Ranger | 凌云时刻 2.1.2 policy


干货:一文看懂Apache Ranger | 凌云时刻


(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的具体内容。



干货:一文看懂Apache Ranger | 凌云时刻 2.2 授权和撤权


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


干货:一文看懂Apache Ranger | 凌云时刻 2.2.1 Ranger Admin


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


操作的方式包括: 


  • Web UI
  • REST API 


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


干货:一文看懂Apache Ranger | 凌云时刻 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做减法;如果不存在,则什么也不做。


干货:一文看懂Apache Ranger | 凌云时刻 2.3 鉴权


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


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


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

干货:一文看懂Apache Ranger | 凌云时刻


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

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

干货:一文看懂Apache Ranger | 凌云时刻


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

干货:一文看懂Apache Ranger | 凌云时刻


除了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。


干货:一文看懂Apache Ranger | 凌云时刻



3. 功能介绍
干货:一文看懂Apache Ranger | 凌云时刻


干货:一文看懂Apache Ranger | 凌云时刻 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上。



干货:一文看懂Apache Ranger | 凌云时刻 3.2 Label


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



干货:一文看懂Apache Ranger | 凌云时刻 3.3 Audit


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



干货:一文看懂Apache Ranger | 凌云时刻 3.4 Security Zone


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



干货:一文看懂Apache Ranger | 凌云时刻 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. 代码解读
干货:一文看懂Apache Ranger | 凌云时刻


在plugin层面,以Hive Plugin为例。



干货:一文看懂Apache Ranger | 凌云时刻 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<HivePrincipal> hivePrincipals, List<HivePrivilege> hivePrivileges, HivePrivilegeObject hivePrivObject, HivePrincipal grantorPrincipal, boolean grantOption) throws HiveAuthzPluginException, HiveAccessControlException { RangerHiveAuditHandler auditHandler = new RangerHiveAuditHandler(); try { List<HivePrivilegeObject> 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<RangerPolicy.RangerPolicyItemAccess> accesses = new ArrayList<RangerPolicy.RangerPolicyItemAccess>(); Set<String> 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<RangerPolicy.RangerPolicyItem> 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<String> users = new HashSet<String>(); Set<String> groups = new HashSet<String>(); Set<String> roles = new HashSet<String>();
Map<String, RangerPolicy.RangerPolicyItem[]> userPolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>(); Map<String, RangerPolicy.RangerPolicyItem[]> groupPolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>(); Map<String, RangerPolicy.RangerPolicyItem[]> rolePolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>();
// 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<String, RangerPolicyResource> policyResources = new HashMap<String, RangerPolicyResource>(); Set<String> 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<RangerPolicy.RangerPolicyItemAccess> appliedRangerPolicyItemAccess = new ArrayList<RangerPolicy.RangerPolicyItemAccess>(); Set<String> appliedPolicyItemAccessType = revokeRequest.getAccessTypes(); for (String accessType : appliedPolicyItemAccessType) { appliedRangerPolicyItemAccess.add(new RangerPolicy.RangerPolicyItemAccess(accessType, false)); } appliedRangerPolicyItem.setAccesses(appliedRangerPolicyItemAccess); appliedRangerPolicy.getPolicyItems().add(appliedRangerPolicyItem);
List<RangerPolicy.RangerPolicyItem> appliedRangerPolicyItems = appliedRangerPolicy.getPolicyItems(); if (CollectionUtils.isNotEmpty(appliedRangerPolicyItems)) { Set<String> users = new HashSet<String>(); Set<String> groups = new HashSet<String>(); Set<String> roles = new HashSet<>();
Map<String, RangerPolicy.RangerPolicyItem[]> userPolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>(); Map<String, RangerPolicy.RangerPolicyItem[]> groupPolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>(); Map<String, RangerPolicy.RangerPolicyItem[]> rolePolicyItems = new HashMap<String, RangerPolicy.RangerPolicyItem[]>();
// 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<String> 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<String> 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<String> 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;}



干货:一文看懂Apache Ranger | 凌云时刻 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<RangerPolicyEvaluator> 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

干货:一文看懂Apache Ranger | 凌云时刻



往期精彩文章回顾


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

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


以上是关于干货:一文看懂Apache Ranger | 凌云时刻的主要内容,如果未能解决你的问题,请参考以下文章

干货来了,一文看懂DBA 与 PhD EMBA后 EMBA的区别

干货:一文看懂编程中的基本数据结构与算法思想

一文看懂TomcatNginx和Apache的区别

干货分享一文看懂Web服务器应用服务器Web容器反向代理服务器区别与联系

逐句深扒 Apache 许可协议原文,一文看懂!

一文看懂Nginx正向/反向代理原理及应用场景!