sparksql集成sentry遇到的问题

       sparksql本身并不提供安全认证机制,当前集群的安全认证主要包括sentry和ranger两大块,在通过sparksql执行建表时,sentry的权限报错'org.apache.hadoop.hive.metastore.api.MetaException: User xxx does not have privileges for CREATETABLE',然而通过hive的beeline执行就可以。

        通过对sentry日志的分析,定位到sentry校验权限的代码:

public void authorize(HiveOperation hiveOp, HiveAuthzPrivileges stmtAuthPrivileges,
      Subject subject, List<List<DBModelAuthorizable>> inputHierarchyList,
      List<List<DBModelAuthorizable>> outputHierarchyList)
          throws AuthorizationException {
    if (!open) {
      throw new IllegalStateException("Binding has been closed");
    }
    boolean isDebug = LOG.isDebugEnabled();
    if(isDebug) {
      LOG.debug("Going to authorize statement " + hiveOp.name() +
          " for subject " + subject.getName());
    }

    // Check read entities
    Map<AuthorizableType, EnumSet<DBModelAction>> requiredInputPrivileges =
        stmtAuthPrivileges.getInputPrivileges();
    if(isDebug) {
      LOG.debug("requiredInputPrivileges = " + requiredInputPrivileges);
      LOG.debug("inputHierarchyList = " + inputHierarchyList);
    }
    Map<AuthorizableType, EnumSet<DBModelAction>> requiredOutputPrivileges =
        stmtAuthPrivileges.getOutputPrivileges();
    if(isDebug) {
      LOG.debug("requiredOuputPrivileges = " + requiredOutputPrivileges);
      LOG.debug("outputHierarchyList = " + outputHierarchyList);
    }
    LOG.info("user: {}, required input hierarchy: {}, required output hierarchy: {}",
            new Object[]{subject.getName(), inputHierarchyList, outputHierarchyList});

    boolean found = false;
    for (Map.Entry<AuthorizableType, EnumSet<DBModelAction>> entry : requiredInputPrivileges.entrySet()) {
      AuthorizableType key = entry.getKey();
      for (List<DBModelAuthorizable> inputHierarchy : inputHierarchyList) {
        if (getAuthzType(inputHierarchy).equals(key)) {
          found = true;
          if (!authProvider.hasAccess(subject, inputHierarchy, entry.getValue(), activeRoleSet)) {
            throw new AuthorizationException("User " + subject.getName() +
                " does not have privileges for " + hiveOp.name());
          }
        }
      }
      if (!found && !key.equals(AuthorizableType.URI) && !(hiveOp.equals(HiveOperation.QUERY))
          && !(hiveOp.equals(HiveOperation.CREATETABLE_AS_SELECT))) {
        throw new AuthorizationException("Required privilege( " + key.name() + ") not available in input privileges");
      }
      found = false;
    }

    for (Map.Entry<AuthorizableType, EnumSet<DBModelAction>> entry : requiredOutputPrivileges.entrySet()) {
      AuthorizableType key = entry.getKey();
      for (List<DBModelAuthorizable> outputHierarchy : outputHierarchyList) {
        if (getAuthzType(outputHierarchy).equals(key)) {
          found = true;
          if (!authProvider.hasAccess(subject, outputHierarchy, entry.getValue(), activeRoleSet)) {
            throw new AuthorizationException("User " + subject.getName() +
                " does not have privileges for " + hiveOp.name());
          }
        }
      }
      if(!found && !(key.equals(AuthorizableType.URI)) &&  !(hiveOp.equals(HiveOperation.QUERY))) {
        throw new AuthorizationException("Required privilege( " + key.name() + ") not available in output privileges");
      }
      found = false;
    }

  }

    该方法的传入参数包括:

    hiveOp: 当前sql的操作类型

    stmtAuthPrivileges: 本次操作所需的权限集合

    subject: 表示当前用户

    inputHierarchyList和outputHierarchyList分别表示输入对象和输出对象,即本次sql需要访问的输入输出资源

    用户的鉴权分为两步:

    1. 用户是否拥有对输入对象列表的该operation对应的访问权限

    2. 用户是否拥有对输出对象列表的该operation对应的访问权限

    stmtAuthPrivileges包含了输入对象权限map和输出对象权限map,map的key值为一个AuthorizableType枚举对象,取值为Server,Db,Table,Column,View,URI中的一种,对于每一个AuthorizableType,至少有一个inputList或outputList与其authzType相同,此时通过Provider的hasAccess方法判断该用户是否对该对象列表拥有相应的权限。

      真正校验权限的逻辑在ResourceAuthorizationProvider的doHasAccess方法中:

private boolean doHasAccess(Subject subject,
      List<? extends Authorizable> authorizables, Set<? extends Action> actions,
      ActiveRoleSet roleSet) {
    List<String> requestPrivileges = buildPermissions(authorizables, actions);
    try {
      Set<String> groups = getGroups(subject);
      Set<String> users = Sets.newHashSet(subject.getName());
      Set<String> hierarchy = new HashSet<String>();
      for (Authorizable authorizable : authorizables) {
        hierarchy.add(KV_JOINER.join(authorizable.getTypeName(), authorizable.getName()));
      }
      LOGGER.info("get privileges args, groups: {}, users: {}, role set: {}, authorizables: {}",
              new Object[]{groups, users, roleSet, authorizables.toArray(new Authorizable[0])});
      Iterable<Privilege> privileges = getPrivileges(groups, users, roleSet,
              authorizables.toArray(new Authorizable[0]));

      lastFailedPrivileges.get().clear();

      for (String requestPrivilege : requestPrivileges) {
        Privilege priv = privilegeFactory.createPrivilege(requestPrivilege);
        for (Privilege permission : privileges) {
          boolean result = permission.implies(priv, model);
            LOGGER.info("user: {}, group: {}, ProviderPrivilege {}, RequestPrivilege {}, RoleSet, {}, Result {}",
                    new Object[]{users, groups, permission, requestPrivilege, roleSet, result});
          if (result) {
            return true;
          }
        }
      }
    } catch (Exception ex) {
      LOGGER.error("sentry auth privilege error: {}", Throwables.getStackTraceAsString(ex));
    }
    lastFailedPrivileges.get().addAll(requestPrivileges);
    return false;
  }

      sentry根据用户的组、角色从数据库中读取其拥有的权限,并与需要的权限进行比对,只有当inputHierarchyList中的所需权限都符合时,才能通过认证。

      通过对sparksql执行时的sentry日志和beeline执行时的sentry日志对比发现,sparksql执行传入的inputHierarchyList中包含了欲创建表的location,此时表尚未创建,用户只有对数据库的权限,因此对该表的权限认证不能通过。

 

      通过分析sparksql的源码,spark创建表的逻辑存在于HiveClientImpl的createTable方法中:

override def createTable(table: CatalogTable, ignoreIfExists: Boolean): Unit = withHiveState {
    verifyColumnDataType(table.dataSchema)
    client.createTable(toHiveTable(table, Some(userName)), ignoreIfExists)
  }

     这里进行了转换,将CatalogTable转换成HiveTable,CatalogTable中包含了location信息。

     对这块逻辑做修改,在执行createTable时,去掉table中的location信息。

override def createTable(table: CatalogTable, ignoreIfExists: Boolean): Unit = withHiveState {
    verifyColumnDataType(table.dataSchema)
    val hiveTable = toHiveTable(table, Some(userName))
	if (sparkConf.getBoolean("spark.sql.enable.sentry", defaultValue = false)) {
		hiveTable.getTTable.getSd.setLocation(null)
	}
    client.createTable(hiveTable, ignoreIfExists)
  }

      执行sparksql时,设置spark.sql.enable.sentry为true,则sentry权限校验通过。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Boot 可以很方便地集成 Sentry,以下是集成步骤: 1. 在 `pom.xml` 文件中添加 Sentry 依赖: ```xml <dependency> <groupId>io.sentry</groupId> <artifactId>sentry-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> ``` 2. 在 `application.properties` 或 `application.yml` 文件中配置 Sentry: ```properties sentry.dsn=YOUR_DSN sentry.release=YOUR_RELEASE_VERSION sentry.environment=YOUR_ENVIRONMENT ``` 其中,`YOUR_DSN` 是你的 Sentry DSN(Data Source Name),`YOUR_RELEASE_VERSION` 是你的应用版本号,`YOUR_ENVIRONMENT` 是你的应用环境(如 `production`、`development` 等)。 3. 创建一个全局异常处理器类,用于捕获异常并发送到 Sentry: ```java import io.sentry.Sentry; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ModelAndView handleException(Exception e) { // 发送异常信息到 Sentry Sentry.captureException(e); // 返回错误页面或其他处理方式 ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("error"); modelAndView.addObject("errorMessage", "An error occurred"); return modelAndView; } } ``` 以上步骤完成后,当应用发生异常时,Sentry 将会自动捕获并发送相应的异常信息。你可以登录 Sentry 平台查看应用的异常报告和性能指标。 希望以上信息对你有帮助!如果你还有其他问题,请继续提问。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值