中心思想,设置kerberos环境变量时,发现JDK源码当中的一个问题,故描述如下。

  在平时的使用中,如果hadoop集群配置kerberos认证的话,使用java访问hdfs或者hive时,需要先进行认证登陆,之后才可以访问。登陆代码大致如下:

package demo.kerberos;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.UserGroupInformation; public class KerberosLoginTest { public static void main(String[] args) throws IOException {
// 设置kerberos相关登陆信息
// 方法1:设置krb5.conf配置文件
System.setProperty("java.security.krb5.conf", "/home/eabour/hadoop/conf/krb5.conf");
// 方法2:设置kerberos环境变量
// System.setProperty("java.security.krb5.realm", "HADOOP.COM");
// System.setProperty("java.security.krb5.kdc", "kdc.server.com");
// 开启登陆调试日志
System.setProperty("sun.security.krb5.debug", "true");
Configuration conf = new Configuration();
conf.addResource(new Path("/home/eabour/hadoop/conf/core-site.xml"));
conf.addResource(new Path("/home/eabour/hadoop/conf/hdfs-site.xml"));
UserGroupInformation.setConfiguration(conf);
// 登陆kerberos
UserGroupInformation.loginUserFromKeytab("test@HADOOP.COM", "/home/eabour/test.keytab");
// TODO
// 访问HDFS
} }

  设置krb5.conf和core-site.xml、hdfs-site.xml相关大数据平台的配置文件,用来初始化相关配置信息。使用krb5.conf登陆没有问题,不管设置多个kdc server还是单个,jdk中的相关模块都会解析出来,jdk源码位置为openjdk\jdk\src\share\classes\sun\security\krb5,其中openjdk的源码地址为http://hg.openjdk.java.net/jdk7u/jdk7u60/jdk/file/33c1eee28403/src/share/classes,其他版本的类似,解析类为sun.security.krb5.Config,部分代码如下:

    /**
* Private constructor - can not be instantiated externally.
*/
private Config() throws KrbException {
/*
* If either one system property is specified, we throw exception.
*/
String tmp = getProperty("java.security.krb5.kdc");
if (tmp != null) {
// The user can specify a list of kdc hosts separated by ":"
defaultKDC = tmp.replace(':', ' ');
} else {
defaultKDC = null;
}
defaultRealm = getProperty("java.security.krb5.realm");
if ((defaultKDC == null && defaultRealm != null) ||
(defaultRealm == null && defaultKDC != null)) {
throw new KrbException
("System property java.security.krb5.kdc and " +
"java.security.krb5.realm both must be set or " +
"neither must be set.");
} // Always read the Kerberos configuration file
try {
Vector<String> configFile;
String fileName = getJavaFileName();
if (fileName != null) {
configFile = loadConfigFile(fileName);
stanzaTable = parseStanzaTable(configFile);
if (DEBUG) {
System.out.println("Loaded from Java config");
}
} else {
boolean found = false;
if (isMacosLionOrBetter()) {
try {
stanzaTable = SCDynamicStoreConfig.getConfig();
if (DEBUG) {
System.out.println("Loaded from SCDynamicStoreConfig");
}
found = true;
} catch (IOException ioe) {
// OK. Will go on with file
}
}
if (!found) {
fileName = getNativeFileName();
configFile = loadConfigFile(fileName);
stanzaTable = parseStanzaTable(configFile);
if (DEBUG) {
System.out.println("Loaded from native config");
}
}
}
} catch (IOException ioe) {
// No krb5.conf, no problem. We'll use DNS or system property etc.
}
}

  

  从代码可以看出,先从环境变量中获取java.security.krb5.kdc和java.security.krb5.realm,然后在读取配置文件java.security.krb5.conf。如果同时设置java.security.krb5.kdc、java.security.krb5.realm和java.security.krb5.conf,在登陆的时候,会使用java.security.krb5.kdc,除非登陆的realm与defaultRealm不一致,从代码中可以看出来。

  那么,我要说的问题来了,在某些场景下,没有设置java.security.krb5.conf变量,而使用java.security.krb5.kdc、java.security.krb5.realm来登陆。在1个或多个kdc server情况下,这样的设置没有问题。我们知道kdc server的默认端口为88,使用的时UDP协议,当然可以为TCP协议,服务端口也可以修改,这时候大致为:

kdc=kdc.server.com:88

  或者

kdc=kdc.server.com:1088

  krb5.conf文件配置

[realms]
HADOOP.COM = {
kdc = kdc.server.com:1088
}

  此时,设置环境变量:

System.setProperty("java.security.krb5.kdc", "kdc.server.com:1088");

  那么,真正的问题就来了,执行登陆的话,会出现如下报错:

Caused by: GSSException: No valid credentials provided (Mechanism level: ICMP Port Unreachable)
at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:775)
at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:248)
at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179)
at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:192)
… 76 more
Caused by: java.net.PortUnreachableException: ICMP Port Unreachable
at java.net.PlainDatagramSocketImpl.receive0(Native Method)
at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:143)
at java.net.DatagramSocket.receive(DatagramSocket.java:812)
at sun.security.krb5.internal.UDPClient.receive(NetClient.java:206)
at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:411)
at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:364)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.krb5.KdcComm.send(KdcComm.java:348)
at sun.security.krb5.KdcComm.sendIfPossible(KdcComm.java:253)
at sun.security.krb5.KdcComm.send(KdcComm.java:229)
at sun.security.krb5.KdcComm.send(KdcComm.java:200)
at sun.security.krb5.KrbTgsReq.send(KrbTgsReq.java:254)
at sun.security.krb5.KrbTgsReq.sendAndGetCreds(KrbTgsReq.java:269)
at sun.security.krb5.internal.CredentialsUtil.serviceCreds(CredentialsUtil.java:302)
at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(CredentialsUtil.java:120)
at sun.security.krb5.Credentials.acquireServiceCreds(Credentials.java:458)
at sun.security.jgss.krb5.Krb5Context.initSecContext(Krb5Context.java:693)

  为什么会报错呢?因为在解析环境变量时,解析出问题了,现在,再来看看解析代码:

    /**
* Private constructor - can not be instantiated externally.
*/
private Config() throws KrbException {
/*
* If either one system property is specified, we throw exception.
*/
String tmp = getProperty("java.security.krb5.kdc");
if (tmp != null) {
// The user can specify a list of kdc hosts separated by ":"
defaultKDC = tmp.replace(':', ' ');
} else {
defaultKDC = null;
}
defaultRealm = getProperty("java.security.krb5.realm");
if ((defaultKDC == null && defaultRealm != null) ||
(defaultRealm == null && defaultKDC != null)) {
throw new KrbException
("System property java.security.krb5.kdc and " +
"java.security.krb5.realm both must be set or " +
"neither must be set.");
}

  请看红色黄底的代码 defaultKDC = tmp.replace(':', ' ') ,对,就是这句代码的问题,他将kdc.server.com:1088分割为kdc.server.com和1088了,认为时两个kdc server。我的心崩溃呀,为什么要用冒号来分割多个server的配置,如果在使用默认端口的话,这样也没问题,但是,如果kdc修改了端口,这种通过环境变量设置kdc server的方式就没法用了。

  到最后,原来时jdk源码的问题,正常的途径怕是设置不了了。但是,不是不能修改了,我们可以用反射来修改ConfigdefaultKDC的值,虽然说defaultKDC为final String ,到那时它是private final String defaultKDC;,所以还是可以修改的。

  以上就是我分析的问题,在实际项目中是真实遇到过,因为涉及JDK底层代码,所以请大家来参详一下。

附:

1.kerberos配置文件设置:https://www.ibm.com/support/knowledgecenter/zh/SSAW57_9.0.0/com.ibm.websphere.nd.multiplatform.doc/ae/tsec_kerb_create_conf.html

关于hadoop登陆kerberos时设置环境变量问题的思考的更多相关文章

  1. docker 容器启动时设置环境变量source

    镜像启动时,自动执行的是~/.bashrc文件,所以,环境变量需要配置在该文件内,这样镜像启动时,可自动执行该文件,使环境变量生效. vi  ~/.bashrc ------------------- ...

  2. Mac 可设置环境变量的位置、查看和添加PATH环境变量

    Mac 启动加载文件位置(可设置环境变量) ------------------------------------------------------- (1)首先要知道你使用的Mac OS X是什 ...

  3. 转载--Ubuntu设置环境变量

    Ubuntu设置环境变量并立即生效(以Ubuntu12.04为例) 标签: UbuntuLinux环境变量 2013-09-12 19:04 9961人阅读 评论(1) 收藏 举报  分类: Ubun ...

  4. Mac可设置环境变量的位置、查看和添加PATH环境变量

    Mac 启动加载文件位置(可设置环境变量) 首先要知道你使用的 Mac OS X 是什么样的 Shell,使用命令 echo $SHELL 如果输出的是:csh 或者是 tcsh,那么你用的就是 C ...

  5. Ubuntu设置环境变量并立即生效

    Ubuntu Linux系统包含两类环境变量:系统环境变量和用户环境变量.系统环境变量对所有系统用户都有效,用户环境变量仅仅对当前的用户有效. 修改用户环境变量 用户环境变量通常被存储在下面的文件中: ...

  6. crontab演出newLISP脚本设置环境变量

    今天遇到一个问题.执行在终端newLISP文字,一切正常,搬去crontab在将无法正常工作.即使crontab -e命令是在同一个用户执行.还是有问题. 因为newLISP脚本使用hive和hado ...

  7. Linux中使用export命令设置环境变量

    Linux export 命令 2011-08-31 22:36:39|  分类: 命令总结|举报|字号 订阅     功能说明:设置或显示环境变量. ######################## ...

  8. 00006 - Linux中使用export命令设置环境变量

    功能说明:设置或显示环境变量. #################################################################################### ...

  9. CentOS 设置环境变量

    1. 查看环境变量,echo 命令用于在终端输出字符串或变量提取后的值,格式为“echo [字符串 | $变量]” echo $PATH /usr/local/bin:/usr/bin:/usr/lo ...

随机推荐

  1. 2017第二届广东省强网杯线上赛--Nonstandard

    测试文件:http://static2.ichunqiu.com/icq/resources/fileupload/CTF/echunqiu/qwb/Nonstandard_26195e1832795 ...

  2. luogu P5329 [SNOI2019]字符串

    传送门 显然要写一个排序,那只要考虑cmp函数怎么写就行了.第\(i\)个字符串和第 \(j\)个,首先前\(min(i,j)-1\)个字符是相同的,然后就是要比较后缀\(min(i,j)\)和\(m ...

  3. Optional接口简记

    @Data public class Employee { private String name; } @Data public class Company { private String nam ...

  4. Cross-Origin-Resource-Sharing-Solutions

    from:https://github.com/hijiangtao/hijiangtao.github.io/blob/master/_posts/2017-06-13-Cross-Origin-R ...

  5. String.IsNullOrEmpty官方示例

    // This example demonstrates the String.IsNullOrEmpty() method using System; class Sample { public s ...

  6. 对 django rest framework框架的认识

    - 路由 - 可以通过as_view传参数,根据请求方式不同执行相应的方法 - 可以在url中设置一个结尾,类似于: .json - 视图 - 帮助开发者提供了一些类,并在类中提供了多个方法以供我们使 ...

  7. 如何编写testbench的总结(非常实用的总结)

    1.激励的设置 相应于被测试模块的输入激励设置为reg型,输出相应设置为wire类型,双向端口inout在测试中需要进行处理. 方法1:为双向端口设置中间变量inout_reg作为该inout的输出寄 ...

  8. Ubuntu配置python操作

    Ubuntu16.04 安装python 查看当前python情况root@localhost:/# cd /root@localhost:/usr/bin# cd /usr/binroot@loca ...

  9. redux中间件来执行异步操作

    在redux中我们都是执行同步操作,如果我们想要执行异步操作,那么我们就需要依赖到中间件,具体的中间件的概念我就不描述了相信官方文档更详尽.现在就描述下具体的用法,就已我们项目中用到的最多的数据请求为 ...

  10. diff 比较两个文件的不同

    1.命令功能 diff 逐行比较文件内容,并输出文件差异. 2.语法格式 diff  option   file1    file2 diff  选项   文件1   文件2 参数说明 参数 参数说明 ...