From a65d24488a858ec3a2d4158071426288805d3157 Mon Sep 17 00:00:00 2001 From: Surendra Singh Lilhore <45159337+surendralilhore@users.noreply.github.com> Date: Sun, 8 Jan 2023 23:55:06 +0530 Subject: [PATCH] =?UTF-8?q?HADOOP-18581=20:=20Handle=20Server=20KDC=20re-l?= =?UTF-8?q?ogin=20when=20Server=20and=20Client=20run=20=E2=80=A6=20(#5248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * HADOOP-18581 : Handle Server KDC re-login when Server and Client run in same JVM. --- .../java/org/apache/hadoop/ipc/Server.java | 46 ++++++++++++++++++- .../hadoop/security/UserGroupInformation.java | 41 ++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java index e10e7bfd7c1..a79fc2eeb57 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java @@ -123,6 +123,7 @@ import org.apache.hadoop.util.ProtoUtil; import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.util.Time; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.hadoop.tracing.Span; import org.apache.hadoop.tracing.SpanContext; import org.apache.hadoop.tracing.TraceScope; @@ -153,6 +154,13 @@ public abstract class Server { private ExceptionsHandler exceptionsHandler = new ExceptionsHandler(); private Tracer tracer; private AlignmentContext alignmentContext; + + /** + * Allow server to do force Kerberos re-login once after failure irrespective + * of the last login time. + */ + private final AtomicBoolean canTryForceLogin = new AtomicBoolean(true); + /** * Logical name of the server used in metrics and monitor. */ @@ -2206,7 +2214,23 @@ private void saslProcess(RpcSaslProto saslMessage) AUDITLOG.warn(AUTH_FAILED_FOR + this.toString() + ":" + attemptingUser + " (" + e.getLocalizedMessage() + ") with true cause: (" + tce.getLocalizedMessage() + ")"); - throw tce; + if (!UserGroupInformation.getLoginUser().isLoginSuccess()) { + doKerberosRelogin(); + try { + // try processing message again + LOG.debug("Reprocessing sasl message for {}:{} after re-login", + this.toString(), attemptingUser); + saslResponse = processSaslMessage(saslMessage); + AUDITLOG.info("Retry {}{}:{} after failure", AUTH_SUCCESSFUL_FOR, + this.toString(), attemptingUser); + canTryForceLogin.set(true); + } catch (IOException exp) { + tce = (IOException) getTrueCause(e); + throw tce; + } + } else { + throw tce; + } } if (saslServer != null && saslServer.isComplete()) { @@ -3322,6 +3346,26 @@ protected Server(String bindAddress, int port, metricsUpdaterInterval, metricsUpdaterInterval, TimeUnit.MILLISECONDS); } + private synchronized void doKerberosRelogin() throws IOException { + if(UserGroupInformation.getLoginUser().isLoginSuccess()){ + return; + } + LOG.warn("Initiating re-login from IPC Server"); + if (canTryForceLogin.compareAndSet(true, false)) { + if (UserGroupInformation.isLoginKeytabBased()) { + UserGroupInformation.getLoginUser().forceReloginFromKeytab(); + } else if (UserGroupInformation.isLoginTicketBased()) { + UserGroupInformation.getLoginUser().forceReloginFromTicketCache(); + } + } else { + if (UserGroupInformation.isLoginKeytabBased()) { + UserGroupInformation.getLoginUser().reloginFromKeytab(); + } else if (UserGroupInformation.isLoginTicketBased()) { + UserGroupInformation.getLoginUser().reloginFromTicketCache(); + } + } + } + public synchronized void addAuxiliaryListener(int auxiliaryPort) throws IOException { if (auxiliaryListenerMap == null) { diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java index 9671d8da38f..8a5a0ee234f 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java @@ -529,6 +529,18 @@ private void setLogin(LoginContext login) { user.setLogin(login); } + /** This method checks for a successful Kerberos login + * and returns true by default if it is not using Kerberos. + * + * @return true on successful login + */ + public boolean isLoginSuccess() { + LoginContext login = user.getLogin(); + return (login instanceof HadoopLoginContext) + ? ((HadoopLoginContext) login).isLoginSuccess() + : true; + } + /** * Set the last login time for logged in user * @param loginTime the number of milliseconds since the beginning of time @@ -1276,6 +1288,23 @@ private void reloginFromKeytab(boolean checkTGT, boolean ignoreLastLoginTime) relogin(login, ignoreLastLoginTime); } + /** + * Force re-Login a user in from the ticket cache irrespective of the last + * login time. This method assumes that login had happened already. The + * Subject field of this UserGroupInformation object is updated to have the + * new credentials. + * + * @throws IOException + * raised on errors performing I/O. + * @throws KerberosAuthException + * on a failure + */ + @InterfaceAudience.Public + @InterfaceStability.Evolving + public void forceReloginFromTicketCache() throws IOException { + reloginFromTicketCache(true); + } + /** * Re-Login a user in from the ticket cache. This * method assumes that login had happened already. @@ -1287,6 +1316,11 @@ private void reloginFromKeytab(boolean checkTGT, boolean ignoreLastLoginTime) @InterfaceAudience.Public @InterfaceStability.Evolving public void reloginFromTicketCache() throws IOException { + reloginFromTicketCache(false); + } + + private void reloginFromTicketCache(boolean ignoreLastLoginTime) + throws IOException { if (!shouldRelogin() || !isFromTicket()) { return; } @@ -1294,7 +1328,7 @@ public void reloginFromTicketCache() throws IOException { if (login == null) { throw new KerberosAuthException(MUST_FIRST_LOGIN); } - relogin(login, false); + relogin(login, ignoreLastLoginTime); } private void relogin(HadoopLoginContext login, boolean ignoreLastLoginTime) @@ -2083,6 +2117,11 @@ private static class HadoopLoginContext extends LoginContext { this.conf = conf; } + /** Get the login status. */ + public boolean isLoginSuccess() { + return isLoggedIn.get(); + } + String getAppName() { return appName; }