干货:一文看懂Apache Ranger | 凌云时刻
凌云时刻 ·技术导读:Apache Ranger来源于XA Secure公司。2013年,XA Secure在加利福尼亚成立,专门做Hadoop生态的安全管控。2014年,Horton...
凌云时刻 · 技术
导读: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<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是合并逻辑的核心处理方法。处理过程包括以下几个步骤:
从新policy中,按照user、role、group的维度,分离出各自的policy item
从老policy中,分离出新policy中user、role、group对应的policy item
将(1)和(2)中的新、老polity item按照user、role、group的维度进行合并
将(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中将撤权请求相关的内容删除,删除过程包括以下几个步骤:
根据授权请求构造新的policy
从新policy中,按照user、role、group的维度,分离出各自的policy item
从老policy中,分离出新policy中user、role、group对应的policy item
按照user、role、group维度,从(2)中老policy item中,删除(1)中新policy item的access
将(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;
}
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
往期精彩文章回顾
长按扫描二维码关注凌云时刻
每日收获前沿技术与科技洞见
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)