sentry学习总结
概述
sentry是大数据安全组件,用于管控hive、hbase等组件权限的控制,在hadoop3.0之后使用到的安全组件是ranger,sentry已经废弃,不过很多大数据平台 版本依然是2.x,而且这种安全组件原理也是相通的,因此有必要研究一下(主要是最近被分到了这个组件,😄),由于在工程实践中我们这里更多的是使用sentry 管控hive的权限,因此有必要在介绍sentry之前对hive的体系结构进行说明。
HIVE概述
HIVE架构
如下图所示为hive的架构:

- UI:hive的客户端,包含了hiveCli、beeLine,用户通过UI来实现自己的操作,CliDriver是SQL本地直接编译,然后访问metastore,并提交作业,是重客户端,BeeLine会将SQL提交给Hive Server2,由Hive server2编译,然后访问metastore,并提交作业,是轻客户端。(因此在使用sentry来管控权限的时候,通过hiveCli是无法完美的做到权限管控的,通过beeLine来进行操作才是可以,原因见下面hive的扩展机制)
- Driver:接收查询请求,并处理会话
- Complier:解析查询语句,做语义分析,借助于metastore来生成执行计划
- Execution Engine:用来执行生成的执行计划,是Hive和Hadoop的桥梁
- metastore:提供hive元数据相关的服务
HIVE扩展机制
下面来看一下HIVE中存在的扩展机制,sentry的权限的控制就是基于这种扩展机制完成的:
在生产环境中,hive通常会对应3个不同的进程,这些进程分别是Hive server、Metastore、RDBMS,其中提供扩展机制的是:
- Metastore的Listener
- hive server的Hook
测试需要,我本地搭建了一个hive的环境,下面是针对Listener和Hook的测试:
1 | import org.apache.hadoop.conf.Configuration; |
下面是将这些listener、hook注册到hive之后运行相关的DDL、DML操作的时候触发的事件,如下:
上面测试的Listener和Hook也是sentry使用到的Listener和Hook,sentry中使用Hook鉴权,
使用Listener监听权限及相关的元数据的变更, 并通知到服务端,在sentry的listener中会
从生成的事件中获取事件的id,并将事件的id记录到hive的metastore中, 之后hive的hivemetastoreclient
定时拉取相关的事件并更新到sentry server端的数据库中, 但是在实际测试的过程中却发现应
该存在的事件id真实情况却是null, 线上测试环境中有相关的指标,怀疑和使用的hive版本有关
系或并非所有的event都有eventid。 查看hive的metastore所使用到的数据库发现有表专门对
notification进行了记录,下面展示了eventid为空的情况:
关于hive的相关的知识就到这里,下面我们来看一下sentry的整体架构。
sentry
ER关系图
下图展示了sentry的实体关系图(有些表的作用尚不清楚)
上面的关系表可以看到的是权限的控制是通过RBAC的方式,不过这里的角色是和group绑定的,并不是user,可以让用户加入不同的组来完成权限的控制,
不过用户和组的绑定关系并没有对应的表,而是复用了hadoop中user和group的关系,这一点不论从当前的表中还是代码中都可以得到印证。
sentry整体架构
如上图所示为sentry整体架构图,其中sentry-binding就是利用了Hive上面的扩展机制,
分别使用Listener和Hook对数据的DDL、DML操作进行权限的控制,sentry实现的Listener和Hook的主要的作用:
- Listener主要是针对DDL操作发布对应的事件的id,这样sentryServer端会使用HMSFollower从hive的metastore处进行同步相关的数据。
- Hook的话,会截取DML中的信息,并进行鉴权操作,也会截取DDL中的信息进行授权操作,这个具体可以看下面的代码
权限的grant、revoke以及权限的校验
在hive-binding模块的HiveAuthzBindingHook的postAnalyze中会将task转换成SentryGrantRevokeTask,SentryGrantRevokeTask中重 写的execute方法中通过RPC请求执行了权限的grant、revoke操作。 HiveAuthzBindingHook 继承了AbstractSemanticAnalyzerHook,重写的两个重要的方法分别是preAnalyze、postAnalyze两个方法, 我们来看一下这两个方法所做的操作:
client端鉴权操作(sentry-binding模块)
preAnalyze
下面是preAnalyze方法,我们可以看到preAnalyze所做的操作仅仅是提取hiveSQL语句中的分区、库、表等信息
1 | public ASTNode preAnalyze(HiveSemanticAnalyzerHookContext context, ASTNode ast) |
postAnalyze
下面是postAnalyze方法:
1 | public void postAnalyze(HiveSemanticAnalyzerHookContext context, |
上面最后调用了authorizeWithHiveBindings方法,这里才是权限校验的地方,如下:
1 | private void authorizeWithHiveBindings(HiveSemanticAnalyzerHookContext context, |
由于获取binding的会通过远程调用获取权限,因此有必要看一下这一步的操作。
1 | private static HiveAuthzBinding getHiveBindingWithPrivilegeCache(HiveAuthzBinding hiveAuthzBinding, |
至于最后一步权限的校验逻辑就不再赘述了,具体可以查看相应的代码。由上面的代码分析可以知道, Hook这里实现了授权(SentryGrantRevokeTask)、鉴权(#getHiveBindingWithPrivilegeCache), 也即是用户的所有的请求都会被这一层拦截,并针对对象的类型执行相关的操作。client端的鉴权操作到此可以告一段落
服务端鉴权操作
在client端鉴权操作的过程中我们看到了客户端会通过远程调用执行授权操作,也会通过远程调用执行鉴权操作,这个调用的过程是通过 RPC调用来实现的, 接下来我们看一下服务端的的操作,服务端的操作不止包含了鉴权和授权的操作,还包含了其他的操作,因此我们有必要先概要的介绍 一下服务端的操作,然后 详细的介绍一下鉴权和授权的操作。通过上面的整体架构图我们可以知道,服务端的入口在sentryService这个类中,主要的组成部 分如上图所示其中包含的模块和每一个模块的重要作用如下:
- thrift server:针对hook、listener发送过来的事件做相应的处理操作,处理的逻辑在sentryPolicyStoreProcessor
- sentryWebServer:sentryService内置的一个基于jetty的webserver,有对应的界面:http://${sentry.server.url}:29000,查看了一下主要是一些配置信息和一些监控数据
- sentryStore:(是一个单例)sentryStore在SentryService的构造函数中初始化的,初始化的时候有针对是否开启HDFS同步做检测。
- leadMonitor:sentry开启高可用之后,用来监听leader变更的组件
- HMSFollower:封装了hivemetastoreclient,用于从hivemetastore中同步hive相关的元数据
接下来看一下sentryService的入口:
1 | public String call() throws Exception { |
可以看到call方法内仅仅是针对是否开启kerberos进行判断,最终入口都会调用runServer方法,跟进runServer方法来看一下:
1、首先会启动一些清理工作
2、接下来会启动HMSFollower,首先会获取metastore的thrifturi,然后会以500ms的周期定时调度,在hmsfollower构造函数中,会分别实例化 NotificationProcessor:用于处理notification,主要是针对DDL操作通过sentrystore持久化变更(这里会判断是否开启HDFS同步)。 SentryHMSClient:会首先获取sentryStore的notification的id,获取所有的库表信息(没有持久化),接下来再次获取notification的id, 如果两次获取到的id相同,说明在同步库表信息的过程中并没有新的DDL操作,这就表示已经完成同步操作。如果两次notification的id不同, 说明同步库表信息的时候有DDL操作,这个时候只需要继续同步即可。 HiveNotificationFetcher:会使用sentryHMSClient获取notification
3、接下来会构建thrift server,thrift server包含了processor逻辑(用于处理RPC请求,这里主要是权限的一些校验) 和sentrystore(持久化)的逻辑,之后启动sentryService的服务端来处理客户端的请求
4、然后启动sentryWebServer,这里主要是一些配置信息的查看和内置的一些监控数据
上面的鉴权、授权的主要的业务逻辑是在thriftserver的processor中,对应的实现类为:SentryPolicyStoreProcessor,
我们可以具体查看一下相关的逻辑,下面是该类中包含的方法:

1 | public TCreateSentryRoleResponse create_sentry_role( |
上面代码核心代码只有三行,我们已经对其进行标注,其他的业务处理流程类似,具体可以查看相关的代码,这里不再赘述。
表变更事件同步
client端触发notification的地方是SentrySyncHMSNotificationsPostEventListener这个类,如下: 上面这些方法在监听到特定的事件的时候会触发notification,查看hive的metastore对应的数据库发现有针 对notification的记录,如下为监听到特定事件之后的client的触发机制:
1 | private void syncNotificationEvents(ListenerEvent event, String eventName) { |
如下,还是在SentryPolicyStoreProcessor这个类中,服务端会对客户端发过来的消息进行处理:
1 | public TSentrySyncIDResponse sentry_sync_notifications(TSentrySyncIDRequest request) |
核心代码只有一行,我们看到在sentryStore中包含了一个counterWaiter,该变量会用在thriftserver和hmsfollower同步notification, 存放在sentryStore并不合适,只不过sentryStore是thriftserver和hmsfollower唯一的纽带,后面可能会调整。 在上面的代码中,通过counterWaiter实现了notification的发布机制,真正的处理逻辑在hmsfollower中, 因此我们接下来看一下hmsfollower是怎么处理这些事件的。Hmsfollower的构造函数如下:
1 | public HMSFollower(Configuration conf, SentryStore store, LeaderStatusMonitor leaderMonitor, |
比较重要的地方都已经用中文注释,从名字可以大致看出来notificationFetcher是用来从hive的metastore中拉取notification的,notificationProcessor是用来处理获取到的notification的。 由于hms是定时调度的,因此我们可以通过定时调度的方法作为入口来分析hmsfollower,对应的代码如下:
1 | public void run() { |
核心的代码在syncupWithHms中,最终方法会调度到notificationProcessor.processNotificationEvent(event), 进入该方法发现会针对特定的事件执行权限的变更操作,具体的代码如下:
1 | try (Context ignored = timer.time()) { |
进入到对应的方法查看,发现根据具体的事件同步的变更权限,如:删除数据库会把数据库相关的权限的数据都删除。在完成同步操作之后会记录到数据库中本次metastore的变更。
Hdfs namenode鉴权
元数据同步
hdfs namenode会定期从sentry server中同步权限以及hive的元数据,程序的入口在SentryAuthorizationProvider,最终会通过SentryAuthorizationInfo定时更新authzPaths和authzPermissions,核心代码在类SentryAuthorizationInfo中,如下:
1 | private boolean update() { |
在上面的代码中,更新权限或者hive元数据的时候,会使用一个readwritelock来实现线程之间的同步,这一块应该是考虑让两个变量的同步更新。这一个锁在更新数据的时候会让其他的读操作阻塞,因此可能会产生性能瓶颈。
鉴权操作
hdfs鉴权操作在类SentryAuthorizationProvider中,如下所示:
1 | public void checkPermission(String user, Set<String> groups, |
在上面的代码中我们可以看到,最终是由于defaultAuthzProvider来进行权限的校验的,因此SentryAuthorizationProvider更多的像是一个门面。
总结
sentry权限的管控用到了hive的hook、listener机制,本质上就是一个切面操作,这里比较重要的,我个人认为应当是DDL、DML操作数据的提取, 这样如果在定制化权限控制的时候才可以做到较快的响应