|
@@ -20,6 +20,8 @@ package org.apache.hadoop.security;
|
|
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.IOException;
|
|
import java.io.IOException;
|
|
import java.lang.reflect.UndeclaredThrowableException;
|
|
import java.lang.reflect.UndeclaredThrowableException;
|
|
@@ -40,6 +42,7 @@ import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
+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;
|
|
@@ -71,8 +74,6 @@ 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;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* User and group information for Hadoop.
|
|
* User and group information for Hadoop.
|
|
* This class wraps around a JAAS Subject and provides methods to determine the
|
|
* This class wraps around a JAAS Subject and provides methods to determine the
|
|
@@ -1037,10 +1038,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 " + ticket.getServer() + "), remove" +
|
|
|
|
+ " and destroy it.");
|
|
|
|
+ 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.
|
|
@@ -1048,13 +1079,13 @@ public class UserGroupInformation {
|
|
*/
|
|
*/
|
|
@InterfaceAudience.Public
|
|
@InterfaceAudience.Public
|
|
@InterfaceStability.Evolving
|
|
@InterfaceStability.Evolving
|
|
- public synchronized void reloginFromKeytab()
|
|
|
|
- throws IOException {
|
|
|
|
- if (!isSecurityEnabled() ||
|
|
|
|
- user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
|
|
|
- !isKeytab)
|
|
|
|
|
|
+ public synchronized void reloginFromKeytab() throws IOException {
|
|
|
|
+ if (!isSecurityEnabled()
|
|
|
|
+ || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
|
|
|
|
+ || !isKeytab) {
|
|
return;
|
|
return;
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
long now = Time.now();
|
|
long now = Time.now();
|
|
if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
|
|
if (!shouldRenewImmediatelyForTests && !hasSufficientTimeElapsed(now)) {
|
|
return;
|
|
return;
|
|
@@ -1066,12 +1097,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);
|
|
@@ -1094,6 +1125,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);
|
|
}
|
|
}
|
|
@@ -1115,12 +1147,12 @@ public class UserGroupInformation {
|
|
*/
|
|
*/
|
|
@InterfaceAudience.Public
|
|
@InterfaceAudience.Public
|
|
@InterfaceStability.Evolving
|
|
@InterfaceStability.Evolving
|
|
- public synchronized void reloginFromTicketCache()
|
|
|
|
- throws IOException {
|
|
|
|
- if (!isSecurityEnabled() ||
|
|
|
|
- user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
|
|
|
- !isKrbTkt)
|
|
|
|
|
|
+ public synchronized void reloginFromTicketCache() throws IOException {
|
|
|
|
+ if (!isSecurityEnabled()
|
|
|
|
+ || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
|
|
|
|
+ || !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");
|
|
@@ -1148,13 +1180,13 @@ 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* 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
|