背景
medusa0.6的全局属性导入功能,过程中需要导出图中的所有点。
接入资源池版本后,导出一直提示
org.apache.hadoop.hbase.security.AccessDeniedException: org.apache.hadoop.hbase.security.AccessDeniedException: Insufficient permissions for user ‘account526147',action: scannerOpen, tableName:c38fe44e4439d4d03a0e378f35ea56edc, family:e.
提示权限不够
原因
主要是资源池版本,提交spark任务时,使用了资源池的任务提交流程,该流程中会故意加上了proxy-user 配置,指定了account526147用户
但是这个用户又没有hbase表的权限,所以对hbase的scan操作会报错
初步解决方案
想办法让newAPIHadoopRDD能用上admin这样的用户权限
spark提交任务时额外配置(无效)
this.addConf(options, "--conf", "spark.driver.extraJavaOptions=\"-DHADOOP_USER_NAME=" + "admin" + "\"", BLANK);
this.addConf(options, "--conf", "spark.executor.extraJavaOptions=\"-DHADOOP_USER_NAME=" + "admin" + "\"", BLANK);
提交newAPIHadoopRDD前配置conf(无效)
conf.set(TableInputFormat.SCAN, scanToString)
conf.set("HADOOP_USER_NAME","admin")
sparkSession.sparkContext.newAPIHadoopRDD(
conf,
classOf[TableInputFormat],
classOf[ImmutableBytesWritable],
classOf[Result])
newAPIHadoopRDD前,额外配置UserGroupInformation(无效)
阅读newAPIHadoopRDD的源码,发现SparkHadoopUtil类的中有
def addCredentials(conf: JobConf): Unit = {
val jobCreds = conf.getCredentials()
jobCreds.mergeAll(UserGroupInformation.getCurrentUser().getCredentials())
}
获取crediential的过程中有,UserGroupInformation.getCurrentUser()操作。
可以尝试配置一下UserGroupInformation的当前用户,如下所示。
UserGroupInformation.setLoginUser(UserGroupInformation.createRemoteUser("admin"))
sparkSession.sparkContext.newAPIHadoopRDD(
conf,
classOf[TableInputFormat],
classOf[ImmutableBytesWritable],
classOf[Result])
测试后,发现不能生效。
增加额外日志打印,发现:
public static UserGroupInformation getCurrentUser() throws IOException {
AccessControlContext context = AccessController.getContext();
Subject subject = Subject.getSubject(context);
if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
//不会走这个分支
return getLoginUser();
} else {
//subject不为空,走到这里时AccessController已经有鉴权信息了,不会从后来增加的loginUser中获取
return new UserGroupInformation(subject);
}
}
使用UserGroupInformation的doAs方法(无效)
观看源码,貌似doAs方法可以已新建的用户来执行逻辑
这样是不是可以绕开权限的控制呢?
UserGroupInformation.createRemoteUser("admin").doAs(new PrivilegedExceptionAction[RDD[Result]] {
def run(): RDD[Result] = {
// scan habse and produce dataframe
}
})
观察日志可见,driver端,确实更换了用户名称;
但是executor端没有同步,权限信息仍是account用户的。还是报错
重写TableInputFormat的子类
跟ranger那边沟通后,得知一种新方法:
sparkSession.sparkContext.newAPIHadoopRDD(
conf,
classOf[TableInputFormat],
classOf[ImmutableBytesWritable],
classOf[Result])
newAPIHadoopRDD中传入的TableInputFormat类,可以额外配置用户权限:
我们新建一个类MedusaTableInputFormat extends TableInputFormat
复写其initialize方法:
@Override
protected void initialize(JobContext context) throws IOException {
TableName tableName = TableName.valueOf(this.conf.get("hbase.mapreduce.inputtable"));
try {
//把这里的连接获取方式替换一下,使用admin用户即可
//this.initializeTable(ConnectionFactory.createConnection(new Configuration(this.conf)), tableName);
this.initializeTable(ConnectionFactory.createConnection(new Configuration(this.conf),
User.create(UserGroupInformation.createRemoteUser("admin"))), tableName);
} catch (Exception var4) {
LOG.error(StringUtils.stringifyException(var4)); }
}
测试后生效!