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
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就是存放票据的地方,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); } }
不过这种方式有局限性:
1.切换用户,必须进行一次登录操作,频繁登录,给kdc服务器造成压力,kerberos的票据,是有保存期限的,在期限里面,我们不需要登录。
2.这种方式对jdk有依赖,根据实践,IBM的jdk,重复登录,无法切换到新的用户,必须用doAs的方式才正常。