HADOOP-13433 Race in UGI.reloginFromKeytab. Contributed by Duo Zhang.

This commit is contained in:
Xiao Chen 2017-02-08 23:44:21 -08:00
parent af65f8bb59
commit 04076e783b
1 changed files with 49 additions and 17 deletions

View File

@ -22,6 +22,8 @@ import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_KERBEROS_MIN_S
import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS; import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS;
import static org.apache.hadoop.util.PlatformName.IBM_JAVA; import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
import com.google.common.annotations.VisibleForTesting;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@ -43,6 +45,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.Subject; import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosPrincipal;
@ -74,8 +77,6 @@ import org.apache.hadoop.security.token.TokenIdentifier;
import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.Time; import org.apache.hadoop.util.Time;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -1120,10 +1121,40 @@ public class UserGroupInformation {
reloginFromKeytab(); reloginFromKeytab();
} }
// if the first kerberos ticket is not TGT, then remove and destroy it since
// the kerberos library of jdk always use the first kerberos ticket as TGT.
// See HADOOP-13433 for more details.
private void fixKerberosTicketOrder() {
Set<Object> creds = getSubject().getPrivateCredentials();
synchronized (creds) {
for (Iterator<Object> iter = creds.iterator(); iter.hasNext();) {
Object cred = iter.next();
if (cred instanceof KerberosTicket) {
KerberosTicket ticket = (KerberosTicket) cred;
if (!ticket.getServer().getName().startsWith("krbtgt")) {
LOG.warn("The first kerberos ticket is not TGT"
+ "(the server principal is {}), remove and destroy it.",
ticket.getServer());
iter.remove();
try {
ticket.destroy();
} catch (DestroyFailedException e) {
LOG.warn("destroy ticket failed", e);
}
} else {
return;
}
}
}
}
LOG.warn("Warning, no kerberos ticket found while attempting to renew" +
" ticket");
}
/** /**
* Re-Login a user in from a keytab file. Loads a user identity from a keytab * Re-Login a user in from a keytab file. Loads a user identity from a keytab
* file and logs them in. They become the currently logged-in user. This * file and logs them in. They become the currently logged-in user. This
* method assumes that {@link #loginUserFromKeytab(String, String)} had * method assumes that {@link #loginUserFromKeytab(String, String)} had
* happened already. * happened already.
* The Subject field of this UserGroupInformation object is updated to have * The Subject field of this UserGroupInformation object is updated to have
* the new credentials. * the new credentials.
@ -1131,13 +1162,13 @@ public class UserGroupInformation {
*/ */
@InterfaceAudience.Public @InterfaceAudience.Public
@InterfaceStability.Evolving @InterfaceStability.Evolving
public synchronized void reloginFromKeytab() public synchronized void reloginFromKeytab() throws IOException {
throws IOException { if (!isSecurityEnabled()
if (!isSecurityEnabled() || || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS || || !isKeytab) {
!isKeytab)
return; return;
}
long now = Time.now(); long now = Time.now();
if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) { if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
return; return;
@ -1149,12 +1180,12 @@ public class UserGroupInformation {
now < getRefreshTime(tgt)) { now < getRefreshTime(tgt)) {
return; return;
} }
LoginContext login = getLogin(); LoginContext login = getLogin();
if (login == null || keytabFile == null) { if (login == null || keytabFile == null) {
throw new IOException("loginUserFromKeyTab must be done first"); throw new IOException("loginUserFromKeyTab must be done first");
} }
long start = 0; long start = 0;
// register most recent relogin attempt // register most recent relogin attempt
user.setLastLogin(now); user.setLastLogin(now);
@ -1177,6 +1208,7 @@ public class UserGroupInformation {
} }
start = Time.now(); start = Time.now();
login.login(); login.login();
fixKerberosTicketOrder();
metrics.loginSuccess.add(Time.now() - start); metrics.loginSuccess.add(Time.now() - start);
setLogin(login); setLogin(login);
} }
@ -1198,12 +1230,12 @@ public class UserGroupInformation {
*/ */
@InterfaceAudience.Public @InterfaceAudience.Public
@InterfaceStability.Evolving @InterfaceStability.Evolving
public synchronized void reloginFromTicketCache() public synchronized void reloginFromTicketCache() throws IOException {
throws IOException { if (!isSecurityEnabled()
if (!isSecurityEnabled() || || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS || || !isKrbTkt) {
!isKrbTkt)
return; return;
}
LoginContext login = getLogin(); LoginContext login = getLogin();
if (login == null) { if (login == null) {
throw new IOException("login must be done first"); throw new IOException("login must be done first");
@ -1231,6 +1263,7 @@ public class UserGroupInformation {
LOG.debug("Initiating re-login for " + getUserName()); LOG.debug("Initiating re-login for " + getUserName());
} }
login.login(); login.login();
fixKerberosTicketOrder();
setLogin(login); setLogin(login);
} catch (LoginException le) { } catch (LoginException le) {
throw new IOException("Login failure for " + getUserName() + ": " + le, throw new IOException("Login failure for " + getUserName() + ": " + le,
@ -1238,7 +1271,6 @@ public class UserGroupInformation {
} }
} }
/** /**
* Log a user in from a keytab file. Loads a user identity from a keytab * Log a user in from a keytab file. Loads a user identity from a keytab
* file and login them in. This new user does not affect the currently * file and login them in. This new user does not affect the currently