From a932796d0cad3d84df0003782e4247cbc2dcca93 Mon Sep 17 00:00:00 2001 From: sguggilam Date: Mon, 24 Aug 2020 23:39:57 -0700 Subject: [PATCH] HADOOP-17159 Ability for forceful relogin in UserGroupInformation class (#2197) Contributed by Sandeep Guggilam. Signed-off-by: Mingliang Liu Signed-off-by: Steve Loughran --- .../hadoop/security/UserGroupInformation.java | 35 ++++++++++++++---- .../security/TestUGILoginFromKeytab.java | 36 +++++++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) 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 91b64ade59..d1ab4365a0 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 @@ -1232,7 +1232,26 @@ public void reloginFromKeytab() throws IOException { reloginFromKeytab(false); } - private void reloginFromKeytab(boolean checkTGT) throws IOException { + /** + * Force 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 method assumes that {@link #loginUserFromKeytab(String, String)} had + * happened already. The Subject field of this UserGroupInformation object is + * updated to have the new credentials. + * + * @param ignoreTimeElapsed Force re-login irrespective of the time of last + * login + * @throws IOException + * @throws KerberosAuthException on a failure + */ + @InterfaceAudience.Public + @InterfaceStability.Evolving + public void reloginFromKeytab(boolean ignoreTimeElapsed) throws IOException { + reloginFromKeytab(false, ignoreTimeElapsed); + } + + private void reloginFromKeytab(boolean checkTGT, boolean ignoreTimeElapsed) + throws IOException { if (!shouldRelogin() || !isFromKeytab()) { return; } @@ -1247,7 +1266,7 @@ private void reloginFromKeytab(boolean checkTGT) throws IOException { return; } } - relogin(login); + relogin(login, ignoreTimeElapsed); } /** @@ -1268,25 +1287,27 @@ public void reloginFromTicketCache() throws IOException { if (login == null) { throw new KerberosAuthException(MUST_FIRST_LOGIN); } - relogin(login); + relogin(login, false); } - private void relogin(HadoopLoginContext login) throws IOException { + private void relogin(HadoopLoginContext login, boolean ignoreTimeElapsed) + throws IOException { // ensure the relogin is atomic to avoid leaving credentials in an // inconsistent state. prevents other ugi instances, SASL, and SPNEGO // from accessing or altering credentials during the relogin. synchronized(login.getSubjectLock()) { // another racing thread may have beat us to the relogin. if (login == getLogin()) { - unprotectedRelogin(login); + unprotectedRelogin(login, ignoreTimeElapsed); } } } - private void unprotectedRelogin(HadoopLoginContext login) throws IOException { + private void unprotectedRelogin(HadoopLoginContext login, + boolean ignoreTimeElapsed) throws IOException { assert Thread.holdsLock(login.getSubjectLock()); long now = Time.now(); - if (!hasSufficientTimeElapsed(now)) { + if (!hasSufficientTimeElapsed(now) && !ignoreTimeElapsed) { return; } // register most recent relogin attempt diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUGILoginFromKeytab.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUGILoginFromKeytab.java index d233234c26..bf4cf75ba8 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUGILoginFromKeytab.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUGILoginFromKeytab.java @@ -158,6 +158,42 @@ public void testUGIReLoginFromKeytab() throws Exception { Assert.assertNotSame(login1, login2); } + /** + * Force re-login from keytab using the MiniKDC and verify the UGI can + * successfully relogin from keytab as well. + */ + @Test + public void testUGIForceReLoginFromKeytab() throws Exception { + // Set this to false as we are testing force re-login anyways + UserGroupInformation.setShouldRenewImmediatelyForTests(false); + String principal = "foo"; + File keytab = new File(workDir, "foo.keytab"); + kdc.createPrincipal(keytab, principal); + + UserGroupInformation.loginUserFromKeytab(principal, keytab.getPath()); + UserGroupInformation ugi = UserGroupInformation.getLoginUser(); + Assert.assertTrue("UGI should be configured to login from keytab", + ugi.isFromKeytab()); + + // Verify relogin from keytab. + User user = getUser(ugi.getSubject()); + final long firstLogin = user.getLastLogin(); + final LoginContext login1 = user.getLogin(); + Assert.assertNotNull(login1); + + // Sleep for 2 secs to have a difference between first and second login + Thread.sleep(2000); + + // Force relogin from keytab + ugi.reloginFromKeytab(true); + final long secondLogin = user.getLastLogin(); + final LoginContext login2 = user.getLogin(); + Assert.assertTrue("User should have been able to relogin from keytab", + secondLogin > firstLogin); + Assert.assertNotNull(login2); + Assert.assertNotSame(login1, login2); + } + @Test public void testGetUGIFromKnownSubject() throws Exception { KerberosPrincipal principal = new KerberosPrincipal("user");