1. Can't get Kerberos realm

原因分析:

原始代码为:

1

2

org.apache.hadoop.security.UserGroupInformation.setConfiguration(conf)

sun.security.krb5.Config.refresh()

  

首先根据传进来的Hadoop配置conf,去设置UserGroupInformation(UGI),方法的调用关系如下(删除了部分不相关代码):

1

2

3

public static void setConfiguration(Configuration conf) {

  initialize(conf, true);

}

initialize方法如下

1

2

3

4

5

6

7

8

9

10

11

12

private static synchronized void initialize(Configuration conf, boolean overrideNameRules) {

  authenticationMethod = SecurityUtil.getAuthenticationMethod(conf);

  if (overrideNameRules || !HadoopKerberosName.hasRulesBeenSet()) {

    try {

      HadoopKerberosName.setConfiguration(conf);

    catch (IOException ioe) {

      throw new RuntimeException(

          "Problem with Kerberos auth_to_local name configuration", ioe);

    }

  }

  ......

}

  

setConfiguration方法如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public static void setConfiguration(Configuration conf) throws IOException {

  final String defaultRule;

  switch (SecurityUtil.getAuthenticationMethod(conf)) {

    case KERBEROS:

    case KERBEROS_SSL:

      try {

        KerberosUtil.getDefaultRealm();

      catch (Exception ke) {

        throw new IllegalArgumentException("Can't get Kerberos realm", ke);

      }

      ......

  }

  ......

}

  

getDefaultRealm使用了反射,目的是为了兼容两套jdk,即IBM(com.ibm.security.krb5.internal.Config) 和 Oracle(sun.security.krb5.Config)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public static String getDefaultRealm()

    throws ClassNotFoundException, NoSuchMethodException,

    IllegalArgumentException, IllegalAccessException,

    InvocationTargetException {

  Object kerbConf;

  Class<?> classRef;

  Method getInstanceMethod;

  Method getDefaultRealmMethod;

  if (System.getProperty("java.vendor").contains("IBM")) {

    classRef = Class.forName("com.ibm.security.krb5.internal.Config"); // 获取IBM jdk的类引用

  else {

    classRef = Class.forName("sun.security.krb5.Config"); // 获取Oracle jdk的类引用

  }

  getInstanceMethod = classRef.getMethod("getInstance"new Class[0]);

  kerbConf = getInstanceMethod.invoke(classRef, new Object[0]);

  getDefaultRealmMethod = classRef.getDeclaredMethod("getDefaultRealm"new Class[0]);

  return (String)getDefaultRealmMethod.invoke(kerbConf, new Object[0]);

}

  

从上述代码来看,先获取Config类引用,然后getInstanceMethod是获得getInstance方法,再次getDefaultRealmMethod是获得getDefaultRealm方法。

因此,假设我们是使用的Oracle的JDK,那么最后是调用的sun.security.krb5.getDefaultRealm()。接下来看一下sun.security.krb5.getDefaultRealm()是如何实现的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public String getDefaultRealm() throws KrbException {

  if(this.defaultRealm != null) { // 如果defaultRealm不为空,直接返回defaultRealm

    return this.defaultRealm;

  else // 如果defaultRealm为null,获取defaultRealm

    KrbException var1 = null;

    String var2 = this.getDefault("default_realm""libdefaults");

    if(var2 == null && this.useDNS_Realm()) {

      try {

        var2 = this.getRealmFromDNS();

      catch (KrbException var4) {

        var1 = var4;

      }

    }

    ......

  }

我们假设defaultRealm = null,看一下如何从var2 = this.getRealmFromDNS();来获取defaultRealm

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

private String getRealmFromDNS() throws KrbException {

  String var1 = null;

  String var2 = null;

  try {

    var2 = InetAddress.getLocalHost().getCanonicalHostName(); // 1. 获取local host name

  catch (UnknownHostException var7) {

    KrbException var4 = new KrbException(60"Unable to locate Kerberos realm: " + var7.getMessage());

    var4.initCause(var7);

    throw var4;

  }

  String var3 = PrincipalName.mapHostToRealm(var2); // 2. 根据local host name获取realm

  ....

mapHostToRealm()方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

static String mapHostToRealm(String var0) {

  String var1 = null;

  try {

    String var2 = null;

    Config var3 = Config.getInstance(); // 获取Config的单例对象

    if((var1 = var3.getDefault(var0, "domain_realm")) != null) {

      return var1;

    }

    .......

  catch (KrbException var5) {

    ;

  }

  return var1;

}

  

这里会获取Config的单例对象,

1

2

3

4

5

6

7

public static synchronized Config getInstance() throws KrbException {

  if(singleton == null) {

    singleton = new Config();

  }

  return singleton;

}

再看Config.getInstance();的具体动作就是判断单例对象是否为null,不为null直接返回,为null重新new一个Config对象。

同时,Config类中还有一个方法refresh,其代码如下:

1

2

3

4

public static synchronized void refresh() throws KrbException {

  singleton = new Config();

  KdcComm.initStatic();

}

  

从refresh的代码看,只要调用refresh()方法,就会重新生成Config的单例对象。这个refresh()方法,也是我们代码里面要调用的。

再回顾一下我们的原始代码:

1

2

org.apache.hadoop.security.UserGroupInformation.setConfiguration(conf)

sun.security.krb5.Config.refresh()

回到getInstance()方法,假设singleton单例是null,会生成Config的单例对象。以后,再次调用getInstance方法都会直接返回这个单例对象了,没有再new的机会了。有人开始质疑没有机会new Config()对象了? 调用Config.refresh()方法不是可以new吗? 答案是可以new,但是如果我们的UserGroupInformation.setConfiguration(conf)会抛出异常,是不是Config.refresh()方法就不会被调用了! 我们的错误就是出现在这里,后面分析UserGroupInformation.setConfiguration(conf)怎么抛出异常了。

在我们来看一下new Config()具体做了什么事情。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

private Config() throws KrbException {

  String var1 = getProperty("java.security.krb5.kdc"); // 从系统变量获取kdc地址,假设我们启动JVM时没有设置该变量

  if(var1 != null) {

    this.defaultKDC = var1.replace(':'' ');

  else {

    this.defaultKDC = null;

  }

  this.defaultRealm = getProperty("java.security.krb5.realm"); // 从系统变量获取realm,假设我们启动JVM时也没有设置该变量

  if((this.defaultKDC != null || this.defaultRealm == null) && (this.defaultRealm != null || this.defaultKDC == null)) {

    try {

      String var3 = this.getJavaFileName(); // 该方法会从JVM参数java.security.krb5.conf以及<java-home>/lib/security/krb5.conf获取到krb5.conf文件

      Vector var2;

      if(var3 != null) {

        var2 = this.loadConfigFile(var3);

        this.stanzaTable = this.parseStanzaTable(var2);

        if(DEBUG) {

          System.out.println("Loaded from Java config");

        }

      else // 假设JVM参数java.security.krb5.conf以及<java-home>/lib/security/krb5.conf都没有获取到krb5.conf文件

        boolean var4 = false;

        if(isMacosLionOrBetter()) {

          try {

            this.stanzaTable = SCDynamicStoreConfig.getConfig();

            if(DEBUG) {

              System.out.println("Loaded from SCDynamicStoreConfig");

            }

            var4 = true;

          catch (IOException var6) {

            ;

          }

        }

        if(!var4) {

          var3 = this.getNativeFileName(); // 我们是centos机器, 会拿到/etc/krb5.conf

          var2 = this.loadConfigFile(var3); // 加载/etc/krb5.conf文件

          this.stanzaTable = this.parseStanzaTable(var2);

          if(DEBUG) {

            System.out.println("Loaded from native config");

          }

        }

      }

    catch (IOException var7) {

      ;

    }

  else {

    throw new KrbException("System property java.security.krb5.kdc and java.security.krb5.realm both must be set or neither must be set.");

  }

}

  

我们的问题就出在var2 = this.loadConfigFile(var3); 位置,因为加载/etc/krb5.conf文件的时候,恰好/etc/krb5.conf文件不存在,因为我们会把修改的krb5.conf去替换/etc/krb5.conf文件,在替换的时间内,恰好去loadConfigFile(),该方法就报了FileNotFoundException的异常。这个异常一直throw到UserGroupInformation.setConfiguration(conf)调用的地方,导致我们永远调用不到Config.refresh()方法。

2. 报错com.google.common.util.concurrent.UncheckedTimeoutException: java.util.concurrent.TimeoutException

原因分析:首先这个异常是因为调试上述报错产生的,所以顺便分析下原因。

上述报错是Can't get Kerberos realm,网上查一下,大概是因为拿不到kdc和realm。

因此,我在JVM启动参数中添加了如下3个参数:

1

2

3

-Djava.security.krb5.conf=/etc/krb5.conf \

-Djava.security.krb5.kdc=node1:8080 \

-Djava.security.krb5.realm=KFC.com \

指定了krb5.conf文件,kdc地址,realm值。然后重启程序,发现可以正常使用,然后把/etc/krb5.conf文件删除了(上个错误其实猜想到了是因为读不到krb5.conf造成的)。

程序竟然报错 java.util.concurrent.TimeoutException,打jstack

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

TimeoutException 的jstack如下:

"builtin-checker-serviceId-58" prio=10 tid=0x00007f678800e800 nid=0x4084 waiting for monitor entry [0x00007f672fffe000]

   java.lang.Thread.State: BLOCKED (on object monitor)

        at org.apache.hadoop.security.UserGroupInformation.loginUserFromKeytabAndReturnUGI(UserGroupInformation.java:1074)

        - waiting to lock <0x00000000a8b940d0> (a java.lang.Class for org.apache.hadoop.security.UserGroupInformation)

        ......       

        at java.util.concurrent.FutureTask.run(FutureTask.java:262)

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

调用UserGroupInformation.loginUserFromKeytabAndReturnUGI被block了

往上找jstack,

"builtin-checker-serviceId-59" prio=10 tid=0x00007f67680b3800 nid=0x4097 runnable [0x00007f672f2ee000]

   java.lang.Thread.State: RUNNABLE

        at java.net.PlainDatagramSocketImpl.receive0(Native Method)

        - locked <0x000000009a0076e0> (a java.net.PlainDatagramSocketImpl)

        at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:146)

        - locked <0x000000009a0076e0> (a java.net.PlainDatagramSocketImpl)

        at java.net.DatagramSocket.receive(DatagramSocket.java:816)

        - locked <0x000000009a017848> (a java.net.DatagramPacket)

        - locked <0x000000009a0076a0> (a java.net.DatagramSocket)

        at sun.security.krb5.internal.UDPClient.receive(NetClient.java:207)  // 卡主了

        at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:390)

        at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:343)

        at java.security.AccessController.doPrivileged(Native Method)

        at sun.security.krb5.KdcComm.send(KdcComm.java:327)

        at sun.security.krb5.KdcComm.send(KdcComm.java:219)

        at sun.security.krb5.KdcComm.send(KdcComm.java:191)

        at sun.security.krb5.KrbAsReqBuilder.send(KrbAsReqBuilder.java:319)

        at sun.security.krb5.KrbAsReqBuilder.action(KrbAsReqBuilder.java:364)

        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:735)

        at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:584)

        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

        at java.lang.reflect.Method.invoke(Method.java:606)

        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:762)

        at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)

        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:690)

        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:688)

        at java.security.AccessController.doPrivileged(Native Method)

        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:687)

        at javax.security.auth.login.LoginContext.login(LoginContext.java:595)

        at org.apache.hadoop.security.UserGroupInformation.loginUserFromKeytabAndReturnUGI(UserGroupInformation.java:1092)

        - locked <0x00000000a8b940d0> (a java.lang.Class for org.apache.hadoop.security.UserGroupInformation)

        ........

        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

        at java.lang.Thread.run(Thread.java:745)

  

从jstack中看到UDPClient.receive卡主了,为什么卡主了,不知道! 问大神,大神说加入JVM调试参数-Dsun.security.krb5.debug=true,可以打印日志到console中。在console中看到如下日志:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

Ordering keys wrt default_tkt_enctypes list

default etypes for default_tkt_enctypes: 3 1 16.

default etypes for default_tkt_enctypes: 3 1 16.

>>> KrbAsReq creating message

>>> KrbKdcReq send: kdc=node1    UDP:88, timeout=30000, number of retries =3, #bytes=134

>>> KDCCommunication: kdc=node1   UDP:88, timeout=30000,Attempt =1, #bytes=134

SocketTimeOutException with attempt: 1

>>> KDCCommunication: kdc=node1   UDP:88, timeout=30000,Attempt =2, #bytes=134

SocketTimeOutException with attempt: 2

>>> KDCCommunication: kdc=node1   UDP:88, timeout=30000,Attempt =3, #bytes=134

SocketTimeOutException with attempt: 3

>>> KrbKdcReq send: error trying node1 

java.net.SocketTimeoutException: Receive timed out

        at java.net.PlainDatagramSocketImpl.receive0(Native Method)

        at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:146)

        at java.net.DatagramSocket.receive(DatagramSocket.java:816)

        at sun.security.krb5.internal.UDPClient.receive(NetClient.java:207)

        at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:390)

        at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:343)

        at java.security.AccessController.doPrivileged(Native Method)

        at sun.security.krb5.KdcComm.send(KdcComm.java:327)

        at sun.security.krb5.KdcComm.send(KdcComm.java:219)

        at sun.security.krb5.KdcComm.send(KdcComm.java:191)

        at sun.security.krb5.KrbAsReqBuilder.send(KrbAsReqBuilder.java:319)

        at sun.security.krb5.KrbAsReqBuilder.action(KrbAsReqBuilder.java:364)

        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:735)

        at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:584)

        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

        at java.lang.reflect.Method.invoke(Method.java:606)

        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:762)

        at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)

        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:690)

        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:688)

        at java.security.AccessController.doPrivileged(Native Method)

        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:687)

        at javax.security.auth.login.LoginContext.login(LoginContext.java:595)

        at org.apache.hadoop.security.UserGroupInformation.loginUserFromKeytabAndReturnUGI(UserGroupInformation.java:1092)

        ........

看到默认去连了KDC的88端口,默认端口被改成了1088,所以连接失败,导致超时。 听说没有参数可以设置KDC的端口, 不知道真假,在-Djava.security.krb5.kdc参数中指定kdc端口无效。

参考: https://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/KerberosReq.html 及源代码

Kerberos认证代码分析Can't get Kerberos realm的更多相关文章

  1. kerberos认证协议分析

    Kerberos认证协议分析 Kerberos认证协议流程 如上图: * 第一步:client和认证服务器(AS)通信完成认证过程,如果认证成功AS返回给client一个TGT(用来向TGS获取tic ...

  2. Kerberos认证与攻击学习总结

    0.背景 聆听了n1nty大佬(90后黑客代表)的谆谆指导,学习了n1nty大佬的基本操作,决定总结一下,做一个简要的读书笔记,也把之前自己记录的关于Windows的安全的一些博客能够串联起来.所以首 ...

  3. Kerberos认证浅析

    1 引言 在希腊神话中Kerberos是守护地狱之门的一条凶猛的三头神犬,而我们在本文中所要介绍的Kerberos认证协议是由美国麻省理工学院(MIT)首先提出并实现的,是该校雅典娜计划的一部分.这个 ...

  4. 01: kerberos认证原理

    1.1 kerberos认证浅析 1.kerberos定义 1. Kerberos 是一种网络认证协议,其设计目标是通过密钥系统为客户机 / 服务器应用程序提供强大的认证服务. 2. Kerberos ...

  5. hdfs/hbase 程序利用Kerberos认证超过ticket_lifetime期限后异常

    问题描述 业务需要一个长期运行的程序,将上传的文件存放至HDFS,程序启动后,刚开始一切正常,执行一段时间(一般是一天,有的现场是三天),就会出现认证错误,用的JDK是1.8,hadoop-clien ...

  6. Java Api Consumer 连接启用Kerberos认证的Kafka

    java程序连接到一个需要Kerberos认证的kafka集群上,消费生产者生产的信息,kafka版本是2.10-0.10.0.1: Java程序以maven构建,(怎么构建maven工程,可去问下度 ...

  7. yarn 用户导致的被挖矿 启用Kerberos认证功能,禁止匿名访问修改8088端口

    用户为dr.who,问下内部使用人员,都没有任务在跑: 结论: 恭喜你,你中毒了,攻击者利用Hadoop Yarn资源管理系统REST API未授权漏洞对服务器进行攻击,攻击者可以在未授权的情况下远程 ...

  8. kerberos认证原理---讲的非常细致,易懂

    前几天在给人解释Windows是如何通过Kerberos进行Authentication的时候,讲了半天也别把那位老兄讲明白,还差点把自己给绕进去.后来想想原因有以下两点:对于一个没有完全不了解Ker ...

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

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

  10. 使用kafka-python客户端进行kafka kerberos认证

    之前说过python confluent kafka客户端做kerberos认证的过程,如果使用kafka python客户端的话同样也可以进行kerberos的认证,具体的认证机制这里不再描述,主要 ...

随机推荐

  1. vue3自动导入 api ,不需要多次导入 api 了

    安装插件   npm i -D unplugin-auto-import 配置 vite.config.js export default defineConfig({ plugins: [ vue( ...

  2. 6. CSS有哪些方法可以提升层级

    1. 使用 z-index 2. 使用定位,脱离标准流

  3. 旧电脑配置玩魔兽世界带不动?云电脑轻松解决,用ToDesk!

    最近魔兽世界重新回归,不少游戏老玩家都摩拳擦掌准备上线回忆青春,但发现手里的旧电脑早已带不动游戏,硬要打开很容易出现画面卡顿.延迟严重,甚至频繁掉线,这些问题都让游戏乐趣大打折扣.但要为此再重新买一台 ...

  4. nginx关于正向代理与反向代理的概念区分

    正向代理:如果把局域网外的 Internet 想象成一个巨大的资源库,则局域网中的客户端要访问 Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理. 反向代理 反向代理中客户端对 ...

  5. 工作中的技术总结 _Thymeleaf限制字符串的展示长度 _20210910

    工作中的技术总结 _Thymeleaf限制字符串的展示长度 _20210910 比较简单就这一行代码 #strings.abbreviate 应该是调用了 thymeleaf 的内置函数 这一个方法的 ...

  6. WPS Excel中配置下拉多选(VBA)

    网上找到两种方案,一种利用数据选择其他单元格,也就是在其他单元格建数据.需求是模板,不合适 这里我用的VBA,踩了挺多坑,详细说下 首先更新WPS为最新版,确保可用VBA和JSA 确定使用VBA还是J ...

  7. 如何查找Windows 11中的共享文件夹

    windows11的控制面板: Windows工具: 计算机管理: 共享文件夹:

  8. 联邦学习开山之作Communication-Efficient Learning of Deep Networks from Decentralized Data

    1 介绍 1.1 背景 越来越多的手机和平板电脑成为许多人的主要计算设备.这些设备上强大的传感器(包括摄像头.麦克风和GPS),加上它们经常被携带的事实,意味着它们可以访问前所未有的大量数据,其中大部 ...

  9. 【一步步开发AI运动小程序】七、进行运动计时、计数

    随着人工智能技术的不断发展,阿里体育等IT大厂,推出的"乐动力"."天天跳绳"AI运动APP,让云上运动会.线上运动会.健身打卡.AI体育指导等概念空前火热.那 ...

  10. 迁移到 Eclipse: Eclipse 对 IntelliJ IDEAA 评估开发指南

    为何考虑 Eclipse 以及它与 IntelliJ IDEA 有什么不同 Eclipse 是一个免费的.正日益流行起来的 Java 集成开发环境,最新版本的 Eclipse 中提供了很多特性,这些特 ...