作者:辉少
1.文档编写目的
在Kerberos环境中,我们的应用程序通过Java代码来提交任务需要先进行Kerberos凭证的初始化然后进行应用程序的提交,本文档主要讲述Java应用程序中读取krb5.conf 中配置ticket_lifetime 参数不生效的异常分析。
- 测试环境
1.CM和CDH版本为5.15.1
2.操作系统版本为RedHat7.2
3.集群已启用Kerberos
4.JDK 1.8.131
2.问题描述
首先我们先在Linux 上Kerberos 客户端机器上修改ticket_lifetime和renew_lifetime 验证该参数的有效性,将ticket_lifetime 设置为20s,为了避免超时自动续期将renew_lifetime也设置为20S
ticket_lifetime = 20s (默认值24h)renew_lifetime = 20s (默认值7d)
![5d8b29b959ed407cc7bed4c113e0d133.png](https://i-blog.csdnimg.cn/blog_migrate/83925b2c824a9cb3fc95b6c01083fdd1.jpeg)
然后执行HDFS文件查看命令去查看,可以看到第一次成功,而第二次显示没有找到Kerberos凭证,说明已经过期,而我们在上图中可以看到过期时间为17:24:52,第二次命令执行的时间为17:24:59 ,表明在Linux的客户端机器上正常执行
![b1ca3a6ef486112d694aee378a2ee574.png](https://i-blog.csdnimg.cn/blog_migrate/65e05a7c4e6c54b4ab9661e76bf74431.jpeg)
然后看在Java代码中的表现,首先修改krb5.conf的配置
![7df11d6e4e0656af2d9082831c03ed3c.png](https://i-blog.csdnimg.cn/blog_migrate/a844bc9c82d174cb303b875fe2089e61.jpeg)
代码如下:
![ef9fa487ef52de7b3e891d93ca9e265f.png](https://i-blog.csdnimg.cn/blog_migrate/185bff7c4e92146b8ce996b79359d8ea.jpeg)
然后执行,这里在获取凭证后延时了30S,再执行创建文件命令
![8e6481f593a663506a36688c21003074.png](https://i-blog.csdnimg.cn/blog_migrate/288b03e3cc4706dfb1b275eda93b9f3e.jpeg)
发现依旧创建成功,没有任何异常,并且打印的凭证过期时间为1天后的时间,说明在krb5.conf 中设置的ticket_lifetime 是无效的
![6c9595c5975784a0ca6fc946a1bebf0c.png](https://i-blog.csdnimg.cn/blog_migrate/3916461153e1d21837109b368304353e.jpeg)
![bc23424b0729ba46dd5566a8aa00bd69.png](https://i-blog.csdnimg.cn/blog_migrate/a170d9ed0be965560ecf9aa7ab8400e2.jpeg)
![48a10437f7e198a2cfdb32198d9bfbaf.png](https://i-blog.csdnimg.cn/blog_migrate/28e0735be3f38a7f8f90e59343e0ce66.jpeg)
3.问题分析
基于上述在Java代码中修改ticket_lifetime不生效的问题,起初认为是Windows客户端的问题,于是将代码和依赖包一起打jar包上传到Linux 系统的Kerberos客户端机器上执行,并且修改/etc/krb5.conf 的ticket_lifetime 和renew_lifetime 设置为20S
java -jar hdfs-demo-1.0-SNAPSHOT-jar-with-dependencies.jar
![3cbed7b97e384e778b525d47960ca032.png](https://i-blog.csdnimg.cn/blog_migrate/252c925e3179e6041b6ebfa06cee2722.jpeg)
发现凭证的有效期仍然是1天,并且文件夹创建成功,无任何异常。
![2a5471b5c64b4b6cd84b87f43c48aa72.png](https://i-blog.csdnimg.cn/blog_migrate/4b438c3cbccca5b62343460fd630a37e.jpeg)
这说明并不是客户端机器的问题,而是Java程序的问题,于是便DEBUG代码进行分析
发现在UGI类中,执行完UGI. loginUserFromKeytab()函数中执行login.login() 函数后,在对象 subject 中发现凭证信息,并且有效期显示1天
![de2661104ad522ff9d0354979074738c.png](https://i-blog.csdnimg.cn/blog_migrate/0969eebaded506e94fca034c94db1a26.jpeg)
DEBUG到KerberosUtils类查看,发现获取Realm 和 获取服务凭证相关的函数,也没有任何关于凭证效期的函数,继续往下看
![9e7c3d0ef1b712eb7d5607d531454a66.png](https://i-blog.csdnimg.cn/blog_migrate/2e78ce601bdf0b9d9d3b7b0a0dab7ebf.jpeg)
一直下一步看下去,找到一个Krb5LoginModule类,于是查看一下,该类中也没有任何有关ticket_lifetime 的属性或者函数
![712aa0d7424262d2010cabf9780fd455.png](https://i-blog.csdnimg.cn/blog_migrate/4ff0256ed4ed2272ba471552b059ff6e.jpeg)
在前面的一步我们已经知道凭证的有效期是在login.login() 函数后就能在subject对象中看到的,而login.login()实际上调用的是LoginContext 类中的invokePriv()函数,而invokePriv()函数实际调用的是invoke() 函数。
![a9ddd3d8bb977df90c060dfff702f3e1.png](https://i-blog.csdnimg.cn/blog_migrate/2c196c45dbcbdbcd8ff9b70177264b5e.jpeg)
invokePriv() 函数如下:
![19ccc9d3a52d7611b554c55b7ba5288c.png](https://i-blog.csdnimg.cn/blog_migrate/5075a39ba8c052841d29a4462dabbb5b.jpeg)
在下图中我们可以看到invoke() 函数还没执行完成,凭证的信息包括有效期止的日期已经出来,但是没有具体时间,凭证的startTime为null ,但是endTime 已经显示为1天后。说明执行login.login()的时候就已经指定了凭证的有效期日期为1天,也就是程序并没有读取ticket_lifetime 和renew_lifetime 参数。所以不论你krb5.conf 中是否有ticket_lifetime 和renew_lifetime 参数并不会改变凭证的有效时间。invoke() 函数如下:
![b9ada3b9f1240e158f6a048c4cb8f8b0.png](https://i-blog.csdnimg.cn/blog_migrate/b8073acfd66ce254cf39f4cb554166f9.jpeg)
为了验证程序并没有读取ticket_lifetime 和renew_lifetime 参数,我们使用设置系统参数的方式来验证,注释掉krb5.conf 文件然后执行。
System.setProperty("java.security.krb5.kdc