hadoop在Kerberos认证模式下的用户切换问题

hadoop使用到kerberos作为用户身份的一种认证方式。关于Kerberos的定义和用法,可以参考很多资料,这里就略过。

hadoop体系,hdfs,hbase,hive,spark等系统,事实上,都有权限控制体系,比如HDFS的读写权限控制,文件的所有者所属组等。kerberos在这里扮演的角色,事实上是一个验证的行为,告诉系统当前用户的身份是什么,方便权限控制系统进行判定。

  对于已经启用了kerberos认证方式的hadoop集群,这里进行JAVA作为客户端的连接说明:


		String userCode="user1";
		String keytabPath = "./user1.keytab";
		System.setProperty("java.security.krb5.kdc", kdc); 
		System.setProperty("java.security.krb5.realm", realm);
		final Configuration conf = new Configuration();
		UserGroupInformation ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(userCode, keytabPath);
		final HConnection connection;
		ugi.doAs(new PrivilegedAction() {

			@Override
			public Object run() {
				try {
					connection = HConnectionManager.createConnection(conf);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return null;
			}
		}); 
  

     userCode是用户的名称,keytabPath是keytab文件的路径,一般系统是采用kdc生成的验证文件,来进行系统登录。系统不需要知道密码。   

    这里的ugi,就是kerberos的ticket, 是通过  UserGroupInformation.loginUserFromKeytabAndReturnUGI( userCode , keytabPath ); 这个方法

成功后,返回的票据信息,通过这个票据,可以在没过期的情况下,用来访问hadoop系统。

  重点是要说明,为什么要用这个票据的doAs方法来进行访问。首先我们看下UserGroupInformation的代码:

  @InterfaceAudience.Public
  @InterfaceStability.Evolving
  public  T doAs(PrivilegedAction action) {
    logPrivilegedAction(subject, action);
    return Subject.doAs(subject, action);
  }


logPrivilegedAction是记录日志信息,先不用管,关键在Subject.doAs,事实上,这个是jdk的代码

    public static  T doAs(final Subject subject,
                        final java.security.PrivilegedAction action) {

        java.lang.SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(AuthPermissionHolder.DO_AS_PERMISSION);
        }
        if (action == null)
            throw new NullPointerException
                (ResourcesMgr.getString("invalid.null.action.provided"));

        // set up the new Subject-based AccessControlContext
        // for doPrivileged
        final AccessControlContext currentAcc = AccessController.getContext();

        // call doPrivileged and push this new context on the stack
        return java.security.AccessController.doPrivileged
                                        (action,
                                        createContext(subject, currentAcc));
    }

最后的代码是原生的看不到了,根据注释,可以看出,这里是新开了一个上下文,这个上下文是subject来初始化的,然后调用我们的连接方法。

subject就是存放票据的地方,Userinformation初始化后,就不可变了 

定义是 privatefinal Subjectsubject;


为什么有的时候,不用doAs方法,调用认证的方法后,也可以正常访问kerberos的hadoop集群呢?  这里看下 loginUserFromKeytabAndReturnUGI的方法

  static UserGroupInformation loginUserFromKeytabAndReturnUGI(String user,
                                  String path
                                  ) throws IOException {
    if (!isSecurityEnabled())
      return UserGroupInformation.getCurrentUser();
    String oldKeytabFile = null;
    String oldKeytabPrincipal = null;

    long start = 0;
    try {
      oldKeytabFile = keytabFile;
      oldKeytabPrincipal = keytabPrincipal;
      keytabFile = path;
      keytabPrincipal = user;
      Subject subject = new Subject();
      
      LoginContext login = newLoginContext(
          HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject,
          new HadoopConfiguration());
       
      start = Time.now();
      login.login();
      metrics.loginSuccess.add(Time.now() - start);
      UserGroupInformation newLoginUser = new UserGroupInformation(subject);
      newLoginUser.setLogin(login);
      newLoginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
      
      return newLoginUser;
    }
 
  

private static LoginContext   newLoginContext(String appName, Subject subject,     javax.security.auth.login.Configuration loginConf)       throws LoginException {     // Temporarily switch the thread's ContextClassLoader to match this     // class's classloader, so that we can properly load HadoopLoginModule     // from the JAAS libraries.     Thread t = Thread.currentThread();     ClassLoader oldCCL = t.getContextClassLoader();     t.setContextClassLoader(HadoopLoginModule.class.getClassLoader());     try {       return new LoginContext(appName, subject, null, loginConf);     } finally {       t.setContextClassLoader(oldCCL);     }   }

 
  
 LoginContext login 这个方法,是把当前线程的,切换到登录后subject上面去了,也就是当前线程的上下文,具备了登录信息,也就不需要用doAs的方法,也可以正常访问kerberos.

  不过这种方式有局限性:

   1.切换用户,必须进行一次登录操作,频繁登录,给kdc服务器造成压力,kerberos的票据,是有保存期限的,在期限里面,我们不需要登录。

   2.这种方式对jdk有依赖,根据实践,IBM的jdk,重复登录,无法切换到新的用户,必须用doAs的方式才正常。







你可能感兴趣的:(hadoop在Kerberos认证模式下的用户切换问题)