HBase自带的权限管理工具:
HBase的权限管理包括两个部分,分别是Authentication&Authorization
Authentication:针对某host是否有权成为集群的regionserver或者client端;
Authorization:是针对client端对集群数据的读写等权限;
其中Authentication由kerberos提供解决方案,用户级别的Authorization由ACL模块控制
(一)、Authentication——
kerberos的基本原理:
kerberos实现了机器级别的安全认证,包括服务器到服务器的认证,确保不会有其他机器冒充regionserver。kerberos中包含一个中心服务器(KDC),KDC中有数据库,由管理员将集群中确定的机器手动添加到上述数据库中,在KDC上会分别产生主机与各节点的keytab,并将这些keytab分发到对应的节点上。通过这些keytab文件,节点可以从KDC上获得与目标节点通信的秘钥,进而被目标节点所认证,从而才能提供相应的服务;
HBase的kerberos安装流程简述:
1、配置kerberos server和kerberos client
kerberos server既KDC,kerberos client是我们集群节点;
这一步安装kerberos的基本组件,包括krb5-libs、krb5-server等,并初始化kerberos数据库,添加admin用户等等;
2、配置hadoop
创建keytab并修改core-site.xml、hadoop-env.sh、hdfs-site.xml,主要是添加keytab文件的位置信息,并使能kerberos控制;
3、配置zookeeper
创建keytab,修改java.env,修改zoo.cfg
4、配置HBase
创建keytab,修改hbase-env.sh/hbase-site.xml等等
hbase-site.xml中主要是添加下述配置:
hbase.security.authentication
kerberos
添加了如上配置,便使能了hbase的权限控制,此时只有经过了认证的服务器,才能够成功向master申报,所以如果host没有配置kerberos时,贸然添加上述配置可能会导致regionserver进程难以启动;
总结:
1、kerberos提供了机器级别的安全认证,但调研及尝试安装下来之后,觉得首先kerberos的安装流程复杂,如果是建设新集群,可以考虑使用kerberos,但当前集群已经存在上百个业务在时时跑,复杂的安装流程势必会造成业务中断,不是业务方所能接受的;
2、kerberos对应用访问集群的性能有一定影响,加了kerberos认证后,client端再想访问集群,需要先两次访问KDC,第一次访问向KDC证明我是我自己,第二次访问时生成services端和client端之间的session key用于service端对client的身份鉴别,一次用户请求的访问路径变长,必然有性能损耗;
3、使用kerberos,也添加了外部需要维护的依赖,包括KDC&数据库DB等等
(二)Authorization
a、使用方式:
使能hbase的ACL控制比较简单,只需要在集群的各个节点hbase-site.xml添加如下配置即可
hbase.security.authorization
true
hbase.superuser
默认是hbase的安装用户
hbase.coprocessor.master.classes
org.apache.hadoop.hbase.security.access.AccessController
hbase.coprocessor.region.class
org.apache.hadoop.hbase.security.token.TokenProvider,org.apache.hadoop.hbase.security.access.AccessController
其中,hbase.superuser是超级用户,可以不配置,不配置时默认是hbase的安装用户,超级用户用于给其它用户添加或者收回相应权限;
AccessController类是hbase权限控制的主入口类,关于hbase权限控制的机理在下文中有简述;
b、Authorization中可认证的权限都包括了哪些:
R——范围内可读;
W——范围内可写;
X——可以执行授权范围内埋设的coprocessor;
C——范围内可执行creat操作,如scope指定为namespace,则C是指ns之内可以创建表,如scope指定为table,则C是指可对table执行create snapshot操作;
A——范围内可以执行admin操作,如果你的scope是namespace,那么admin操作是指balance、assign、move、merge等,如果你的scope是table,那么admin操作是指alter操作,即修改表属性;
需要注意的是:
1、表的disable权限由’C’认证控制
2、表数据的删除权限由’W’认证控制
c、上面的“scope”都包含了哪些维度:
hbase包含了如下几种可定义的scope维度:
superuser——超级管理员,可执行针对集群的任意操作,一般为集群的安装者,superuser也可以由用户添加,即hbase.superuser配置;
global——全局scope,可操作所有tables;
namespace——可操作指定namespace内的scope,我们的namespace只有一个,就是default;
table——可操作指定的table;
ColumnFamily——可操作指定的columnFamily;
cell——具体到列元的scope指定;
e、hbase权限控制的实现原理:
几个实现要点:
AccessContoller实现了CoprocessorService接口,并继承了Observer类,这说明AccessContoller是一个observer endpoint,即hbase里的coprocessor插件。
AccessContoller作为observer覆写了postStartMaster方法,用于在master启动完成之后调createACLTable来创建acl table,acl table是hbase的system tables之一,用于持久化保存所有的权限信息。acl table和用户表一样都分region存储在regionserver上。
RegionServer启动之后,会逐一open各regionserver上的regions,AccessController同样覆写了postOpen函数,postOpen在region被open之后触发,如果打开的是属于acl table的region,AccessController会遍历该region中的所有内容,load到AccessController维护的一个map中,与此同时,这些数据同时被序列化之后写到zk上,关键代码如下:
void initialize(RegionCoprocessorEnvironment e) throws IOException {
final Region region = e.getRegion();
Configuration conf = e.getConfiguration();
Map> tables = AccessControlLists.loadAll(region);
for (Map.Entry> t : tables.entrySet()) {
byte[] entry = t.getKey();
ListMultimap perms = t.getValue();
byte[] serialized = AccessControlLists.writePermissionAsBytes(perms, conf);
this.authManager.getZKPermissionWatcher().writeToZookeeper(entry,serialized);
}
initialized=true;
}
acl region被加载到AccessContoller的map之后,一个AccessContoller才真正初始化完成。
AccessContoller还实现了coprocessorService接口,这是一个可序列化的service接口,coprocessorService在coprocessor被加载到host的时候实例化,具体实现在coprocessorHost的loadInstance中:
public E loadInstance(Class> implClass, int priority, Configuration conf)
throws IOException {
// create the instance
Coprocessor impl;
Object o = null;
try {
o = implClass.newInstance();
impl = (Coprocessor)o;
} catch (InstantiationException e) {
throw new IOException(e);
} catch (IllegalAccessException e) {
throw new IOException(e);
}
// create the environment
E env = createEnvironment(implClass, impl, priority, loadSequence.incrementAndGet(), conf);
if (env instanceof Environment) {
((Environment)env).startup();
}
coprocessorNames.add(implClass.getName());
return env;
}
createEnvironment中会将各个coprocessorService实例register到masterService上,而在AccessControlService反射出来的类所需要实现的接口,就是shell中hbase权限相关的四个命令:
grant、revoke、getUserPermissions、checkPermissions
其中grant用于授权操作,revoke用于收回用户权限,其余两个是查询操作,以grant接口为例,其实现代码如下所示:
public void grant(RpcController controller,
AccessControlProtos.GrantRequest request,
RpcCallback done) {
final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
AccessControlProtos.GrantResponse response = null;
try {
// verify it's only running at .acl.
if (aclRegion) {
if (!initialized) {
throw new CoprocessorException("AccessController not yet initialized");
}
//用户鉴权
switch(request.getUserPermission().getPermission().getType()) {
case Global :
case Table :
requirePermission("grant", perm.getTableName(), perm.getFamily(),
perm.getQualifier(), Action.ADMIN);
break;
case Namespace :
requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace());
break;
}
//执行权限添加,向acl table新写入一条记录
User.runAsLoginUser(new PrivilegedExceptionAction() {
@Override
public Void run() throws Exception {
AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
return null;
}
});
} else {
throw new CoprocessorException(AccessController.class, "This method "
+ "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
}
response = AccessControlProtos.GrantResponse.getDefaultInstance();
} catch (IOException ioe) {
ResponseConverter.setControllerException(controller, ioe);
}
//执行回调操作
done.run(response);
}
在AccessController保存权限信息的map中,维护了一个ZKPermissionWatcher类型的变量,该变量继承了zookeeperListener,可见该变量可用于监听zk节点上的变化,前面说过acl table的信息会序列化到zk上,如果用户修改acl table的内容,这些修改会同步到zk到,反映为zk节点的变化,zkPermissionWatcher监听zk节点的变化,同步修改AccessController中map的内容,实现了内存、zk与acl table三者之间数据的一致。
下面讲讲zookeeperListener的初始化方式,AccessController在start的时候,会通过env获取该服务上的zookeeperWatcher,并将zkwatcher和config一起构造TableAuthManager,在TableAuthManager的构造函数中,使用上述两个参数和TableAuthManager自身构造成员变量ZKPermissionWatcher,并将之start,start函数的关键代码如下:
public void start() throws KeeperException {
try {
watcher.registerListener(this);
if (ZKUtil.watchAndCheckExists(watcher, aclZNode)) {
List existing =
ZKUtil.getchildDataAndWatchForNewChildren(watcher, aclZNode);
if (existing != null) {
refreshNodes(existing);
}
}
} finally {
initialize.countDown();
}
}
需要注意的是这里的initialize变量,它是一个CountDownLatch,用于确保ZKPermissionWatcher已经初始化完成并成功start,initialize被初始化为1,在start结束后置为0,此时阻塞在initialize await上的nodeCreated、nodeDelete、nodeDataChanged等方法能够进行下去,以监听zk节点的变化,并将zk上的变化同步更新到本地的TableAuthManager中,即集群各节点的内存之中。
总而言之,hbase acl权限的控制是通过hbase自身系统表acl table做持久化,具体发挥作用则是由AccessControler加载进内存,以便于权限的查询结果能够直接从内存返回,减少权限认证对数据访问性能的影响,而保证table和内存内容一致性则是由zookeeper来实现。
f、hbase自带权限的几个注意点(坑点)
1)权限内容通过zk同步,可能会导致部分节点不能准确同步到最新的权限内容,造成不一致,hbase2.0中的procedure V2解决了上述问题,proceduer V2引入了两阶段提交的框架,只有所有节点的更新都成功之后,才确认此次更新成功,否则回滚;
2)生效不平滑,acl生效之后除superuser之外的用户默认不具有任何权限,而新用户授权操作grant只有在acl生效之后才能使用,这造成的问题是,如果在时时有用户读写数据的hbase集群上生效acl,会造成集群在一段时间对所有用户停服。
2、apache ranger的权限控制
a)apache ranger的基本原理:
Apache Ranger也是以coprocessor的形式集成到HBase中,由Ranger Admin管理访问策略,具体的权限策略存储在外部DB中,Ranger插件定期轮询Admin以更新策略到本地,并根据策略信息进行用户权限的判定,其中同样包含一个super用户,可以管理所有策略。插件的ranger web和Ranger Plugin,与Admin之间的通信是基于Http的Restful来实现。
关于Ranger插件的具体机制原理和初始化流程,本文不再详述,只列出它与HBase自带权限控制策略的比较:
1、ranger将权限策略存储于外部DB,由第三方角色Ranger Admin管理,HBase则存储于自身的系统表acl table中,由superuser(HBase的安装用户)管理;
2、新权限的更新方面,ranger是由编译到HBase上的插件Plugin,定期向admin发送restfule请求,以拉取最新的policy,而HBase的自身实现是依靠zk做集群各节点权限信息同步一致;
3、权限的执行,两者相同,都是依赖coprocessor在client端行为的前后埋hook,用户权限的查询都是从内存中获得,ranger是从Plugin中维护的内存,而hbase自带的是从AccessController中维护的内存;
4、权限管理的粒度,两者相似,都可以管理到namespace/table/column family/cell这四个粒度,具体的权限内容,同样包括了get&scan,写,create table等等;
5、关于grant、revoke等操作,ranger掩盖了hbase中的实现,hbase中的不再生效,想要操作从ranger admin入口进行操作;
总结:
对于apache ranger,一需要外部部署DB,solr等外部系统,增加了第三方系统就增加了维护的难度,比如ranger的DB挂掉,那么同样会影响到hbase,造成hbase的数据无法访问;
二、是DB容易成为该系统的单点,因为所有的策略更新都要访问该DB;
三、权限的生效周期不如hbase自带策略及时;