环境:
CDH 5.16
Hive 1.1.0
已开启Kerberos
Hive 授权使用SQL StandardsBased Authorization模式(以下简称SSBA模式)
症状表现:
在编译好UDF的jar包之后,上传到HDFS目录。
hdfs dfs -mkdir /udf
hdfs dfs -put -f my_udf.jar /udf
以管理员用户hive,进入beeline客户端。
切换到管理员角色,执行:
set role admin;
提示成功切换角色后,执行创建自定义函数语句:
create function default.ch_cnv as 'com.my.hive.udf.ChsUDF' using jar 'hdfs:///udf/my_udf.jar';
创建自定义函数报错,提示对应用在CREATE FUNCTION上的DFS_URI对象没有“管理权限”:
Error: Error while compiling statement:FAILED: HiveAccessControlException Permission denied: Principal [name=hive,type=USER] does not have following privileges for operation CREATEFUNCTION [[ADMINPRIVILEGE] on Object [type=DFS_URI, name=hdfs://nameservice1/udf/my_udf.jar]](state=42000,code=40000)
尝试带temporary创建临时函数,也是报同样的错误:
create temporary function default.ch_cnv as'com.my.hive.udf.ChsUDF' using jar 'hdfs:///udf/my_udf.jar';
分析思路:
在SSBA模式下,执行某一些语句会要求对应的HDFS URI必须为rwx+所有者的权限,非常繁琐(所以从Hive 3.0.0开始,基于SSBA的方式进行授权时,对URI权限要求,进行了大幅度的修改。例如建立外表时,不再要求用户必须是HDFS目录的所有者,只需要拥有rw权限即可)。会不会是这里出了问题?
先查阅官方文档:
https://cwiki.apache.org/confluence/display/Hive/SQL+Standard+Based+Hive+Authorization
官方文档上清楚的显示CREATE FUNCTION操作是和HDFS URI权限完全无关的。
难道是官方文档写错了???
尝试将jar权限改为777,所属用户和组都修改为hive。
重试create function语句,没有效果,依然报同样的错误。
这条路走不通,那就试试看能不能从别的信息入手。
重新以hive身份进入beeline,不执行set role admin,就以普通的public角色身份,执行create function语句,提示:
Error: Error while compiling statement:FAILED: HiveAccessControlException Permission denied: Principal [name=hive,type=USER] does not have following privileges for operation CREATEFUNCTION [[ADMINPRIVILEGE] on Object [type=DATABASE, name=default], [ADMIN PRIVILEGE] on Object[type=DFS_URI, name=hdfs://nameservice1/udf/my_udf.jar], [ADMIN PRIVILEGE] onObject [type=FUNCTION, name=default.ch_cnv]](state=42000, code=40000)
这时可以看到,对于执行create function,要求了三个权限:
1、 数据库的admin权限
2、 HDFS URI的admin权限
3、 函数对象的admin权限
其中1和3与官方文档的描述是完全一致的。而我们set role admin之后,1和3都被满足。只有2却依然不满足,这说明并不是我们的管理员角色配置,或是管理员角色切换出现了问题。
那只剩下一种可能性,Hive的这个功能有BUG…
搜索的时候,发现了一名网友遇到了同样的问题:
http://lxw1234.com/archives/2016/01/600.htm
虽然并没有人解答这个问题,但至少说明这并非奇特的个案。
来到Apache的官方JIRA,进行搜索。果然有相关的BUG报告:
https://issues.apache.org/jira/browse/HIVE-11175
从描述和提交的补丁来看,是Hive在SSBA模式下进行create function的语义分析时,错误的将一个输入型资源(UDF的JAR包),当作了输出型资源。要求对该路径施加写的管理员权限(实际应当为要求读的权限),一个已经存在的输入型资源是只读的,于是导致永远卡死在这个验证上。
解决方法:
由于已经对Hive集群实施了SSBA模式,所以我们肯定是不可能为了装载一个UDF函数,就去临时将集群的验证关闭的。彻底关闭会有一段时间暴漏在安全风险中,并且还需要重启服务,临时关闭需要重启一次,开启又要重启一次,这会非常麻烦。
另外一种方式就是去应用前面社区中提交的补丁到源码重新编译,然后替换有问题的JAR包。这个补丁目前还没被集成到Hive的主线中,这么做存在一定风险。而且目前这个集群是客户购买的商业版的CDH,也不太建议自己去弄。
怎么办?这个时候就要请最老旧的Hive CLI上场了!
Hive CLI正是由于不兼容完备的授权模式(Sentry/Ranger/SSBA),可以绕过这些安全限制,所以被官方用beeline给代替了,只在Hive3之前的版本,作为一个兼容性功能保留。
所以一旦开启了Hive授权,都是建议对普通用户禁用Hive CLI的(除非你有Spark多租户的需求,否则都会进行下面的HMS代理禁用)。最常见的禁用手段,就是通过配置Hive的hadoop.proxyuser.hive.groups(Hive Metastore 访问控制和代理用户组覆盖),指定值由 * 改为具体的管理员组,使得普通用户就算进入Hive CLI,也无法操作,但是管理员组用户依然可以进入Hive CLI操作。
所以我们可以使用hive管理员用户,直接进入Hive CLI,绕过SSBA的验证过程,来创建UDF函数:
kinit -k -t hive.keytab hive@XXX.COM
hive
进入Hive CLI后,执行:
create function default.ch_cnv as 'com.my.hive.udf.ChsUDF' using jar 'hdfs:///udf/my_udf.jar';
提示创建成功!
切换到beeline,试用带UDF函数的HQL语句,没有问题。搞定!