关于hadoop登陆kerberos时设置环境变量问题的思考

  中心思想,设置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("[email protected]", "/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

原文地址:https://www.cnblogs.com/flowerbirds/p/10124207.html

时间: 2024-11-14 09:39:07

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

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

Ubuntu Linux系统包含两类环境变量:系统环境变量和用户环境变量.系统环境变量对所有系统用户都有效,用户环境变量仅仅对当前的用户有效. 修改用户环境变量 用户环境变量通常被存储在下面的文件中: ~/.profile ~/.bash_profile 或者 ~./bash_login ~/.bashrc 上述文件在Ubuntu 10.0以前版本不推荐使用. 系统环境变量 系统环境变量一般保存在下面的文件中: /etc/environment /etc/profile /etc/bash.ba

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

(1)首先要知道你使用的Mac OS X是什么样的Shell,使用命令 echo $SHELL 如果输出的是:csh或者是tcsh,那么你用的就是C Shell. 如果输出的是:bash,sh,zsh,那么你的用的可能就是Bourne Shell的一个变种. Mac OS X 10.2之前默认的是C Shell. Mac OS X 10.3之后默认的是Bourne Shell. (2)如果是Bourne Shell. 那么你可以把你要添加的环境变量添加到你主目录下面的.profile或者.bas

Linux设置环境变量方法(export PATH)

1.动态库路径的设置 Linux下调用动态库和windows不一样.linux 可执行程序是靠配置文件去读取路径的,因此有些时候需要设置路径 具体操作如下 export LD_LIBRARY_PATH=/home/.....(动态库的目录) 不过这种设置方法只是在当前的session中有效 你可以修改配置文件实现任何session都有效 2.环境变量的设置 一般来说,配置交叉编译工具链的时候需要指定编译工具的路径,此时就需要设置环境变量.例如我的mips-linux-gcc编译器在"/opt/a

Ubuntu中设置环境变量详解

1, 为单一用户:.bashrc: 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取.打开用户主目录下的.bashrc,在这个文件中加入export PATH="$PATH:export PATH="$PATH:/home/***/android-sdk-linux_86 /platform-tools/"使生效 source .bashrc2,全局设置:/etc/profile 是所有用户的环境变量在/etc/profile中增

Linux设置环境变量小结:设置永久变量&amp;临时变量 全局变量&amp;局部变量

1.总结背景 在linux系统下,如果你下载并安装了应用程序,很有可能在键入它的名称时出现“command not found”的提示内容.如果每次都到安装目标文件夹内,找到可执行文件来进行操作就太繁琐了. 这涉及到环境变量PATH的设置问题,而PATH的设置也是在linux下定制环境变量的一个组成部分. 2.变量简介 Linux是一个多用户的操作系统.每个用户登录系统后,都会有一个专用的运行环境.通常每个用户默认的环境都是相同的,这个默认环境实际上就是一组环境变量的定义.用户可以对自己的运行环

CentOS安装JDK1.7设置环境变量及profile和bashrc文件的区别

1.查看当前系统中是否装有JDK(通常CentOS中默认安装OpenJDK) # java –version 如果存在OpenJDK需要先卸载: # rpm -qa|grep jdk 或 rpm -qa|grep java java-1.6.0-openjdk-1.*** java-1.7.0-openjdk-1.*** # rpm -qa|grep gcj java-1.*** libgcj-*** # yum -y remove java java-1.6.0-openjdk-1.*** #

Linux 启动文件、设置环境变量的位置

系统级启动文件  ==================================== 1./etc/rc  主启动文件,不要修改它 2./etc/rc.conf  决定启动哪些系统自带的守护进程,不要修改它 3./etc/rc.conf.local 如果你想干涉系统启动时启动的守护进程,请编辑本文件,本文件的内容会覆盖/etc/rc.conf中的内容. 4./etc/rc.local  重点,你想让Nginx,MySQL,Tomcat自启动,请修改这个文件. 5./etc/rc.shutd

[转] linux 启动文件及设置环境变量

系统级启动文件  ==================================== 1./etc/rc  主启动文件,不要修改它 2./etc/rc.conf  决定启动哪些系统自带的守护进程,不要修改它 3./etc/rc.conf.local 如果你想干涉系统启动时启动的守护进程,请编辑本文件,本文件的内容会覆盖/etc/rc.conf中的内容. 4./etc/rc.local  重点,你想让Nginx,MySQL,Tomcat自启动,请修改这个文件. 5./etc/rc.shutd

C语言的编译过程、安装gcc编译器以及设置环境变量

以我对C语言编译过程的了解,我用了一点时间画了一个图,提供给大家参考一下,希望有些能对您的问题提上帮助. 前几天刚初步学习了C语言的编译过程,感触挺深的.在C语言中头文件其实起了一个很大的作用. 1.头文件可以不需要编译 2.可以查看具体的声明 3.头文件加上实现文件的o文件提交给使用者即可 ,不需要知道源代码 4..o文件预先编译,所以整个项目编译时,会大大提高编译的时间 . 5.当一个文件(A.c文件)依赖于头文件(b.h)时 ,如果b.c编译之后形成的b.o文件重新编译后,a.o的文件不需