背景

medusa0.6的全局属性导入功能,过程中需要导出图中的所有点。
接入资源池版本后,导出一直提示

  1. 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提交任务时额外配置(无效)

  1. this.addConf(options, "--conf", "spark.driver.extraJavaOptions=\"-DHADOOP_USER_NAME=" + "admin" + "\"", BLANK);
  2. this.addConf(options, "--conf", "spark.executor.extraJavaOptions=\"-DHADOOP_USER_NAME=" + "admin" + "\"", BLANK);

提交newAPIHadoopRDD前配置conf(无效)

  1. conf.set(TableInputFormat.SCAN, scanToString)
  2. conf.set("HADOOP_USER_NAME","admin")
  3. sparkSession.sparkContext.newAPIHadoopRDD(
  4. conf,
  5. classOf[TableInputFormat],
  6. classOf[ImmutableBytesWritable],
  7. classOf[Result])

newAPIHadoopRDD前,额外配置UserGroupInformation(无效)

阅读newAPIHadoopRDD的源码,发现SparkHadoopUtil类的中有

  1. def addCredentials(conf: JobConf): Unit = {
  2. val jobCreds = conf.getCredentials()
  3. jobCreds.mergeAll(UserGroupInformation.getCurrentUser().getCredentials())
  4. }

获取crediential的过程中有,UserGroupInformation.getCurrentUser()操作。
可以尝试配置一下UserGroupInformation的当前用户,如下所示。

  1. UserGroupInformation.setLoginUser(UserGroupInformation.createRemoteUser("admin"))
  2. sparkSession.sparkContext.newAPIHadoopRDD(
  3. conf,
  4. classOf[TableInputFormat],
  5. classOf[ImmutableBytesWritable],
  6. classOf[Result])

测试后,发现不能生效。
增加额外日志打印,发现:

  1. public static UserGroupInformation getCurrentUser() throws IOException {
  2. AccessControlContext context = AccessController.getContext();
  3. Subject subject = Subject.getSubject(context);
  4. if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
  5. //不会走这个分支
  6. return getLoginUser();
  7. } else {
  8. //subject不为空,走到这里时AccessController已经有鉴权信息了,不会从后来增加的loginUser中获取
  9. return new UserGroupInformation(subject);
  10. }
  11. }

使用UserGroupInformation的doAs方法(无效)

观看源码,貌似doAs方法可以已新建的用户来执行逻辑
这样是不是可以绕开权限的控制呢?

  1. UserGroupInformation.createRemoteUser("admin").doAs(new PrivilegedExceptionAction[RDD[Result]] {
  2. def run(): RDD[Result] = {
  3. // scan habse and produce dataframe
  4. }
  5. })

观察日志可见,driver端,确实更换了用户名称;
但是executor端没有同步,权限信息仍是account用户的。还是报错

重写TableInputFormat的子类

跟ranger那边沟通后,得知一种新方法:

  1. sparkSession.sparkContext.newAPIHadoopRDD(
  2. conf,
  3. classOf[TableInputFormat],
  4. classOf[ImmutableBytesWritable],
  5. classOf[Result])

newAPIHadoopRDD中传入的TableInputFormat类,可以额外配置用户权限:
我们新建一个类MedusaTableInputFormat extends TableInputFormat
复写其initialize方法:

  1. @Override
  2. protected void initialize(JobContext context) throws IOException {
  3. TableName tableName = TableName.valueOf(this.conf.get("hbase.mapreduce.inputtable"));
  4. try {
  5. //把这里的连接获取方式替换一下,使用admin用户即可
  6. //this.initializeTable(ConnectionFactory.createConnection(new Configuration(this.conf)), tableName);
  7. this.initializeTable(ConnectionFactory.createConnection(new Configuration(this.conf),
  8. User.create(UserGroupInformation.createRemoteUser("admin"))), tableName);
  9. } catch (Exception var4) {
  10. LOG.error(StringUtils.stringifyException(var4)); }
  11. }

测试后生效!