ranger 策略评估
原来ranger在plugin里构建的是一颗trie树, 根据request请求体里的访问资源, 快速查找匹配的policy权限定义, 然后判断是否有权限.
这算是少见的能看到leetcode里代码有应用的场景, trie树速度还是挺快的.
过了一遍isAccessAllowed的权限判断流程, 一个请求是否有权限, 判断的链路实在太长了...
public RangerAccessResult isAccessAllowed(RangerAccessRequest request, RangerAccessResultProcessor resultProcessor)
不过request里, 怎么知道当前用户在哪个group里呢?
upate: 一番调研, 发现hdfs在发起请求的时候, 从用户信息里获取相关联的所有user group信息, 直接发起了包含用户和用户组的权限鉴定请求. 因此ranger侧不用关注用户与用户组之间的关系, 专注 policy evaluation即可. 至于hdfs的用户组信息, 如果hdfs配置了ldap, 会自动给从ldap获取并保存. 这时候神奇的事情出现了, ranger里甚至都不需要维持用户和用户组的关系, 只要有个组名即可. 当ranger的ldap与hdfs的ldap是同一个的时候, 一切就都联动起来了.
管理员快速判断
如果用户是配置文件里的管理员, 快速通过, 不用走漫长的策略判断.
审计日志也有对应的配置, 对于某些用户的操作, 可以配置不记录审计.
final boolean isSuperUser = isSuperUser(request.getUser(), request.getUserGroups());
final Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date();
final RangerAccessResult ret = createAccessResult(request, policyType);
trie树策略搜索
public static <T extends RangerResourceEvaluator> Collection<T> getEvaluators(Map<String, RangerResourceTrie<T>> resourceTrie, Map<String, ?> resource, Map<String, ResourceElementMatchingScope> scopes) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> RangerPolicyResourceEvaluatorsRetriever.getEvaluators(" + resource + ")");
}
Set<T> ret = null;
if (scopes == null) {
scopes = Collections.emptyMap();
}
if (MapUtils.isNotEmpty(resourceTrie) && MapUtils.isNotEmpty(resource)) {
Set<String> resourceKeys = resource.keySet();
List<Evaluators<T>> sortedEvaluators = new ArrayList<>(resourceKeys.size());
for (String resourceDefName : resourceKeys) {
RangerResourceTrie<T> trie = resourceTrie.get(resourceDefName);
if (trie == null) {
continue;
}
Object resourceValues = resource.get(resourceDefName);
Set<T> inheritedMatchers = trie.getInheritedEvaluators();
Set<T> matchersForResource = trie.getEvaluatorsForResource(resourceValues, scopes.get(resourceDefName));
if (LOG.isDebugEnabled()) {
LOG.debug("ResourceDefName:[" + resourceDefName + "], values:[" + resourceValues + "], resource-matchers:[" + matchersForResource + "], inherited-matchers:[" + inheritedMatchers + "]");
}
if (CollectionUtils.isEmpty(inheritedMatchers) && CollectionUtils.isEmpty(matchersForResource)) {
sortedEvaluators.clear();
break;
}
sortedEvaluators.add(new Evaluators<>(inheritedMatchers, matchersForResource));
}
if (CollectionUtils.isNotEmpty(sortedEvaluators)) {
Collections.sort(sortedEvaluators);
ret = sortedEvaluators.remove(0).getMatchers();
for (Evaluators<T> evaluators : sortedEvaluators) {
if (CollectionUtils.isEmpty(evaluators.inheritedMatchers)) {
ret.retainAll(evaluators.resourceMatchers);
} else if (CollectionUtils.isEmpty(evaluators.resourceMatchers)) {
ret.retainAll(evaluators.inheritedMatchers);
} else {
Set<T> smaller = evaluators.getSmaller();
Set<T> bigger = evaluators.getBigger();
Set<T> tmp = new HashSet<>(ret.size());
if (ret.size() < smaller.size()) {
ret.stream().filter(smaller::contains).forEach(tmp::add);
ret.stream().filter(bigger::contains).forEach(tmp::add);
} else {
smaller.stream().filter(ret::contains).forEach(tmp::add);
if (ret.size() < bigger.size()) {
ret.stream().filter(bigger::contains).forEach(tmp::add);
} else {
bigger.stream().filter(ret::contains).forEach(tmp::add);
}
}
ret = tmp;
}
if (ret.isEmpty()) {
break;
}
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== RangerResourceEvaluatorsRetriever.getEvaluators(" + resource + ") : evaluator:[" + ret + "]");
}
return ret;
}
private List<RangerPolicyEvaluator> getLikelyMatchPolicyEvaluators(Map<String, RangerResourceTrie<RangerPolicyResourceEvaluator>> resourceTrie, RangerAccessRequest request) {
List<RangerPolicyEvaluator> ret = Collections.EMPTY_LIST;
RangerAccessResource resource = request.getResource();
RangerPerfTracer perf = null;
if(RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_OP_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerPolicyRepository.getLikelyMatchEvaluators(resource=" + resource.getAsString() + ")");
}
Collection<RangerPolicyResourceEvaluator> smallestList = RangerResourceEvaluatorsRetriever.getEvaluators(resourceTrie, resource.getAsMap(), request.getResourceElementMatchingScopes());
if (smallestList != null) {
if (smallestList.size() == 0) {
ret = new ArrayList<>();
} else if (smallestList.size() == 1) {
ret = new ArrayList<>(1);
for (RangerPolicyResourceEvaluator resourceEvaluator : smallestList) {
RangerPolicyEvaluator policyEvaluator = resourceEvaluator.getPolicyEvaluator();
ret.add(policyEvaluator);
}
} else {
ret = new ArrayList<>(smallestList.size());
Set<Long> policyIds = new HashSet<>();
for (RangerPolicyResourceEvaluator resourceEvaluator : smallestList) {
RangerPolicyEvaluator policyEvaluator = resourceEvaluator.getPolicyEvaluator();
if (policyIds.add(policyEvaluator.getPolicyId())) {
ret.add(policyEvaluator);
}
}
ret.sort(RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR);
}
}
RangerPerfTracer.logAlways(perf);
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerPolicyRepository.getLikelyMatchPolicyEvaluators(" + resource.getAsString() + "): evaluatorCount=" + ret.size());
}
return ret;
}
policy item的判断
protected void evaluatePolicyItems(RangerAccessRequest request, RangerPolicyResourceMatcher.MatchType matchType, RangerAccessResult result) {
if(LOG.isDebugEnabled()) {
LOG.debug("==> RangerDefaultPolicyEvaluator.evaluatePolicyItems(" + request + ", " + result + ", " + matchType + ")");
}
if (useAclSummaryForEvaluation && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Using ACL Summary for access evaluation. PolicyId=[" + getPolicyId() + "]");
}
Integer accessResult = null;
if (request.isAccessTypeAny() || RangerAccessRequestUtil.getIsAnyAccessInContext(request.getContext())) {
accessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), RangerPolicyEngine.ANY_ACCESS);
} else {
Set<String> allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
if (CollectionUtils.isNotEmpty(allRequestedAccesses)) {
for (String accessType : allRequestedAccesses) {
accessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), accessType);
if (accessResult == null) {
break;
}
}
} else {
accessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), request.getAccessType());
}
}
if (accessResult != null) {
updateAccessResult(result, matchType, accessResult.equals(RangerPolicyEvaluator.ACCESS_ALLOWED), null);
} else if (getPolicy().getIsDenyAllElse()) {
updateAccessResult(result, matchType, false, "matched deny-all-else policy");
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Using policyItemEvaluators for access evaluation. PolicyId=[" + getPolicyId() + "]");
}
Set<String> allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request);
if (CollectionUtils.isNotEmpty(allRequestedAccesses)) {
RangerAccessResult denyResult = null;
RangerAccessResult allowResult = null;
boolean noResult = false;
for (String accessType : allRequestedAccesses) {
if (LOG.isDebugEnabled()) {
LOG.debug("Checking for accessType:[" + accessType + "]");
}
RangerAccessRequestWrapper oneRequest = new RangerAccessRequestWrapper(request, accessType);
RangerAccessResult oneResult = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), oneRequest);
oneResult.setAuditResultFrom(result);
RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(oneRequest, oneResult);
if (matchedPolicyItem != null) {
matchedPolicyItem.updateAccessResult(this, oneResult, matchType);
} else if (getPolicy().getIsDenyAllElse() && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) {
updateAccessResult(oneResult, matchType, false, "matched deny-all-else policy");
}
if (request.isAccessTypeAny() || RangerAccessRequestUtil.getIsAnyAccessInContext(request.getContext())) {
// Implement OR logic
if (oneResult.getIsAllowed()) {
allowResult = oneResult;
denyResult = null;
break;
} else if (oneResult.getIsAccessDetermined()) {
if (!noResult) {
if (denyResult == null) {
denyResult = oneResult;
}
}
} else {
noResult = true;
denyResult = null;
}
} else {
// Implement AND logic
if (oneResult.getIsAccessDetermined() && !oneResult.getIsAllowed()) {
denyResult = oneResult;
allowResult = null;
break;
} else if (oneResult.getIsAllowed()) {
allowResult = noResult ? null : oneResult;
} else {
noResult = true;
allowResult = null;
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("allowResult:[" + allowResult + "], denyResult:[" + denyResult + "], noResult:[" + noResult + "]");
}
if (allowResult != null) {
if (!result.getIsAllowed() || result.getPolicyPriority() < allowResult.getPolicyPriority()) {
result.setAccessResultFrom(allowResult);
}
} else if (denyResult != null) {
result.setAccessResultFrom(denyResult);
}
} else {
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)) {
updateAccessResult(result, matchType, false, "matched deny-all-else policy");
}
}
}
if(LOG.isDebugEnabled()) {
LOG.debug("<== RangerDefaultPolicyEvaluator.evaluatePolicyItems(" + request + ", " + result + ", " + matchType + ")");
}
}