【sentry 到 ranger 系列】二、Sentry 的 Hadoop 鉴权插件

这篇具有很好参考价值的文章主要介绍了【sentry 到 ranger 系列】二、Sentry 的 Hadoop 鉴权插件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前景引入

  在本系列的前篇文章里【sentry 到 ranger 系列】一、Sentry 的 Hive 鉴权插件 ,已经对最重要的 Sentry 的 Hive 插件做了一些说明,回顾一下这张 Sentry 和 Hive 交互的关系图:【sentry 到 ranger 系列】二、Sentry 的 Hadoop 鉴权插件,# 从 sentry 到 ranger 不得不知道的事儿,sentry,hadoop,大数据
  以上在当 Sentry 只对 Hive 进行权限管控的时候是符合的,但是如果 Sentry 也对 Hadoop 进行了权限接管,那么权限的流转还需要再补充一些部分。可以先看下图:
【sentry 到 ranger 系列】二、Sentry 的 Hadoop 鉴权插件,# 从 sentry 到 ranger 不得不知道的事儿,sentry,hadoop,大数据

二、NN插件定时拉取Sentry权限和路径信息

2.1、数据流转概述

  在前一篇文章中提到了,使用 Sentry 的一个便捷的地方就是同时上了 Hive 和 Hadoop 插件,能够在 Hive 这边自动完成对应路径的权限同步。当 Sentry 拿到了 Hive 的和权限相关的 3 组数据:

  • 库表名和hdfs路径的映射关系
  • (sentry)角色和(linux)组的关系
  • 角色 - 库表 - 权限的关系

  保存到了数据库里面,Namenode 的 Sentry 插件会在启动的时候向 Sentry 请求一次这些全量的数据缓存在内存中维护起来,之后每隔 500ms 向 Sentry 请求一次增量数据用来更新数据,其中的权限数据会被转换成对应路径的权限。在有 HDFS 客户端向 NN 请求读写数据时,就会校验内存中的权限数据从而实现鉴权。
  所以当有一个比如 Hive SQL 任务开始执行,首先是在 SQL 的语法解析阶段就会通过 Hive 的 Sentry 插件进行鉴权,通过后,在读取 HDFS上的文件时,还会对该任务的用户进行一次文件权限的校验,而这里就像前面说的一样,是从 Sentry 同步过来的权限,是和前面 Sentry 的权限是一致的,所以在 SQL 校验通过的基础上,这里一般也不会有权限问题。

2.2、插件源码跟踪

2.2.1、DefaulAuthorizationProvider

  在讲到 NN 的 Sentry 插件之前,要先说一下 NN 默认的权限管理类 DefaulAuthorizationProvider
DefaulAuthorizationProvider 随着 FSNamesystem 的启动而启动。FSNamesystem 是 NameNode 中非常重要的一个类,其囊括了全部的关于文件系统的能力,这里主要涉及到 FSNamesystem 下构建的 FSDirectory,其本质是就是一个文件系统的树形结构实现;以及 DefaulAuthorizationProvider,是 FSNamesystem 下的默认的用来做权限校验的类。DefaulAuthorizationProvider 继承的是 AuthorizationProvider,通过调用该类的 start 方法将实现类启动起来。

  void startCommonServices(Configuration conf, HAContext haContext) throws IOException {
	...
    // 从配置文件读取验证器并调start()方法启动
    authzProvider = ReflectionUtils.newInstance(conf.getClass(
        DFS_NAMENODE_AUTHORIZATION_PROVIDER_KEY,
        DefaultAuthorizationProvider.class,
        AuthorizationProvider.class), conf);
    authzProvider.start();
    AuthorizationProvider.initUsersToBypassExtProvider(conf);
    AuthorizationProvider.set(authzProvider);
    snapshotManager.initAuthorizationProvider();
  }

2.2.2、SentryAuthorizationProvider

  经过上面的剖析,很明显,Sentry 的插件的实现方式自然也是去继承并实现 AuthorizationProvider,并将配置文件里的类名改为 SentryAuthorizationProvider 的全类名,这样就实现了插件的加载了。
  有意思的是,AuthorizationProvider 要做的事情其实很多,Sentry 为了不做重复的事情,在 SentryAuthorizationProvider 的 start() 方法里面,还是把 DefaulAuthorizationProvider 启动起来了。

public class SentryAuthorizationProvider
    extends AuthorizationProvider implements Configurable {

  ...
  
  @Override
  public synchronized void start() {
    ...
	  // 偷懒用了原来的校验器
      defaultAuthzProvider = new DefaultAuthorizationProvider();
      defaultAuthzProvider.start();
      ...
      if (authzInfo == null) {
        authzInfo = new SentryAuthorizationInfo(conf);	//提供校验的核心类
      }
      authzInfo.start();
  ...
}

  这里因为实际上存在两个 AuthorizationProvider 在共同起作用,他们肯定是要分工的,这里就涉及到这个参数 sentry.hdfs.integration.path.prefixes,一般来讲其值为/user/hive/warehouse,也就是说,Sentry 会负责管理这个 Hive 路径下的权限,而其他的路径的权限,就还是 DefaulAuthorizationProvider 来管理,这一点在接下来的剖析中还会证明到。

2.2.2.1、SentryAuthorizationInfo

  从上面代码中可以看到,SentryAuthorizationProvider 又启动了一个 SentryAuthorizationInfo,这个 SentryAuthorizationInfo 内部就维护了 Sentry 这边管理的权限信息,包含了权限更新和鉴权的能力。首先看下权限同步的逻辑,也就是在 SentryAuthorizationInfo 的 start() 方法中

  public void start() {
    if (authzPaths != null || authzPermissions != null) {
      boolean success = false;
      try {
        success = update();
      } catch (Exception ex) {
        success = false;
        LOG.warn("Failed to do initial update, will retry in [{}]ms, error: ",
            new Object[]{retryWaitMillisec, ex.getMessage(), ex});
      }
      if (!success) {
        waitUntil = System.currentTimeMillis() + retryWaitMillisec;
      }
      ThreadFactory sentryAuthInfoRefresherThreadFactory = new ThreadFactoryBuilder()
          .setNameFormat(SENTRY_AUTHORIZATION_INFO_THREAD_NAME)
          .setDaemon(true)
          .build();
      executor = Executors.newSingleThreadScheduledExecutor(sentryAuthInfoRefresherThreadFactory);
      executor.scheduleWithFixedDelay(this, refreshIntervalMillisec, 
          refreshIntervalMillisec, TimeUnit.MILLISECONDS);
    }
  }

  这里的 update() 方法就是同步并且更新权限的方法,一般来讲,这里是第一次调 update(),会做一次全量权限的拉取,在 HDFS路径和权限数据比较大的时候,这个地方会拖慢 Namenode 的启动速度。下面可以看到是起了一个固定线程,refreshIntervalMillisec默认值就是500ms,所以其实就是每隔 500ms(默认)调一次 update() 方法持续更新权限。所以解析来看一下 update() 的逻辑。


  private boolean update() {
    //Looks like getting same updates multiple times
    SentryAuthzUpdate updates = updater.getUpdates();
    // Updates can be null if Sentry Service is un-reachable
    if (updates != null) {
      if (updates.isEmpty()) {
        return true; // no updates is a norm, it's still success
      }

      UpdateableAuthzPaths newAuthzPaths = processUpdates(
          updates.getPathUpdates(), authzPaths);
      UpdateableAuthzPermissions newAuthzPerms = processUpdates(
          updates.getPermUpdates(), authzPermissions);

      // processUpdates() should return different newAuthzPaths and newAuthzPerms object references
      // if FULL updates were fetched from the Sentry server, otherwise, the same authzPaths and authzPermissions
      // objects will be returned.
      if (newAuthzPaths != authzPaths || newAuthzPerms != authzPermissions) {
        ...
          if (newAuthzPaths != authzPaths) {
            ...
            authzPaths = newAuthzPaths;
			...
          }
          if (newAuthzPerms != authzPermissions) {
            ...
            authzPermissions = newAuthzPerms;
            ...
          }
        ...
      ...
      return true;
    }
    return false;
  }

其实逻辑很清晰,一共有 3 步:

  1. 获取更新的权限数据
  2. 处理更细的数据和当前的数据,也就是merge出新的版本
  3. 替换当前版本
2.2.2.2、获取权限更新

  这里就是插件请求 Sentry Server 数据了,同样还是 SentryClient 调的 thrift 接口,这里的接口参数是上一次请求数据的 id 最大值,Sentry Server 根据 id,去查 sentry_perm_change 和 sentry_path_change 这两张表中 id 请求参数的变更数据然后组装返回。这两张表的数据是已 json 格式存储的,样例如下:

{
    "1": {
        "tf": 0
    },
    "2": {
        "i64": 0
    },
    "3": {
        "map": [    --表示内嵌的结构体为一个map类型
            "str",  --表示内嵌结构的key是字符串
            "rec",  --表示内嵌结构的value是一个结构体
            1,      --表示内嵌结构的元素个数为1
            {
                "test2.ctable": {
                    "1": {
                        "str": "test2.ctable"
                    },
                    "2": {
                        "map": [
                            "str",
                            "str",
                            0,
                            {}
                        ]
                    },
                    "3": {
                        "map": [
                            "str",
                            "str",  --表示内嵌结构的value是字符串
                            1,
                            {
                                "__ALL_ROLES__": "__ALL_ROLES__"
                            }
                        ]
                    }
                }
            }
        ]
    },
    "4": {
        "map": [
            "str",
            "rec",
            0,
            {}
        ]
    }
}

  上面这条变更信息就是一个 Sentry 定义的 thrift 对象 json 序列化后的字符串,其含义就是删除表 test2.ctable 对所有角色的权限。再简单对上述json的含义做一个说明:

  • “1” : 忽略
  • “2” : 忽略
  • “3” : 权限变更信息
    • “1” : 验证对象(authzObject)
    • “2” : 添加的权限
    • “3” : 删除的权限
  • “4” : 路径变更信息

  json 里面带元素类型和元素个数这些信息是 thrift 消息体的风格,对序列化和反序列化的效率有帮助;哪个数字后面的信息是属于什么类型是由 Sentry 定义的,这个结构的定义不用太在意。最终返回给 SentryClient 的权限变更信息就是 PermissionsUpdate 对象,路径变更信息就是 PathsUpdate 对象,他们都被以一个 list 集合封装在 SentryAuthzUpdate 里返回给客户端,就是在上述 update() 里的 updates。
  还有一个点需要注意,Sentry 每隔 12 小时清理一次 sentry_perm_change 和 sentry_path_change 表中的变更数据,只保留最近 200 条。如果变更数据比较大的情况下,又刚好遇到了 Sentry 处理生命周期,可能导致 Sentry 发现 id 已经不存在表中,从而返回给客户端全量的数据。

2.2.2.3、处理权限更新

  在上一步获取到了权限更新数据后,update() 方法中就分别对路径更新和权限更新做合并,以路径更新为例,最终调到 UpdateableAuthzPaths 类的 updatePartial 方法

  @Override
  public void updatePartial(Iterable<PathsUpdate> updates, ReadWriteLock lock) {
    ...
      for (PathsUpdate update : updates) {
        applyPartialUpdate(update);
        ...
        seqNum.set(update.getSeqNum());
        ...
      }
    ...
  }
  
  private void applyPartialUpdate(PathsUpdate update) {
    // Handle alter table rename : will have exactly 2 path changes
    // 1 is add path and the other is del path and oldName != newName
    if (update.getPathChanges().size() == 2) {
      List<TPathChanges> pathChanges = update.getPathChanges();
      TPathChanges newPathInfo = null;
      TPathChanges oldPathInfo = null;
      if (pathChanges.get(0).getAddPathsSize() == 1
          && pathChanges.get(1).getDelPathsSize() == 1) {
        newPathInfo = pathChanges.get(0);
        oldPathInfo = pathChanges.get(1);
      } else if (pathChanges.get(1).getAddPathsSize() == 1
          && pathChanges.get(0).getDelPathsSize() == 1) {
        newPathInfo = pathChanges.get(1);
        oldPathInfo = pathChanges.get(0);
      }
      if (newPathInfo != null && oldPathInfo != null &&
              !newPathInfo.getAuthzObj().equalsIgnoreCase(oldPathInfo.getAuthzObj())) {
        paths.renameAuthzObject(
            oldPathInfo.getAuthzObj(), oldPathInfo.getDelPaths(),
            newPathInfo.getAuthzObj(), newPathInfo.getAddPaths());
        return;
      }
    }

  这里的 pathChanges 就是前面的 json 反序列化后的对象,前面列举的是权限变更 json,路径变更的 json 差不多的样子。最后就是根据得到的信息,去更新 paths 对象;同理,权限信息维护的就是 perms 对象,这两个对象就是 SentryAuthorizationProvider 向外提供鉴权能力的基础。

三、NN插件鉴权能力

  SentryAuthorizationProvider 本身只是维护权限信息,不做权限检查,只是向外界提供数据,对 NN 暴露的方法为 getAclFeature 和 getFsPermission。其调用链路为:

FSDirectory#checkPermission ->
FSPermissionChecker#checkPermission ->
AuthorizationProvider#checkPermission ->
SentryAuthorizationProvider#checkPermission ->
DefaultAuthorizationProvider#checkPermission ->
DefaultAuthorizationProvider#check ->
INode#getFsPermission和INode#getAclFeature
分别就调到了SentryAuthorizationProvider的getAclFeature和getFsPermission。

  FsPermission 就是形如 777 这样的权限,AclFeature 就是对 HDFS使用 getfacl 这样的命令后出现的更细粒度的访问权限,定义了用户、组、超级组、mask 等。

3.1、getFsPermission

  @Override
  public FsPermission getFsPermission(
      INodeAuthorizationInfo node, int snapshotId) {
    Preconditions.checkNotNull(node, "node");
    FsPermission permission;
    String[] pathElements = getPathElements(node);
    if (!isSentryManaged(pathElements)) {
      try {
        permission = defaultAuthzProvider.getFsPermission(node, snapshotId);
      } catch (RuntimeException e) {
        LOG.error("### getFsPermission for " + nodePath(node, snapshotId, true) + " failed", e);
        throw e;
      }
    } else {
      FsPermission returnPerm = this.permission;
      // Handle case when prefix directory is itself associated with an
      // authorizable object (default db directory in hive)
      // An executable permission needs to be set on the the prefix directory
      // in this case.. else, subdirectories (which map to other dbs) will
      // not be travesible.
      for (String [] prefixPath : authzInfo.getPathPrefixes()) {
        if (Arrays.equals(prefixPath, pathElements)) {
          returnPerm = FsPermission.createImmutable((short)(returnPerm.toShort() | 0x01));
          break;
        }
      }
      permission = returnPerm;
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug("### getFsPermission for {} : {}", nodePath(node, snapshotId), permission);
    }
    return permission;
  }

  可以从 isSentryManaged 这个条件看到,是 Sentry 进行管理的使用 Sentry 插件维护的权限,否则就使用 DefaulAuthorizationProvider 的权限,而 prefixPath,就是前面提到的/user/hive/warehouse 路径,返回的就是默认的这个路径的权限。主要看的还是 getAclFeature 返回的权限。

3.2、getAclFeature

  这里也是和 getFsPermission 一样,非 Hive 路径下的由 DefaulAuthorizationProvider 管理,而 Sentry 管理的权限核心就是这两行

addToACLMap(aclMap, authzInfo.getAclEntries(pathElements));
f = new SentryAclFeature(ImmutableList.copyOf(aclMap.values()));`

addToACLMap 和 SentryAclFeature 都是封装,直接看 getAclEntries

  public List<AclEntry> getAclEntries(String[] pathElements) {
    ...
      Set<String> authzObjs = authzPaths.findAuthzObject(pathElements);
	  ...
      Set<AclEntry> retSet = new HashSet<>();
	  ...
      // No duplicate acls should be added.
      for (String authzObj: authzObjs) {
        retSet.addAll(authzPermissions.getAcls(authzObj));
      }
    ...
  }

  这里的 pathElements 就是 HDFS路径,根据这个路径,找到验证对象 authzObjs,也就是 Hive 库、表、udf 等,然后每一个验证对象去那对应的 acl 权限。这里就用到的是前面从 Sentry 获取到并一直更新的路径信息和权限信息。
  至此整体流程已经串起来了,其中还有一些细节,比如权限是怎么从 Hive 权限映射成 HDFS权限等插件维护权限信息之类的问题,可以在需要的时候再关注就好了。

四、收尾

  本篇从 NN 的 Sentry 插件怎么同步 Sentry 的权限信息,到如何提供鉴权能力做了整体的追踪,对关键代码进行了解析,能对 Sentry 在最核心的 Hive 和 Hadoop 组件的对接上有更深入的了解,同上一篇结合起来看的话效果会更好。
  如果可以的话希望给个一键三连(。•̀ᴗ-)✧文章来源地址https://www.toymoban.com/news/detail-841718.html

到了这里,关于【sentry 到 ranger 系列】二、Sentry 的 Hadoop 鉴权插件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安装sentry-cli问题

    安装sentry-cli问题 1 使用brew install sentry-cli 安装的时候 有时候会报 Error: Xcode alone is not sufficient on Monterey. 2 使用 curl -sL https://sentry.io/get-cli/ | sh 安装成功 Installed sentry-cli 2.20.5 Done! 查看

    2024年02月11日
    浏览(28)
  • sentry-cli上传dSYM

    一、安装sentry-cli 第一种方法 第二种方法 二、 上传dSYM 1、需要先配置自己的SENTRY_URL: export SENTRY_URL=xxxx //xxxx替换自己的sentry地址 2、执行sentry-cli上传 AUTH_TOKEN 去settings 》 account 〉API 》 Auth tokens获取 sentry-org 去settings 》 sentry 〉general 中获取 projectName 就是项目中对应的名字

    2024年02月11日
    浏览(38)
  • 前端异常监控平台Sentry安装配置使用及问题

    前言:Sentry是一款开源的异常监控平台,支持各种语言的SDK,通过对应SDK可以收集错误信息和性能数据,并可以再后台web页面中查看相关信息。 官方地址: 安装说明:https://develop.sentry.dev/self-hosted/ 后台使用说明:https://docs.sentry.io/product/releases/ SDK使用说明(根据需要选择平台

    2024年02月04日
    浏览(37)
  • 不得不了解的linux网络配置

    1.1.1ifconfig命令—查看网络接口地址 1.1.1.1查看所有网络接口信息 [root@localhost ~]# ifconfig 1.1.1.3查看指定的网络接口信息(不论该网络接口是否处于激活状态) mtu:代表最大传输单元,它的单位是字节。在我们常用的以太网中,MTU一般是1500,而无线路由器默认一般是 1492。 本地MTU值

    2023年04月21日
    浏览(49)
  • CDH数仓项目(三) —— Kerberos安全认证和Sentry权限管理

    本文基于《CDH数仓项目(一) —— CDH安装部署搭建详细流程》和《CDH数仓项目(二) —— 用户行为数仓和业务数仓搭建》和搭建CDH数仓。本章节主要介绍基于CDH数仓的Kerberos认证和Sentry权限管理 Kerberos是一种计算机网络授权协议,用来在非安全网络中,对个人通信以安全的手段进

    2023年04月22日
    浏览(53)
  • 程序员不得不知道的 API 接口常识

    1、初识 API 接口 记得在我初学 web 开发的时候,后端框架相关的教程基本都会教学生写渲染模版(不分语言),也就是说后端返回的是整个网页的数据,浏览器只负责渲染。 一般这类模版在后端都会对应一个路由,比如前端想登入一个看用户信息的页面,在 url 中输入的访问

    2024年02月01日
    浏览(50)
  • Anaconda你不得不知道的若干知识点

    多半是h5py的版本不对 重新安装适配对应的tensorflow 办法一: 卸载新版本 pip uninstall xlrd 安装老版本:pip install xlrd=1.2.0 (或者更早版本) 方法二: 将xlrd用到的excel版本格式修改为xls(保险起见,另存为xls格式) 建议:个人更推荐使用第二种方法 方法三: 利用openpyxl代替xlrd打开

    2024年02月01日
    浏览(41)
  • 【C++代码之美】你不得不知道的经典代码

    斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、…… 如下代码是15阶的斐波那契数列: 代码如下: 输出结果: 打印

    2024年02月01日
    浏览(49)
  • sentry收集错误[Failed to fetch dynamically imported module]解决

    vue3 + vite创建的项目在引入sentry后,邮箱会经常收到错误:[Failed to fetch dynamically imported module] 分析:错误出现的时间点大致在项目每一次重新部署之后。 原因:每次打包,会生成新文件名称不同的文件。浏览器当下的 script 中会引用之前打包的文件,文件不存在就会报以上错

    2024年02月13日
    浏览(46)
  • 程序员不得不了解的计算机进制转换

    最近在备考软考的软件设计师考试,学到了关于计算机的数据表示,由于我是半路出家学的Java,导致计算机基础知识很差,在这里记录一下学习感受 早期计算机的存储介质是晶体管,晶体管根据电压不同,只能表示2种状态,也就是0和1 计算机使用二进制运算更加方便 更详细

    2024年02月05日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包