前一篇文章介绍了hive的授权模型和spark支持hive的现状,可以看到目前授权管理各自为政:HDFS,hive,yarn,storm等都有自己的授权模型,需要到具体产品下进行修改,比较分散,不利于统一管理,需要有一个集中控制的工具(更准确的应该叫做框架)

目前已有的统一授权管理的开源框架为Ranger和Sentry。前者由hortonworks主推,从目前的情况来看其功能更为全面(例如可以支持hive的列级授权,支持审计Audit);后者由Cloudera开发,可能更有后劲。另外Cloudera在酝酿一个安全平台。

其他还有knox安全网关这种剑走偏锋的工具,简要说明可以参见五种安全工具对比。 这里简要介绍下Ranger的功能,以及其大致机制。

1、功能

我用的是Hortonworks的sandbox,略过了ranger的安装部署过程(具体部署可以参考这篇文章,后面有时间还是再做一次部署)。

先介绍几个概念:

  • user: Ranger自己管理的用户,分为internal和external,前者为Ranger自己的用户,例如admin;后者为linux或者LDAP的用户,在操作系统/LDAP里新增用户后会同步到Ranger。
  • group: Ranger自己管理的用户组,也有内外之分,与user类似,设置与LDAP同步后会将LDAP的组同步过来,该组为外部组;如果是Ranger自己的用户新增的组,则为internal组。
  • Service: 即授权管理服务,每个组件可以设置多个Service。
  • Policy: 每个Service中可以有多条Policy,组件不同,Policy授权模型不同

支持组件

已经支持的有HDFS/HBASE/HIVE/YARN/KNOX/STORM/SOLR/KAFKA。有的资料上说Ranger后面会支持spark,比较好奇最终会怎么实现。

用户/组管理

Ranger的用户/组的设计跟linux基本一致。Ranger可以把操作系统新增的用户同步到Ranger内。Ranger支持Role,但没有看到完善的RBAC的功能,只有Admin/User两个角色。用户可以属于多个组,但是一旦新建后就不能修改还是挺不好用的。

授权

以Hive为例。

1、编辑Service的属性

也可以删掉新建。默认沙箱里只给ambari-qa授权,不过我这里之前给hive做过授权,所以填了hive。需要保证Hive Service可用。

2、编辑Policy

Ranger支持的权限比较细,授权对象可以是DB、表、列、UDF,权限类型包括select/update/create/drop/alter/index等等。 可以选择对某一用户授权,也可以对某一用户组授权。我这里给hive授予了一个表的所有权限。

3、验证

在beeline中使用hive连上hive服务,验证其权限,略。

注意,不经过Ranger,用户A直接连接到hive上向用户B授权,也是可以的;插件会将该配置向上同步给Ranger,因此最终也可以生效。

Hive跟HDFS不一样。HDFS可以设置二级认证,先Ranger如果失败再走一次HDFS,Hive应该只走Ranger。

下面是最终的Policy,第三条即为向上同步的grant(grant select on xademo.table1 to user xxx;)。

审计

Ranger提供4个方面的审计:

  • Access:记录各个服务的接入信息
  • Admin:记录Ranger的管理信息,例如新建了service,新建了用户等
  • Login Sessions:记录用户登录Ranger的信息,例如用户如果包里破解ranger web UI的登录密码会留下记录
  • Plugins:记录各个Plugin的同步信息

Ranger不提供认证的功能,需要搭配Kerbose。

2、机制

分为如下几部分:

  • ranger-admin:提供web server以及Ranger管理
  • ranger-usersync:与linux或LDAT同步用户。注意这个过程有个时间差,最快1分钟
  • ranger-xxx-plugin为针对各组件的插件。

组件plugin

Ranger会将各个组件的plugin编译成jar包,并将jar包配置文件拷贝到对应的组件。以hdfs为例,插件enable的时候,会将rangerplugin.jar拷贝的hadoop的lib中,将ranger*.xml拷贝到./etc/hadoop/中,并修改hdfs-site.xml,增加授权provider:

    <property>
      <name>dfs.namenode.inode.attributes.provider.class</name>
      <value>org.apache.ranger.authorization.hadoop.RangerHdfsAuthorizer</value>
    </property>

RangerHdfsAuthorizer即为权限检查的类,作为一个钩子,可以截取权限请求以做鉴权。不同的组件,使用对应组件的鉴权模型。

官方的自定义plugin的编码指南在这里,其中最关键的是定义鉴权模型,例如hive的这个:

{
	"id":3,
	"name": "hive",
	"implClass": "org.apache.ranger.services.hive.RangerServiceHive",
	"label": "Hive Server2",
	"description": "Hive Server2",
	"guid": "3e1afb5a-184a-4e82-9d9c-87a5cacc243c",
	"resources":
	[
		{
			"itemId": 1,
			"name": "database",
			"type": "string",
...
			"matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
			"matcherOptions": { "wildCard":true, "ignoreCase":true },
...
		},

		{
			"itemId": 2,
			"name": "table",
			"type": "string",
			"level": 20,
			"parent": "database",
...
        },

		{
			"itemId": 3,
			"name": "udf",
			"type": "string",
			"level": 20,
			"parent": "database",
...
		},

		{
			"itemId": 4,
			"name": "column",
			"type": "string",
			"level": 30,
			"parent": "table",
...
		}
	],

	"accessTypes":
	[
		{
			"itemId": 1,
			"name": "select",
			"label": "select"
		},

		{
			"itemId": 2,
			"name": "update",
			"label": "update"
		},

		{
			"itemId": 3,
			"name": "create",
			"label": "Create"
		},
...
		{
			"itemId": 8,
			"name": "all",
			"label": "All",
			"impliedGrants":
			[
				"select",
				"update",
				"create",
				"drop",
				"alter",
				"index",
				"lock"
			]
		}
	],

	"configs":
	[
		{
			"itemId": 1,
			"name": "username",
			"type": "string",
...
		},

		{
			"itemId": 2,
			"name": "password",
			"type": "password",
...
		},

		{
			"itemId": 3,
			"name": "jdbc.driverClassName",
...
			"defaultValue": "org.apache.hive.jdbc.HiveDriver"
		},

		{
			"itemId": 4,
			"name": "jdbc.url",
			"type": "string",
...
	}
}

各个组件的定义方式基本是按照资源、访问类型、配置等几个方面来定义的,hive的相对复杂,像hdfs就比较简单,只有1个资源(路径),3种访问类型(rwx)。从0.5开始Ranger支持stack-model,只要按照这个套路来定义自定义插件鉴权,就可以做到不需要修改Ranger自己的代码。官方以YARN为例做了编码说明,还是比较清晰的,就不费事翻译了,对照源码走读下还比较容易懂。 我的障碍是不管yarn还是hdfs看到鉴权最后都只有这个:

public class RangerYarnAuthorizer extends YarnAuthorizationProvider {
..
	@Override
	public boolean checkPermission(AccessType accessType, PrivilegedEntity entity, UserGroupInformation ugi) {
		RangerYarnPlugin       plugin       = yarnPlugin;
		RangerAccessResult     result       = null;

		if(plugin != null) {
..
			result = plugin.isAccessAllowed(request, auditHandler);
		}

而plugin是基于RangerBasePlugin的,isAccessAllowed也是父类的方法,怎么做到不同的组件用同一个方法的呢?其实,对于Ranger来说它看到的只有资源和访问请求,最多资源有层级关系,访问请求有交并,完全可以忽略实际的组件抽象出来一个统一的鉴权方式的。核心实现应该比较有趣,有机会应该仔细拜读一下。

当然了,最关键的是组件要有一套自定义鉴权的机制,目前spark sql的鉴权也许只能从hdfs和yarn来做数据隔离和资源隔离了(求打脸)。

参考资料: Ranger官方