diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt index d7607260645..56a34ed2025 100644 --- a/hadoop-yarn-project/CHANGES.txt +++ b/hadoop-yarn-project/CHANGES.txt @@ -309,6 +309,9 @@ Release 2.5.0 - UNRELEASED YARN-2216 YARN-2065 AM cannot create new containers after restart (Jian He via stevel) + YARN-2232. Fixed ResourceManager to allow DelegationToken owners to be able + to cancel their own tokens in secure mode. (Varun Vasudev via vinodkv) + Release 2.4.1 - 2014-06-23 INCOMPATIBLE CHANGES diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java index f2e7edba7aa..6ae95dd5d9a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java @@ -918,7 +918,7 @@ public class ClientRMService extends AbstractService implements protoToken.getIdentifier().array(), protoToken.getPassword().array(), new Text(protoToken.getKind()), new Text(protoToken.getService())); - String user = getRenewerForToken(token); + String user = UserGroupInformation.getCurrentUser().getUserName(); rmDTSecretManager.cancelToken(token, user); return Records.newRecord(CancelDelegationTokenResponse.class); } catch (IOException e) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java index 4b1f59c3039..4f4da37da1a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java @@ -44,16 +44,17 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CyclicBarrier; import org.junit.Assert; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.io.Text; +import org.apache.hadoop.security.authentication.util.KerberosName; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.yarn.MockApps; import org.apache.hadoop.yarn.api.ApplicationClientProtocol; import org.apache.hadoop.yarn.api.protocolrecords.ApplicationsRequestScope; +import org.apache.hadoop.yarn.api.protocolrecords.CancelDelegationTokenRequest; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportRequest; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportResponse; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptsRequest; @@ -138,6 +139,10 @@ public class TestClientRMService { private final static String QUEUE_1 = "Q-1"; private final static String QUEUE_2 = "Q-2"; + private final static String kerberosRule = "RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//\nDEFAULT"; + static { + KerberosName.setRules(kerberosRule); + } @BeforeClass public static void setupSecretManager() throws IOException { @@ -479,6 +484,17 @@ public class TestClientRMService { UserGroupInformation.createRemoteUser("owner"); private static final UserGroupInformation other = UserGroupInformation.createRemoteUser("other"); + private static final UserGroupInformation tester = + UserGroupInformation.createRemoteUser("tester"); + private static final String testerPrincipal = "tester@EXAMPLE.COM"; + private static final String ownerPrincipal = "owner@EXAMPLE.COM"; + private static final String otherPrincipal = "other@EXAMPLE.COM"; + private static final UserGroupInformation testerKerb = + UserGroupInformation.createRemoteUser(testerPrincipal); + private static final UserGroupInformation ownerKerb = + UserGroupInformation.createRemoteUser(ownerPrincipal); + private static final UserGroupInformation otherKerb = + UserGroupInformation.createRemoteUser(otherPrincipal); @Test public void testTokenRenewalByOwner() throws Exception { @@ -546,6 +562,147 @@ public class TestClientRMService { rmService.renewDelegationToken(request); } + @Test + public void testTokenCancellationByOwner() throws Exception { + // two tests required - one with a kerberos name + // and with a short name + RMContext rmContext = mock(RMContext.class); + final ClientRMService rmService = + new ClientRMService(rmContext, null, null, null, null, dtsm); + testerKerb.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + checkTokenCancellation(rmService, testerKerb, other); + return null; + } + }); + owner.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + checkTokenCancellation(owner, other); + return null; + } + }); + } + + @Test + public void testTokenCancellationByRenewer() throws Exception { + // two tests required - one with a kerberos name + // and with a short name + RMContext rmContext = mock(RMContext.class); + final ClientRMService rmService = + new ClientRMService(rmContext, null, null, null, null, dtsm); + testerKerb.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + checkTokenCancellation(rmService, owner, testerKerb); + return null; + } + }); + other.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + checkTokenCancellation(owner, other); + return null; + } + }); + } + + @Test + public void testTokenCancellationByWrongUser() { + // two sets to test - + // 1. try to cancel tokens of short and kerberos users as a kerberos UGI + // 2. try to cancel tokens of short and kerberos users as a simple auth UGI + + RMContext rmContext = mock(RMContext.class); + final ClientRMService rmService = + new ClientRMService(rmContext, null, null, null, null, dtsm); + UserGroupInformation[] kerbTestOwners = + { owner, other, tester, ownerKerb, otherKerb }; + UserGroupInformation[] kerbTestRenewers = + { owner, other, ownerKerb, otherKerb }; + for (final UserGroupInformation tokOwner : kerbTestOwners) { + for (final UserGroupInformation tokRenewer : kerbTestRenewers) { + try { + testerKerb.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + try { + checkTokenCancellation(rmService, tokOwner, tokRenewer); + Assert.fail("We should not reach here; token owner = " + + tokOwner.getUserName() + ", renewer = " + + tokRenewer.getUserName()); + return null; + } catch (YarnException e) { + Assert.assertTrue(e.getMessage().contains( + testerKerb.getUserName() + + " is not authorized to cancel the token")); + return null; + } + } + }); + } catch (Exception e) { + Assert.fail("Unexpected exception; " + e.getMessage()); + } + } + } + + UserGroupInformation[] simpleTestOwners = + { owner, other, ownerKerb, otherKerb, testerKerb }; + UserGroupInformation[] simpleTestRenewers = + { owner, other, ownerKerb, otherKerb }; + for (final UserGroupInformation tokOwner : simpleTestOwners) { + for (final UserGroupInformation tokRenewer : simpleTestRenewers) { + try { + tester.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + try { + checkTokenCancellation(tokOwner, tokRenewer); + Assert.fail("We should not reach here; token owner = " + + tokOwner.getUserName() + ", renewer = " + + tokRenewer.getUserName()); + return null; + } catch (YarnException ex) { + Assert.assertTrue(ex.getMessage().contains( + tester.getUserName() + + " is not authorized to cancel the token")); + return null; + } + } + }); + } catch (Exception e) { + Assert.fail("Unexpected exception; " + e.getMessage()); + } + } + } + } + + private void checkTokenCancellation(UserGroupInformation owner, + UserGroupInformation renewer) throws IOException, YarnException { + RMContext rmContext = mock(RMContext.class); + final ClientRMService rmService = + new ClientRMService(rmContext, null, null, null, null, dtsm); + checkTokenCancellation(rmService, owner, renewer); + } + + private void checkTokenCancellation(ClientRMService rmService, + UserGroupInformation owner, UserGroupInformation renewer) + throws IOException, YarnException { + RMDelegationTokenIdentifier tokenIdentifier = + new RMDelegationTokenIdentifier(new Text(owner.getUserName()), + new Text(renewer.getUserName()), null); + Token token = + new Token(tokenIdentifier, dtsm); + org.apache.hadoop.yarn.api.records.Token dToken = + BuilderUtils.newDelegationToken(token.getIdentifier(), token.getKind() + .toString(), token.getPassword(), token.getService().toString()); + CancelDelegationTokenRequest request = + Records.newRecord(CancelDelegationTokenRequest.class); + request.setDelegationToken(dToken); + rmService.cancelDelegationToken(request); + } + @Test (timeout = 30000) @SuppressWarnings ("rawtypes") public void testAppSubmit() throws Exception {