Apache Ranger插件的美丽简洁

如果您在这里,您已经知道什么是Apache Ranger 。 它是管理Hadoop框架中安全性的最流行(即使不是唯一)的方法。 它与Active Directory,Kerberos和其他各种身份验证集成在一起,但是我认为最有趣的功能是其授权支持。 作为Hadoop生态系统的一部分,人们会对它对Hadoop生态系统中的大多数框架(Hive,HBase,HDFS等)具有内建的支持(通过插件)感到惊讶,但是,我发现旋转它实际上非常容易自己的游侠自定义插件。

这篇文章将重点介绍Ranger插件中设计的简单性,并展示为自己构建一个插件有多么容易。 作为示例,我们将构建一个Ranger插件,用于管理对使用Akka HTTP编写的简单HTTP服务的访问。

Note : You are not required to know about Akka HTTP to follow this post. All you needed to know is that Akka HTTP is just a way (albeit, a great way) to build HTTP services

这篇文章后面的代码分为两个存储库:

  1. Ranger HTTP插件
  2. 护林员托管的Akka HTTP服务

写一个插件

为了重申我们在这里试图做的事情,我们将编写一个REST服务,并让Ranger管理它的授权。

编写Ranger插件实际上是两部分的问题–编写服务器端组件应用程序端组件

  1. 服务器端组件是驻留在Ranger端的代码/配置。
  2. 应用程序端组件是驻留在我们的REST服务中的代码,该代码调用Ranger服务并检查应用程序的最终用户是否有权访问他所请求的资源。

我们将详细研究这两件事。 让我们尝试首先编写服务器端组件。

1.服务器端组件:

作为启发,如果我们打开Ranger代码库 ,我们可以看到一些内置插件。

Apache Ranger插件

如图所示,在Ranger代码库中,我们有许多插件,我们想添加自己的插件。

Apache Ranger插件

放大上图,插件上的服务器端组件将意味着编写一个

  1. servicedef配置
  2. 继承RangerBaseService的类

因此,实际上需要在服务器端实现“一个”配置和“一个”类。

Apache Ranger插件
1. SERVICEDEF配置

让我们看一下Hive的servicedef配置:

Apache Ranger插件

我认为,我们在这里谈论三件事:

A.资源:

在Hive示例中,对于Kafka,我们要保护的“资源”是数据库 ,对于HDFS,我们要保护的“资源”是Kafka 主题 ,它将是文件路径 。 对于我们的HTTP服务,我们试图保护的资源是REST slug 。 我们称之为“路径”。

"resources": [
    {
      "itemId": 1,
      "name": "path",
      "type": "path",
      "level": 10,
      "parent": "",
      "mandatory": true,
      "lookupSupported": true,
      "recursiveSupported": true,
      "excludesSupported": true,
      "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher",
      "matcherOptions": {
        "wildCard": true,
        "ignoreCase": true
      },
      "validationRegEx": "",
      "validationMessage": "",
      "uiHint": "",
      "label": "HTTP Path",
      "description": "HTTP Path"
    }
访问类型:

访问类型只是意味着用户需要的访问类型–例如,对于Hive来说, selectcreatedelete就是示例。 对于HDFS, readwriteexecute是示例。 对于Kafka, 发布使用 。 对于我们的HTTP服务,访问类型将为HTTP方法– GETPOSTDELETE

"accessTypes": [
    {
      "itemId": 1,
      "name": "get",
      "label": "get"
    },
    {
      "itemId": 2,
      "name": "post",
      "label": "post"
    },
    {
      "itemId": 3,
      "name": "delete",
      "label": "delete"
    }
  ]
C.配置:

我们知道Ranger可以管理多个Kakfa主题,HDFS和HBase群集的安全性。 每个服务都将在不同的主机中运行,并且对每个服务进行身份验证的方式将有所不同。 捕获此信息的地方将是此configs部分。 为了简化本示例,我们不关心HTTP服务的身份验证。 因此,我们只是捕获了可以ping通的URL,以确保我们的服务已启动并正在运行。

"configs": [
    {
      "itemId": 1,
      "name": "services_list_url",
      "type": "string",
      "subType": "",
      "mandatory": true,
      "validationRegEx": "",
      "validationMessage": "",
      "uiHint": "",
      "label": "HTTP URL for the services list eg. http://localhost:8080/services"
    }
  ]
2.继承RANGERBASESERVICE的类

RangerBaseService插件实现服务器端组件的第二部分和最后一部分是编写一个继承RangerBaseService的类。

Apache Ranger插件

该类希望重写两个函数:

  1. validateConfig :请记住servicedef的configs部分。 显然,我们将接受这些参数的值,对吗? 现在,这个validateConfig是我们验证传递的值的地方。 对于我们的HTTP服务,我们在配置中接受的只是services_list_url 。 现在,该功能的实现将是使用一个简单的HTTP客户端ping并检查服务是否已启动并正在运行。
class RangerServiceHTTP extends RangerBaseService {

  override def validateConfig(): util.Map[String, AnyRef] = {
    if (configs.containsKey("services_list_url")) {
      val serviceUp = HttpServiceClient.isServiceUp(configs.get("services_list_url"))
      if (serviceUp) retSuccessMap() else returnFailureMap()
    }
    else {
      returnFailureMap()
    }
  }
  1. lookupResource :这是一个有趣的功能。 考虑以下屏幕截图。
Apache Ranger插件

稍后,当我们配置访问策略时,我们将在其中配置资源 。 现在,此功能用于查找和自动填充这些资源。 假设,如果我们要输入HDFS资源或Hive表,那么选项的数量就很多,而且很容易打错字。 对于Hive,此功能将连接到metastore并为我们填充表和数据库。

对于HTTP服务,请记住service_list_url ? 该URL将仅返回逗号分隔的REST资源列表。 为了实现此功能,我只是再次调用服务并标记响应。

override def lookupResource(resourceLookupContext: ResourceLookupContext): util.List[String] = {
    val serviceUrl = configs.get("services_list_url")
    HttpServiceClient.getServicePaths(serviceUrl).asJava
  }

现在,作为代码的最后一步,我们需要将RangerServiceHTTP这个类和servicedef配置联系在一起。 我们这样做的方法是通过在implClass属性中配置类。 还要注意,我们正在将该Ranger插件的名称配置为httpservice

{
  "name": "httpservice",
  "label": "HTTP Service",
  "description": "Rudimentary Ranger plugin to enforce security on top of a HTTP Service",
  "guid": "b8290b7f-6f69-44a9-89cc-06b6975ea676",
  "implClass": "com.arunma.ranger.http.RangerServiceHTTP",
* *   "version": 1,
  "isEnabled": 1,
  "resources": [
    {
      "itemId": 1,
      "name": "path",
      ...
      ...

完整的配置如下所示

还有两个较小的管理步骤:

  1. 为了确保我们的类在Ranger类路径上可用,我们将其捆绑到一个jar中,并将其放置在<RANGER_HOME>/ews/webapp/WEB-INF/classes/ranger-plugins/httpservice 。 文件夹httpservice的名称与servicedef配置中声明的名称相对应。
Apache Ranger插件
  1. 将我们的配置上传到Ranger中,以便我们的服务在Ranger UI中可见。
curl -u admin:admin -X POST -H "Accept: application/json" -H "Content-Type: application/json" --data @http-ranger.json http://localhost:6080/service/plugins/definitions

重新启动Ranger服务器。

耶! 现在,我们在Ranger UI上看到HTTPSERVICE

Apache Ranger插件

2.应用程序侧组件:

在应用程序方面,事情再简单不过了。 为了使用Ranger中使用的策略,应用程序需要做的就是调用Ranger并检查用户是否有权访问资源。 该函数从字面上称为isAccessAllowed

Apache Ranger插件

以下代码几乎是需要在应用程序端编写的所有代码:

package com.arunma.ranger

import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler
import org.apache.ranger.plugin.policyengine.{RangerAccessRequestImpl, RangerAccessResourceImpl}
import org.apache.ranger.plugin.service.RangerBasePlugin

import scala.collection.JavaConverters._

object RangerAuthorizer {
  lazy val plugin = {
    val plg = new RangerBasePlugin("httpservice", "httpservice")
    plg.setResultProcessor(new RangerDefaultAuditHandler)
    plg.init()
    plg
  }

  def authorize(path: String, accessType: String, userName: String, userGroups: Set[String] = Set("public")): Boolean = {
    val resource = new RangerAccessResourceImpl()
    resource.setValue("path", path)
    val request = new RangerAccessRequestImpl(resource, accessType, userName, userGroups.asJava)
    val result = plugin.isAccessAllowed(request)
    result != null && result.getIsAllowed
  }
}

RangerBasePlugin("httpservice", "httpservice")init()函数用作我们进入Ranger服务的入口。 注意RangerBasePluginhttpservice参数。 该名称必须与servicedef配置中提供的名称匹配。

authorize函数是拦截器在客户端被授予对REST资源的访问权之前调用的函数。 该函数只是构造一个AccessRequest – RangerAccessRequestImpl并调用插件的isAccessAllowed函数,该函数返回Boolean

拦截器指令 authorize调用isRangerAuthorized函数,然后在RangerAuthorizer中调用authorize函数。

def isRangerAuthorized(path: String, httpMethod: String, userName: String): Boolean = RangerAuthorizer.authorize(path, httpMethod.toLowerCase, userName)  

lazy val userRoutes: Route =
    headerValueByName("username") { userName =>
      extractMethod { method =>
        pathPrefix("users") {
          extractMatchedPath { matchedPath =>
            authorize(isRangerAuthorized(matchedPath.toString(), method.name(), userName)) {
              concat(
                pathEnd {
                  concat(
                    get {
                      val users: Future[Users] =
                        (userRegistryActor ? GetUsers).mapTo[Users]
                      complete(users)

我们需要做的最后一件事是将auditsecurity xml复制到我们的类路径中。 这些就像Ranger的站点xmls 。 对于本练习,我们将xmls放置在resources目录中。

audit xml和security xml可以从游侠代码库复制。 如果您正在运行本地管理员,则审核XML可以保持原样,但是需要为我们的服务更改security xml。 实现此目的的最简单方法是从护林员代码库中复制示例xml,然后开始将服务替换为httpservice如下所示:

Apache Ranger插件

还有一个属性,需要特别注意。 这就是名为ranger.plugin.httpservice.service.name的属性。 此属性的值必须与您在Ranger UI中使用的服务名称相同。

<property>
	<name>ranger.plugin.httpservice.service.name</name>
	<value>MyService</value>
	<description>
		Name of the Ranger service containing policies for this httpservice instance
	</description>
</property>
Apache Ranger插件

试乘

这将涉及两个步骤

  1. 配置Ranger策略
  2. 验证您的HTTP服务
1.配置范围政策
Apache Ranger插件
2.验证您的HTTP服务

让我们通过启动HTTP服务来验证策略-启动com.arunma.RangerManagedHttpServer

策略配置的用户

curl -X GET -H 'username:arunma' http://localhost:8080/users
Apache Ranger插件

无效的用户

curl -X GET -H 'username:nobody' http://localhost:8080/users
Apache Ranger插件

摘要

Ranger插件有两个部分–服务器端组件和客户端组件。 对于服务器端组件,我们创建了一个servicedeef json和一个继承了RangerBaseService的类。 对于客户端组件,我们只调用了pluginisAccessAllowed函数。

您现在可以使用Ranger授权的HTTP服务。

谢谢阅读。 快乐黑客!

翻译自: https://www.javacodegeeks.com/2019/05/beautiful-simplicity-apache-ranger-plugin.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值