问题描述

业务需要一个长期运行的程序,将上传的文件存放至HDFS,程序启动后,刚开始一切正常,执行一段时间(一般是一天,有的现场是三天),就会出现认证错误,用的JDK是1.8,hadoop-client,对应的版本是2.5.1,为什么强调这个版本号,因为错误的根本原因就在于版本问题

错误日志

  1. Caused by: org.ietf.jgss.GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
  2. at sun.security.jgss.krb5.Krb5InitCredential.getInstance(Krb5InitCredential.java:147) ~[?:1.8.0_212]
  3. at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:122) ~[?:1.8.0_212]
  4. at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:187) ~[?:1.8.0_212]
  5. at sun.security.jgss.GSSManagerImpl.getMechanismContext(GSSManagerImpl.java:224) ~[?:1.8.0_212]
  6. at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:212) ~[?:1.8.0_212]
  7. at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179) ~[?:1.8.0_212]
  8. at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:192) ~[?:1.8.0_212]
  9. at org.apache.hadoop.security.SaslRpcClient.saslConnect(SaslRpcClient.java:413) ~[hadoop-common-2.5.1.jar:?]
  10. at org.apache.hadoop.ipc.Client$Connection.setupSaslConnection(Client.java:552) ~[hadoop-common-2.5.1.jar:?]
  11. at org.apache.hadoop.ipc.Client$Connection.access$1800(Client.java:367) ~[hadoop-common-2.5.1.jar:?]
  12. at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:717) ~[hadoop-common-2.5.1.jar:?]
  13. at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:713) ~[hadoop-common-2.5.1.jar:?]
  14. at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_212]
  15. at javax.security.auth.Subject.doAs(Subject.java:422) ~[?:1.8.0_212]
  16. at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1614) ~[hadoop-common-2.5.1.jar:?]
  17. at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:712) ~[hadoop-common-2.5.1.jar:?]
  18. at org.apache.hadoop.ipc.Client$Connection.access$2800(Client.java:367) ~[hadoop-common-2.5.1.jar:?]
  19. at org.apache.hadoop.ipc.Client.getConnection(Client.java:1463) ~[hadoop-common-2.5.1.jar:?]
  20. at org.apache.hadoop.ipc.Client.call(Client.java:1382) ~[hadoop-common-2.5.1.jar:?]
  21. ... 61 more

业务程序调用认证方法


  1. public void init() {
  2. System.setProperty("java.security.krb5.conf", "krb5.conf");
  3. }
  4. public void kerberosLogin() throws IOException {
  5. // 已经认证通过
  6. if ("hdfsuser".concat("@").concat("DATAHOUSE.COM")
  7. .equals(UserGroupInformation.getCurrentUser().getUserName())) {
  8. UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();
  9. return;
  10. }
  11. // ksbName 表示用户名 keytabPath表示秘钥存放位置
  12. UserGroupInformation.loginUserFromKeytab("hdfsuser", "/etc/keytab/hdfsuser.keytab");
  13. }
  • 主要思想就是第一次认证通过loginUserFromKeytab进行认证,之后每次请求再调用checkTGTAndReloginFromKeytab方法判断是否需要重新认证,防止ticket过期

  • 应用在每次获取FileSystem时,都会先调用kerberosLogin,之后才获取FileSystem

  1. public FileSystem getFileSystem() throws IOException {
  2. try {
  3. kerberosLogin();
  4. return FileSystem.get(configuration);
  5. } catch (Exception e) {
  6. logger.error("create hdfs FileSystem has error", e);
  7. throw e;
  8. }
  9. }

问题调查过程

根据错误在网上各种搜索,出来的结果和上面的代码大同小异,有的猜测是客户端调用间隔太大,超过了ticket_lifetime的值,建议加一个定时任务来周期性的调用kerberosLogin()方法,虽然我们业务不太可能出现这种情况,还是加上了这个处理,问题依旧,只好开始慢慢调试

UserGroupInformation.loginUserFromKeytab的认证过程

  1. UserGroupInformation.loginUserFromKeytab 利用传入的user和keytab路径信息,构建一个LoginContext,接着调用LoginContext的login方法

try {

login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME,

subject, new HadoopConfiguration());

start = Time.now();

login.login();

  1. 。。。
  2. ```
  1. LoginContext.login方法依次通过反射调用了登陆模块的login和commit两个方法,调用的主要逻辑在invokePriv方法内

    1. public void login() throws LoginException {
    2. ...
    3. try {
    4. // module invoked in doPrivileged
    5. invokePriv(LOGIN_METHOD);
    6. invokePriv(COMMIT_METHOD);
    7. ...
  2. LoginContext.invokePriv方法主要在doPrivileged内调用invoke方法,invoke方法依次调用登陆模块对应的方法,第一次调用时,还会调用对应的initialize方法

    1. for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
    2. try {
    3. ...
    4. // 查找initialize方法
    5. methods = moduleStack[i].module.getClass().getMethods();
    6. for (mIndex = 0; mIndex < methods.length; mIndex++) {
    7. if (methods[mIndex].getName().equals(INIT_METHOD)) {
    8. break;
    9. }
    10. }
    11. Object[] initArgs = {subject,
    12. callbackHandler,
    13. state,
    14. moduleStack[i].entry.getOptions() };
    15. // 调用 initialize 方法
    16. methods[mIndex].invoke(moduleStack[i].module, initArgs);
    17. }
    18. // 接着查找相应的方法
    19. for (mIndex = 0; mIndex < methods.length; mIndex++) {
    20. if (methods[mIndex].getName().equals(methodName)) {
    21. break;
    22. }
    23. }
    24. // set up the arguments to be passed to the LoginModule method
    25. Object[] args = { };
    26. // 调用相应的方法
    27. boolean status = ((Boolean)methods[mIndex].invoke
    28. (moduleStack[i].module, args)).booleanValue();

  1. 实际执行时对应的moduleStack中有两个LoginModule
  2. * HadoopLoginModule :和kerberos认证关系不大,暂且不看
  3. * Krb5LoginModule kerberos认证类,根据第2LoginContext.login中的方法可知,会依次调用这个module中的logincommit两个方法
  4. 1. Krb5LoginModule.login方法,就是利用我们提供的user名称和krb5.conf中的配置信息以及keytab信息进行认证。代码就不展示了,主要是调用attemptAuthentication进行的处理。
  5. 1. Krb5LoginModule.commit方法是要把认证后证书信息存入到Subject中,以便后续能重复使用subject进行认证,和本次调查问题有关的代码片段如下
  6. ```
  7. public boolean commit() throws LoginException {
  8. Set<Object> privCredSet = subject.getPrivateCredentials();
  9. 。。。
  10. if (ktab != null) {
  11. if (!privCredSet.contains(ktab)) {
  12. // 把keytab保存下来,再次认证使用
  13. privCredSet.add(ktab);
  14. }
  15. } else {
  16. succeeded = false;
  17. throw new LoginException("No key to store");
  18. }
  19. 。。。
  20. ```
  21. 1. 按照这个逻辑,既然keytab保存到Subject中了,再次使用UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();进行认证时,就可以使用保存的keytab直接认证了,应该是不会出错的,我们看下checkTGTAndReloginFromKeytab方法
  22. ```
  23. public synchronized void checkTGTAndReloginFromKeytab() throws IOException {
  24. if (!isSecurityEnabled()
  25. || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
  26. || !isKeytab)
  27. return;
  28. KerberosTicket tgt = getTGT();
  29. if (tgt != null && Time.now() < getRefreshTime(tgt)) {
  30. return;
  31. }
  32. reloginFromKeytab();
  33. }
  34. ```
  35. 方法逻辑,就是判断如果是用keytab进行的认证,就调用reloginFromKeytab进行认证。但在实际执行时却发现isKeytab的值是false,可代码明明是使用keytab来认证的,怎么是false呢,只能看看isKeytab这个值怎么赋值的了,对应逻辑在UserGroupInformation的构造函数里
  36. ```
  37. UserGroupInformation(Subject subject) {
  38. ...
  39. this.isKeytab = !subject.getPrivateCredentials(KerberosKey.class).isEmpty();
  40. ...
  41. }
  42. ```
  43. 1. 至此终于发现问题所在,我们在第5步,认证成功后在subjectPrivateCredentials中存入的是keytab对象,而这个地方判断的是KerberosKey,这肯定是不一样呀,那就只有一种可能,就是引用jar包的版本问题了。更换hadoop-client的版本号为2.10.0,再查看UserGroupInformation对应的构造函数
  44. ```
  45. private UserGroupInformation(Subject subject, final boolean externalKeyTab) {
  46. ...
  47. this.isKeytab = KerberosUtil.hasKerberosKeyTab(subject);
  48. ...
  49. }

将判断逻辑移到了KerberosUtil.hasKerberosKeyTab方法中

  1. /**
  2. * Check if the subject contains Kerberos keytab related objects.
  3. * The Kerberos keytab object attached in subject has been changed
  4. * from KerberosKey (JDK 7) to KeyTab (JDK 8)
  5. *
  6. *
  7. * @param subject subject to be checked
  8. * @return true if the subject contains Kerberos keytab
  9. */
  10. public static boolean hasKerberosKeyTab(Subject subject) {
  11. return !subject.getPrivateCredentials(KeyTab.class).isEmpty();
  12. }
  13. ```
  14. 可以看到判断对象已经变成了KeyTab了,并且从注释信息中明确看到在JDK7时使用的是KerberosKey,在JDK8时换成了KeyTab。
  15. 总结,kerberos认证功能虽然强大,实际使用还是有点复杂,特别是和jaas结合后,出了错还是有些难调查,可只要慢慢分析,还是会找到解决方法的,还有一点就是虽然程序出现的错误一样,引起错误的根本原因还是会有所不同,不能只是按照网上说法一改就万事大吉,有时还是需要靠我们自己刨根问底好好研究。

hdfs/hbase 程序利用Kerberos认证超过ticket_lifetime期限后异常的更多相关文章

  1. hadoop的kerberos认证

    言归正传,介绍过hadoop的simple认证和kerberos后,我们在这一章介绍hadoop的kerberos认证 我们还使用hadoop集群的机器. OS 版本: Centos6.4 Kerbe ...

  2. 使用Spark的newAPIHadoopRDD接口访问有kerberos认证的hbase

    使用newAPIHadoopRDD接口访问hbase数据,网上有很多可以参考的例子,但是由于环境使用了kerberos安全加固,spark使用有kerberos认证的hbase,网上的参考资料不多,访 ...

  3. 通过BulkLoad快速将海量数据导入到Hbase(TDH,kerberos认证)

    一.概念 使用BlukLoad方式利用Hbase的数据信息是 按照特点格式存储在HDFS里的特性,直接在HDFS中生成持久化的Hfile数据格式文件,然后完成巨量数据快速入库的操作,配合MapRedu ...

  4. cloudera集群开启kerberos认证后,删除zk中的/hbase目录

    问题 在cdh集群中开启了kerberos认证,hbase集群出现一点问题,需要通过zookeeper-client访问zookeeper,删除/hbase节点时候报错:Authentication ...

  5. java操作HDFS相关demo(TDH,kerberos认证)

    public class Test {     private static Configuration conf;     private static FileSystem fs;     //开 ...

  6. 配置两个Hadoop集群Kerberos认证跨域互信

    两个Hadoop集群开启Kerberos验证后,集群间不能够相互访问,需要实现Kerberos之间的互信,使用Hadoop集群A的客户端访问Hadoop集群B的服务(实质上是使用Kerberos Re ...

  7. spark 2.x在windows环境使用idea本地调试启动了kerberos认证的hive

    1 概述 开发调试spark程序时,因为要访问开启kerberos认证的hive/hbase/hdfs等组件,每次调试都需要打jar包,上传到服务器执行特别影响工作效率,所以调研了下如何在window ...

  8. CDH构建大数据平台-配置集群的Kerberos认证安全

     CDH构建大数据平台-配置集群的Kerberos认证安全 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 当平台用户使用量少的时候我们可能不会在一集群安全功能的缺失,因为用户少,团 ...

  9. Cloudera Hadoop启用Kerberos认证

    一.Kerberos 二.安装 node01服务器安装Kerberos的核心服务master KDC,node02和node03安装Kerberos client cm也安装在node01上了 1.m ...

随机推荐

  1. H3C 配置基本ACL

  2. java TreeSet的排序之自然排序

    TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合里的元素按升序排列.此时需要排序元素的类必须实现Compareble接口,并覆写其int com ...

  3. npm install 报错(npm ERR! errno -4048,Error: EPERM: operation not permitted)

    问题现象 原因 1.初次看报错日志内容,定义权限为问题,后来查资料才知道是缓存问题. 解决方法 1.简单直接 直接删除 npmrc文件 tips: 不是nodejs安装目录npm模块下的那个npmrc ...

  4. MSBuild 常用参数

    本文告诉大家在 MSBuild 里面常用的参数 一般的 msbuild 在编译的时候都会添加很多参数,用法如下 进入对应编译的 sln 或 csproj 文件所在的文件夹,执行下面命名 msbuild ...

  5. linux 内核协助的探测

    Linux 内核提供了一个低级设施来探测中断号. 它只为非共享中断, 但是大部分能够在共 享中断状态工作的硬件提供了更好的方法来尽量发现配置的中断号.这个设施包括 2 个函 数, 在<linux ...

  6. [板子]SPFA算法+链式前向星实现最短路及负权最短路

    参考:https://blog.csdn.net/xunalove/article/details/70045815 有关SPFA的介绍就掠过了吧,不是很赞同一些博主说是国内某人最先提出来,Bellm ...

  7. P3803 FFT求多项式系数

    P3803 FFT求多项式系数 传送门:https://www.luogu.org/problemnew/show/P3803 题意: 这是一道FFT模板题,求多项式系数 题解: 对a和b的系数求一个 ...

  8. centos 利用mailx发送邮件

    这里就已163或者126邮箱为例!阿里云的25号端口好像发送不了,用465端口可以发送成功! 安装:yum install -y mailx 然后就是修改配置文件 set ssl-verify=ign ...

  9. boostrap-非常好用但是容易让人忽略的地方【3】:clearfix

    代码 显示结果 代码 结果

  10. MFC入门

    目录 001.MFC_应用程序类型    002.MFC_对话框_静态文本_编辑框  003.MFC_对话框_访问控件_7种方法_A   004.MFC_对话框_访问控件_7种方法_B   005.M ...