diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java index 4b32c04..a2023ea 100644 --- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java +++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java @@ -160,7 +160,8 @@ protected void register() { this.context.getClusterInfo().setMaxContainerCapability( maxContainerCapability); if (UserGroupInformation.isSecurityEnabled()) { - setClientToAMToken(response.getClientToAMTokenMasterKey()); + setClientToAMToken(response.getClientToAMTokenMasterKey(), + response.getClientToAMTokenRenewDate()); } this.applicationACLs = response.getApplicationACLs(); LOG.info("maxContainerCapability: " + maxContainerCapability.getMemory()); @@ -173,9 +174,13 @@ protected void register() { } } - private void setClientToAMToken(ByteBuffer clientToAMTokenMasterKey) { + private void setClientToAMToken(ByteBuffer clientToAMTokenMasterKey, + long clientToAMTokenMasterRenewDate) { byte[] key = clientToAMTokenMasterKey.array(); - context.getClientToAMTokenSecretManager().setMasterKey(key); + context.getClientToAMTokenSecretManager().setMasterKey( + this.context.getApplicationAttemptId(), key); + context.getClientToAMTokenSecretManager().setRenewDate( + this.context.getApplicationAttemptId(), clientToAMTokenMasterRenewDate); } protected void unregister() { diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java index 6cb0191..c2b628b 100644 --- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java +++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java @@ -685,6 +685,14 @@ public void rampDownReduces(int rampDown) { updateAMRMToken(response.getAMRMToken()); } + // Update ClientToAMToken renewDate + if (UserGroupInformation.isSecurityEnabled() + && response.getClientToAMTokenRenewDate() != 0) { + getContext().getClientToAMTokenSecretManager().setRenewDate( + getContext().getApplicationAttemptId(), + response.getClientToAMTokenRenewDate()); + } + List finishedContainers = response.getCompletedContainersStatuses(); // propagate preemption requests diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java index 943c0af..9d40fd0 100644 --- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java +++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerRequestor.java @@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import com.google.common.annotations.VisibleForTesting; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience.Private; @@ -38,6 +39,7 @@ import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId; import org.apache.hadoop.mapreduce.v2.app.AppContext; import org.apache.hadoop.mapreduce.v2.app.client.ClientService; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.records.AMCommand; @@ -184,6 +186,11 @@ protected AllocateResponse makeRemoteRequest() throws IOException { AllocateRequest.newInstance(lastResponseID, super.getApplicationProgress(), new ArrayList(ask), new ArrayList(release), blacklistRequest); + if (UserGroupInformation.isSecurityEnabled()) { + allocateRequest.setClientToAMTokenRenewDate(getContext() + .getClientToAMTokenSecretManager().getRenewDate( + getContext().getApplicationAttemptId())); + } AllocateResponse allocateResponse; try { allocateResponse = scheduler.allocate(allocateRequest); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateRequest.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateRequest.java index 62316a6..8acd797 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateRequest.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateRequest.java @@ -22,6 +22,7 @@ import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Stable; +import org.apache.hadoop.classification.InterfaceStability.Unstable; import org.apache.hadoop.yarn.api.ApplicationMasterProtocol; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; @@ -81,7 +82,22 @@ public static AllocateRequest newInstance(int responseID, float appProgress, allocateRequest.setIncreaseRequests(increaseRequests); return allocateRequest; } - + + @Public + @Unstable + public static AllocateRequest newInstance(int responseID, float appProgress, + List resourceAsk, + List containersToBeReleased, + ResourceBlacklistRequest resourceBlacklistRequest, + List increaseRequests, + long clientToAMTokenRenewDate) { + AllocateRequest allocateRequest = + newInstance(responseID, appProgress, resourceAsk, + containersToBeReleased, resourceBlacklistRequest, increaseRequests); + allocateRequest.setIncreaseRequests(increaseRequests); + allocateRequest.setClientToAMTokenRenewDate(clientToAMTokenRenewDate); + return allocateRequest; + } /** * Get the response id used to track duplicate responses. * @return response id @@ -201,4 +217,13 @@ public abstract void setResourceBlacklistRequest( @Stable public abstract void setIncreaseRequests( List increaseRequests); + + @Public + @Unstable + public abstract long getClientToAMTokenRenewDate(); + + @Public + @Unstable + public abstract void + setClientToAMTokenRenewDate(long clientToAMTokenRenewDate); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java index e56ba61..25302c9 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/AllocateResponse.java @@ -111,6 +111,7 @@ public static AllocateResponse newInstance(int responseId, List allocatedContainers, List updatedNodes, Resource availResources, AMCommand command, int numClusterNodes, PreemptionMessage preempt, List nmTokens, Token amRMToken, + long clientToAMTokenRenewDate, List increasedContainers, List decreasedContainers) { AllocateResponse response = @@ -118,6 +119,7 @@ public static AllocateResponse newInstance(int responseId, updatedNodes, availResources, command, numClusterNodes, preempt, nmTokens, increasedContainers, decreasedContainers); response.setAMRMToken(amRMToken); + response.setClientToAMTokenRenewDate(clientToAMTokenRenewDate); return response; } @@ -302,4 +304,15 @@ public abstract void setDecreasedContainers( @Private @Unstable public abstract void setAMRMToken(Token amRMToken); + + /** + * clientToAMToken renew Date + */ + @Public + @Unstable + public abstract long getClientToAMTokenRenewDate(); + + @Private + @Unstable + public abstract void setClientToAMTokenRenewDate(long renewDate); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/RegisterApplicationMasterResponse.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/RegisterApplicationMasterResponse.java index 79f9f3a..1213e32 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/RegisterApplicationMasterResponse.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/RegisterApplicationMasterResponse.java @@ -53,11 +53,12 @@ @Private @Unstable - public static RegisterApplicationMasterResponse newInstance( - Resource minCapability, Resource maxCapability, - Map acls, ByteBuffer key, - List containersFromPreviousAttempt, String queue, - List nmTokensFromPreviousAttempts) { + public static RegisterApplicationMasterResponse + newInstance(Resource minCapability, Resource maxCapability, + Map acls, ByteBuffer key, + List containersFromPreviousAttempt, String queue, + List nmTokensFromPreviousAttempts, + long clientToAMTokenRenewDate) { RegisterApplicationMasterResponse response = Records.newRecord(RegisterApplicationMasterResponse.class); response.setMaximumResourceCapability(maxCapability); @@ -66,6 +67,7 @@ public static RegisterApplicationMasterResponse newInstance( response.setContainersFromPreviousAttempts(containersFromPreviousAttempt); response.setNMTokensFromPreviousAttempts(nmTokensFromPreviousAttempts); response.setQueue(queue); + response.setClientToAMTokenRenewDate(clientToAMTokenRenewDate); return response; } @@ -180,4 +182,13 @@ public abstract void setContainersFromPreviousAttempts( @Private @Unstable public abstract void setNMTokensFromPreviousAttempts(List nmTokens); + + @Public + @Unstable + public abstract long getClientToAMTokenRenewDate(); + + @Private + @Unstable + public abstract void + setClientToAMTokenRenewDate(long clientToAMTokenRenewDate); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index 9e08ef5..4c15284 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -523,6 +523,11 @@ public static final String DEFAULT_RM_NODEMANAGER_MINIMUM_VERSION = "NONE"; + public static final String CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS = RM_PREFIX + + "client-to-am.token.renew-interval-secs"; + + public static final long DEFAULT_CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS = + 24 * 60 * 60; //////////////////////////////// // Node Manager Configs //////////////////////////////// diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto index df8784b..ff39049 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto @@ -47,6 +47,7 @@ message RegisterApplicationMasterResponseProto { repeated ContainerProto containers_from_previous_attempts = 4; optional string queue = 5; repeated NMTokenProto nm_tokens_from_previous_attempts = 6; + optional int64 client_to_am_token_renew_date = 7 [default = 0]; } message FinishApplicationMasterRequestProto { @@ -66,6 +67,7 @@ message AllocateRequestProto { optional int32 response_id = 4; optional float progress = 5; repeated ContainerResourceIncreaseRequestProto increase_request = 6; + optional int64 client_to_am_token_renew_date = 7 [default = 0]; } message NMTokenProto { @@ -86,6 +88,7 @@ message AllocateResponseProto { repeated ContainerResourceIncreaseProto increased_containers = 10; repeated ContainerResourceDecreaseProto decreased_containers = 11; optional hadoop.common.TokenProto am_rm_token = 12; + optional int64 client_to_am_token_renew_date = 13 [default = 0]; } ////////////////////////////////////////////////////// diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/AMRMClientImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/AMRMClientImpl.java index e36d7ad..f8c80f4 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/AMRMClientImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/AMRMClientImpl.java @@ -43,6 +43,7 @@ import org.apache.hadoop.ipc.RPC; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.api.ApplicationMasterProtocol; +import org.apache.hadoop.yarn.api.ApplicationConstants.Environment; import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterRequest; @@ -50,6 +51,7 @@ import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterRequest; import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse; import org.apache.hadoop.yarn.api.records.AMCommand; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerStatus; import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; @@ -68,6 +70,8 @@ import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.security.AMRMTokenIdentifier; +import org.apache.hadoop.yarn.security.ClientToAMTokenCache; +import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.hadoop.yarn.util.RackResolver; import com.google.common.annotations.VisibleForTesting; @@ -83,6 +87,7 @@ Collections.singletonList(ResourceRequest.ANY); private int lastResponseId = 0; + private ApplicationAttemptId appAttemptId; protected String appHostName; protected int appHostPort; @@ -176,6 +181,9 @@ public AMRMClientImpl() { @Override protected void serviceInit(Configuration conf) throws Exception { + synchronized (this) { + this.appAttemptId = getApplicationAttemptId(); + } RackResolver.init(conf); super.serviceInit(conf); } @@ -227,6 +235,13 @@ private RegisterApplicationMasterResponse registerApplicationMaster() if (!response.getNMTokensFromPreviousAttempts().isEmpty()) { populateNMTokens(response.getNMTokensFromPreviousAttempts()); } + if (UserGroupInformation.isSecurityEnabled() + && response.getClientToAMTokenMasterKey() != null) { + ClientToAMTokenCache.setSecretKey(this.appAttemptId, response + .getClientToAMTokenMasterKey()); + ClientToAMTokenCache.setRenewDate(this.appAttemptId, response + .getClientToAMTokenRenewDate()); + } } return response; } @@ -269,6 +284,8 @@ public AllocateResponse allocate(float progressIndicator) allocateRequest = AllocateRequest.newInstance(lastResponseId, progressIndicator, askList, releaseList, blacklistRequest); + allocateRequest.setClientToAMTokenRenewDate(ClientToAMTokenCache + .getRenewData(this.appAttemptId)); // clear blacklistAdditions and blacklistRemovals before // unsynchronized part blacklistAdditions.clear(); @@ -307,6 +324,11 @@ public AllocateResponse allocate(float progressIndicator) if (allocateResponse.getAMRMToken() != null) { updateAMRMToken(allocateResponse.getAMRMToken()); } + if (UserGroupInformation.isSecurityEnabled() + && allocateResponse.getClientToAMTokenRenewDate() != 0) { + ClientToAMTokenCache.setRenewDate(this.appAttemptId, + allocateResponse.getClientToAMTokenRenewDate()); + } if (!pendingRelease.isEmpty() && !allocateResponse.getCompletedContainersStatuses().isEmpty()) { removePendingReleaseRequests(allocateResponse @@ -762,4 +784,16 @@ private void updateAMRMToken(Token token) throws IOException { } currentUGI.addToken(amrmToken); } + + + @Private + protected ApplicationAttemptId getApplicationAttemptId() + throws IOException { + String containerIdStr = System.getenv(Environment.CONTAINER_ID.name()); + if (containerIdStr == null) { + throw new IOException(Environment.CONTAINER_ID.name() + "is null!"); + } + return ConverterUtils.toContainerId(containerIdStr) + .getApplicationAttemptId(); + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClient.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClient.java index 38dbf79..ff62dcc 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClient.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClient.java @@ -19,13 +19,13 @@ package org.apache.hadoop.yarn.client.api.impl; import com.google.common.base.Supplier; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - import java.io.IOException; import java.nio.ByteBuffer; import java.security.PrivilegedAction; @@ -46,6 +46,7 @@ import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.SecretManager.InvalidToken; import org.apache.hadoop.service.Service.STATE; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.ApplicationMasterProtocol; import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; @@ -220,7 +221,7 @@ public void testAMRMClientMatchingFit() throws YarnException, IOException { AMRMClient amClient = null; try { // start am rm client - amClient = AMRMClient.createAMRMClient(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); amClient.registerApplicationMaster("Host", 10000, ""); @@ -334,7 +335,7 @@ public void testAMRMClientMatchingFitInferredRack() throws YarnException, IOExce AMRMClientImpl amClient = null; try { // start am rm client - amClient = new AMRMClientImpl(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); amClient.registerApplicationMaster("Host", 10000, ""); @@ -379,9 +380,7 @@ public void testAMRMClientMatchStorage() throws YarnException, IOException { AMRMClientImpl amClient = null; try { // start am rm client - amClient = - (AMRMClientImpl) AMRMClient - . createAMRMClient(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); amClient.registerApplicationMaster("Host", 10000, ""); @@ -502,9 +501,7 @@ public void testAllocationWithBlacklist() throws YarnException, IOException { AMRMClientImpl amClient = null; try { // start am rm client - amClient = - (AMRMClientImpl) AMRMClient - . createAMRMClient(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); amClient.registerApplicationMaster("Host", 10000, ""); @@ -567,9 +564,7 @@ public void testAMRMClientWithBlacklist() throws YarnException, IOException { AMRMClientImpl amClient = null; try { // start am rm client - amClient = - (AMRMClientImpl) AMRMClient - . createAMRMClient(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); amClient.registerApplicationMaster("Host", 10000, ""); @@ -640,7 +635,7 @@ public void testAMRMClient() throws YarnException, IOException { AMRMClient amClient = null; try { // start am rm client - amClient = AMRMClient.createAMRMClient(); + amClient = createAMRMClient() ; //setting an instance NMTokenCache amClient.setNMTokenCache(new NMTokenCache()); @@ -836,9 +831,7 @@ public void testWaitFor() throws InterruptedException { try { // start am rm client - amClient = - (AMRMClientImpl) AMRMClient - . createAMRMClient(); + amClient = createAMRMClient(); amClient.init(new YarnConfiguration()); amClient.start(); amClient.waitFor(countDownChecker, 1000); @@ -868,7 +861,7 @@ public void testAMRMClientOnAMRMTokenRollOver() throws YarnException, .getAMRMTokenSecretManager(); // start am rm client - amClient = AMRMClient. createAMRMClient(); + amClient = createAMRMClient(); amClient.init(conf); amClient.start(); @@ -977,4 +970,14 @@ public ApplicationMasterProtocol run() { } return null; } + + private AMRMClientImpl createAMRMClient() { + return new AMRMClientImpl() { + @Override + public ApplicationAttemptId getApplicationAttemptId() throws IOException { + return ApplicationAttemptId.newInstance( + ApplicationId.newInstance(Time.now(), 1), 1); + } + }; + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientContainerRequest.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientContainerRequest.java index cb8c86a..e39ab81 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientContainerRequest.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientContainerRequest.java @@ -20,12 +20,16 @@ import static org.junit.Assert.assertEquals; +import java.io.IOException; import java.util.Arrays; import java.util.List; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.net.DNSToSwitchMapping; +import org.apache.hadoop.util.Time; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.api.records.ResourceRequest; @@ -37,8 +41,7 @@ public class TestAMRMClientContainerRequest { @Test public void testFillInRacks() { - AMRMClientImpl client = - new AMRMClientImpl(); + AMRMClientImpl client = createAMRMClient(); Configuration conf = new Configuration(); conf.setClass( @@ -60,8 +63,7 @@ public void testFillInRacks() { @Test public void testDisableLocalityRelaxation() { - AMRMClientImpl client = - new AMRMClientImpl(); + AMRMClientImpl client = createAMRMClient(); Configuration conf = new Configuration(); conf.setClass( CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, @@ -127,8 +129,7 @@ public void testDisableLocalityRelaxation() { @Test (expected = InvalidContainerRequestException.class) public void testDifferentLocalityRelaxationSamePriority() { - AMRMClientImpl client = - new AMRMClientImpl(); + AMRMClientImpl client = createAMRMClient(); Configuration conf = new Configuration(); conf.setClass( CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, @@ -148,8 +149,7 @@ public void testDifferentLocalityRelaxationSamePriority() { @Test public void testInvalidValidWhenOldRemoved() { - AMRMClientImpl client = - new AMRMClientImpl(); + AMRMClientImpl client = createAMRMClient(); Configuration conf = new Configuration(); conf.setClass( CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, @@ -187,8 +187,7 @@ public void testInvalidValidWhenOldRemoved() { @Test (expected = InvalidContainerRequestException.class) public void testLocalityRelaxationDifferentLevels() { - AMRMClientImpl client = - new AMRMClientImpl(); + AMRMClientImpl client = createAMRMClient(); Configuration conf = new Configuration(); conf.setClass( CommonConfigurationKeysPublic.NET_TOPOLOGY_NODE_SWITCH_MAPPING_IMPL_KEY, @@ -230,4 +229,14 @@ private void verifyResourceRequest( assertEquals(1, ask.getNumContainers()); assertEquals(expectedRelaxLocality, ask.getRelaxLocality()); } + + private AMRMClientImpl createAMRMClient() { + return new AMRMClientImpl() { + @Override + public ApplicationAttemptId getApplicationAttemptId() throws IOException { + return ApplicationAttemptId.newInstance( + ApplicationId.newInstance(Time.now(), 1), 1); + } + }; + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientOnRMRestart.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientOnRMRestart.java index dfad6d6..2e69034 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientOnRMRestart.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestAMRMClientOnRMRestart.java @@ -30,10 +30,12 @@ import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.SecretManager.InvalidToken; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.ApplicationMasterProtocol; import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerState; @@ -623,6 +625,12 @@ protected void serviceStop() throws Exception { public void updateRMProxy(MyResourceManager rm) { rmClient = rm.getApplicationMasterService(); } + + @Override + public ApplicationAttemptId getApplicationAttemptId() throws IOException { + return ApplicationAttemptId.newInstance( + ApplicationId.newInstance(Time.now(), 1), 1); + } } private static void assertBlacklistAdditionsAndRemovals( diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestNMClient.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestNMClient.java index 88dbf81..1ec632a 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestNMClient.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestNMClient.java @@ -36,6 +36,7 @@ import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.service.Service.STATE; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse; import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; @@ -55,7 +56,6 @@ import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.api.records.ResourceRequest; import org.apache.hadoop.yarn.api.records.YarnApplicationState; -import org.apache.hadoop.yarn.client.api.AMRMClient; import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest; import org.apache.hadoop.yarn.client.api.NMClient; import org.apache.hadoop.yarn.client.api.NMTokenCache; @@ -161,9 +161,13 @@ public void setup() throws YarnException, IOException { nmTokenCache = new NMTokenCache(); // start am rm client - rmClient = - (AMRMClientImpl) AMRMClient - . createAMRMClient(); + rmClient = new AMRMClientImpl() { + @Override + public ApplicationAttemptId getApplicationAttemptId() throws IOException { + return ApplicationAttemptId.newInstance( + ApplicationId.newInstance(Time.now(), 1), 1); + } + }; //setting an instance NMTokenCase rmClient.setNMTokenCache(nmTokenCache); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateRequestPBImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateRequestPBImpl.java index dc11165..fa4cec2 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateRequestPBImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateRequestPBImpl.java @@ -392,4 +392,16 @@ private ResourceBlacklistRequestPBImpl convertFromProtoFormat(ResourceBlacklistR private ResourceBlacklistRequestProto convertToProtoFormat(ResourceBlacklistRequest t) { return ((ResourceBlacklistRequestPBImpl)t).getProto(); } + + @Override + public long getClientToAMTokenRenewDate() { + AllocateRequestProtoOrBuilder p = viaProto ? proto : builder; + return p.getClientToAmTokenRenewDate(); + } + + @Override + public void setClientToAMTokenRenewDate(long clientToAMTokenRenewDate) { + maybeInitBuilder(); + builder.setClientToAmTokenRenewDate(clientToAMTokenRenewDate); + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java index f2796fd..790a820 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/AllocateResponsePBImpl.java @@ -735,4 +735,16 @@ private TokenPBImpl convertFromProtoFormat(TokenProto p) { private TokenProto convertToProtoFormat(Token t) { return ((TokenPBImpl)t).getProto(); } + + @Override + public synchronized long getClientToAMTokenRenewDate() { + AllocateResponseProtoOrBuilder p = viaProto ? proto : builder; + return p.getClientToAmTokenRenewDate(); + } + + @Override + public synchronized void setClientToAMTokenRenewDate(long renewDate) { + maybeInitBuilder(); + builder.setClientToAmTokenRenewDate(renewDate); + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/RegisterApplicationMasterResponsePBImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/RegisterApplicationMasterResponsePBImpl.java index 06a637a..c5caa2c 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/RegisterApplicationMasterResponsePBImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/RegisterApplicationMasterResponsePBImpl.java @@ -387,4 +387,17 @@ private NMTokenProto convertToProtoFormat(NMToken token) { private NMToken convertFromProtoFormat(NMTokenProto proto) { return new NMTokenPBImpl(proto); } + + @Override + public long getClientToAMTokenRenewDate() { + RegisterApplicationMasterResponseProtoOrBuilder p = + viaProto ? proto : builder; + return (p.getClientToAmTokenRenewDate()); + } + + @Override + public void setClientToAMTokenRenewDate(long clientToAMTokenRenewDate) { + maybeInitBuilder(); + builder.setClientToAmTokenRenewDate(clientToAMTokenRenewDate); + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ClientToAMTokenCache.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ClientToAMTokenCache.java new file mode 100644 index 0000000..58cc66c --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/ClientToAMTokenCache.java @@ -0,0 +1,210 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package org.apache.hadoop.yarn.security; + +import java.nio.ByteBuffer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import javax.crypto.SecretKey; + +import org.apache.hadoop.classification.InterfaceAudience.Public; +import org.apache.hadoop.classification.InterfaceStability.Evolving; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.security.client.ClientToAMTokenSecretManager; + +/** + * ClientToAMTokenCache manages ClientToAMToken required for the client + * communicating with this Application Master. + *
    + *
  • Using the singleton instance of the cache is appropriate when running a + * single/multiple ApplicationMaster in the same JVM.
  • + *
  • When using the singleton, users don't need to do anything special, + * {@link ClientToAMTokenSecretManager} is already set up to use the default + * singleton {@link ClientToAMTokenCache}
  • + *
+ *

+ */ +@Public +@Evolving +public class ClientToAMTokenCache { + private static final ClientToAMTokenCache CLIENT_TO_AM_TOKEN_CACHE = + new ClientToAMTokenCache(); + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final Lock readLock = readWriteLock.readLock(); + private final Lock writeLock = readWriteLock.writeLock(); + + private ConcurrentHashMap tokens; + + + private ClientToAMTokenCache() { + tokens = new ConcurrentHashMap(); + } + + /** + * Returns the singleton ClientToAM token cache. + * + * @return the singleton ClientToAM token cache. + */ + public static ClientToAMTokenCache getSingleton() { + return CLIENT_TO_AM_TOKEN_CACHE; + } + + /** + * Returns ClientToAMToken secretKey, null if absent. Only the singleton + * obtained from {@link #getSingleton()} is looked at for the key. + * + * @return {@link SecretKey} SecretKey required for Client to communicate + * with this AM. + */ + @Public + public static ByteBuffer getSecretKey(ApplicationAttemptId attemptId) { + return CLIENT_TO_AM_TOKEN_CACHE.getSecretKeyInternal(attemptId); + } + + private ByteBuffer getSecretKeyInternal(ApplicationAttemptId attemptId) { + try { + this.readLock.lock(); + if (attemptId == null) { + return null; + } + ClientToAMTokenInformation token = tokens.get(attemptId); + if (null == token) { + return null; + } + return token.getSecretKey(); + } finally { + this.readLock.unlock(); + } + } + + /** + * Sets the SecretKey only in the singleton obtained from + * {@link #getSingleton()}. + * + * @param key + * SecretKey + */ + @Public + public static void + setSecretKey(ApplicationAttemptId attemptId, ByteBuffer key) { + CLIENT_TO_AM_TOKEN_CACHE.setSecretKeyInternal(attemptId, key); + } + + private void setSecretKeyInternal(ApplicationAttemptId attemptId, + ByteBuffer key) { + try { + this.writeLock.lock(); + if (key != null && attemptId != null) { + ClientToAMTokenInformation token = tokens.get(attemptId); + if (null != token) { + token.setSecretKey(key); + } else { + token = new ClientToAMTokenInformation(key); + } + tokens.put(attemptId, token); + } + } finally { + this.writeLock.unlock(); + } + } + + @Public + public static long getRenewData(ApplicationAttemptId attemptId) { + return CLIENT_TO_AM_TOKEN_CACHE.getRenewDataInternal(attemptId); + } + + private long getRenewDataInternal(ApplicationAttemptId attemptId) { + try { + this.readLock.lock(); + if (attemptId == null) { + return 0; + } + ClientToAMTokenInformation token = tokens.get(attemptId); + if (null == token) { + return 0; + } + return token.getRenewDate(); + } finally { + this.readLock.unlock(); + } + } + + @Public + public static void + setRenewDate(ApplicationAttemptId attemptId, long renewDate) { + CLIENT_TO_AM_TOKEN_CACHE.setRenewDateInternal(attemptId, renewDate); + } + + private void setRenewDateInternal(ApplicationAttemptId attemptId, + long renewDate) { + try { + this.writeLock.lock(); + if (attemptId != null) { + ClientToAMTokenInformation token = tokens.get(attemptId); + if (null != token) { + token.setRenwDate(renewDate); + } else { + token = new ClientToAMTokenInformation(renewDate); + } + tokens.put(attemptId, token); + } + } finally { + this.writeLock.unlock(); + } + } + + private static class ClientToAMTokenInformation { + long renewDate; + ByteBuffer secretKey; + + public ClientToAMTokenInformation(long renewDate) { + this.renewDate = renewDate; + this.secretKey = null; + } + + public ClientToAMTokenInformation(ByteBuffer secretKey) { + this.renewDate = 0; + this.secretKey = secretKey; + } + + /** returns renew date */ + public long getRenewDate() { + return renewDate; + } + + public void setRenwDate(long renewDate) { + this.renewDate = renewDate; + } + + /** returns secretKey */ + ByteBuffer getSecretKey() { + return secretKey; + } + + void setSecretKey(ByteBuffer secretKey) { + this.secretKey = secretKey; + } + } +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/BaseClientToAMTokenSecretManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/BaseClientToAMTokenSecretManager.java index c455aac..b659b3d 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/BaseClientToAMTokenSecretManager.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/BaseClientToAMTokenSecretManager.java @@ -24,6 +24,7 @@ import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Evolving; import org.apache.hadoop.security.token.SecretManager; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; /** @@ -43,6 +44,10 @@ public abstract SecretKey getMasterKey( ApplicationAttemptId applicationAttemptId); @Private + public abstract long getRenewDate(ApplicationAttemptId applicationAttemptId) + throws InvalidToken; + + @Private @Override public synchronized byte[] createPassword( ClientToAMTokenIdentifier identifier) { @@ -54,10 +59,14 @@ public abstract SecretKey getMasterKey( @Override public byte[] retrievePassword(ClientToAMTokenIdentifier identifier) throws SecretManager.InvalidToken { - SecretKey masterKey = getMasterKey(identifier.getApplicationAttemptID()); + ApplicationAttemptId appAttemptId = identifier.getApplicationAttemptID(); + SecretKey masterKey = getMasterKey(appAttemptId); if (masterKey == null) { throw new SecretManager.InvalidToken("Illegal client-token!"); } + if (getRenewDate(appAttemptId) < Time.now()) { + throw new InvalidToken(appAttemptId + ". token is expired"); + } return createPassword(identifier.getBytes(), masterKey); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/ClientToAMTokenSecretManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/ClientToAMTokenSecretManager.java index 541f7a8..a5c2e6d 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/ClientToAMTokenSecretManager.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/security/client/ClientToAMTokenSecretManager.java @@ -18,12 +18,15 @@ package org.apache.hadoop.yarn.security.client; +import java.nio.ByteBuffer; + import javax.crypto.SecretKey; import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Evolving; import org.apache.hadoop.security.token.SecretManager; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.security.ClientToAMTokenCache; /** * A simple {@link SecretManager} for AMs to validate Client-RM tokens issued to @@ -37,27 +40,48 @@ public class ClientToAMTokenSecretManager extends BaseClientToAMTokenSecretManager { - // Only one master-key for AM - private SecretKey masterKey; - public ClientToAMTokenSecretManager( ApplicationAttemptId applicationAttemptID, byte[] key) { super(); - if (key != null) { - this.masterKey = SecretManager.createSecretKey(key); - } else { - this.masterKey = null; + if (key != null) { + setMasterKey(applicationAttemptID, key); } - + } + + public ClientToAMTokenSecretManager( + ApplicationAttemptId applicationAttemptID, byte[] key, long renewDate) { + this(applicationAttemptID, key); + setRenewDate(applicationAttemptID, renewDate); } @Override public SecretKey getMasterKey(ApplicationAttemptId applicationAttemptID) { // Only one master-key for AM, just return that. - return this.masterKey; + if (ClientToAMTokenCache.getSecretKey(applicationAttemptID) == null) { + return null; + } + return getSecretKey(ClientToAMTokenCache.getSecretKey(applicationAttemptID) + .array()); + } + + public void + setMasterKey(ApplicationAttemptId applicationAttemptID, byte[] key) { + ClientToAMTokenCache.setSecretKey(applicationAttemptID, + ByteBuffer.wrap(key)); + } + + @Override + public long getRenewDate(ApplicationAttemptId applicationAttemptId) + throws InvalidToken { + return ClientToAMTokenCache.getRenewData(applicationAttemptId); + } + + public void setRenewDate(ApplicationAttemptId applicationAttemptId, + long renewDate) { + ClientToAMTokenCache.setRenewDate(applicationAttemptId, renewDate); } - public void setMasterKey(byte[] key) { - this.masterKey = SecretManager.createSecretKey(key); + private SecretKey getSecretKey(byte[] key) { + return SecretManager.createSecretKey(key); } } \ No newline at end of file diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java index d77180c..21d65be 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ApplicationMasterService.java @@ -295,9 +295,17 @@ public RegisterApplicationMasterResponse registerApplicationMaster( response.setQueue(app.getQueue()); if (UserGroupInformation.isSecurityEnabled()) { LOG.info("Setting client token master key"); + if (rmContext.getClientToAMTokenSecretManager().getMasterKey( + applicationAttemptId) == null) { + throw new YarnException("Client Token has not been set for " + + applicationAttemptId); + } response.setClientToAMTokenMasterKey(java.nio.ByteBuffer.wrap(rmContext .getClientToAMTokenSecretManager() - .getMasterKey(applicationAttemptId).getEncoded())); + .getMasterKey(applicationAttemptId).getEncoded())); + response.setClientToAMTokenRenewDate(rmContext + .getClientToAMTokenSecretManager().getRenewDate( + applicationAttemptId)); } // For work-preserving AM restart, retrieve previous attempts' containers @@ -529,6 +537,12 @@ public AllocateResponse allocate(AllocateRequest request) allocateResponse.setNMTokens(allocation.getNMTokens()); } + if (UserGroupInformation.isSecurityEnabled() + && this.rmContext.getClientToAMTokenSecretManager().getRenewDate( + appAttemptId) > request.getClientToAMTokenRenewDate()) { + allocateResponse.setClientToAMTokenRenewDate(this.rmContext + .getClientToAMTokenSecretManager().getRenewDate(appAttemptId)); + } // update the response with the deltas of node status changes List updatedNodes = new ArrayList(); if(app.pullRMNodeUpdates(updatedNodes) > 0) { diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMSecretManagerService.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMSecretManagerService.java index d0d7d16..52a91ac 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMSecretManagerService.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/RMSecretManagerService.java @@ -57,7 +57,7 @@ public RMSecretManagerService(Configuration conf, RMContextImpl rmContext) { containerTokenSecretManager = createContainerTokenSecretManager(conf); rmContext.setContainerTokenSecretManager(containerTokenSecretManager); - clientToAMSecretManager = createClientToAMTokenSecretManager(); + clientToAMSecretManager = createClientToAMTokenSecretManager(conf); rmContext.setClientToAMTokenSecretManager(clientToAMSecretManager); amRmTokenSecretManager = createAMRMTokenSecretManager(conf, this.rmContext); @@ -101,6 +101,9 @@ public void serviceStop() throws Exception { if(nmTokenSecretManager != null) { nmTokenSecretManager.stop(); } + if(clientToAMSecretManager != null) { + clientToAMSecretManager.stop(); + } super.serviceStop(); } @@ -119,8 +122,9 @@ protected AMRMTokenSecretManager createAMRMTokenSecretManager( return new AMRMTokenSecretManager(conf, rmContext); } - protected ClientToAMTokenSecretManagerInRM createClientToAMTokenSecretManager() { - return new ClientToAMTokenSecretManagerInRM(); + protected ClientToAMTokenSecretManagerInRM + createClientToAMTokenSecretManager(Configuration conf) { + return new ClientToAMTokenSecretManagerInRM(conf); } @VisibleForTesting diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ClientToAMTokenSecretManagerInRM.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ClientToAMTokenSecretManagerInRM.java index 4fbe2ce..1e3bbcf 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ClientToAMTokenSecretManagerInRM.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/ClientToAMTokenSecretManagerInRM.java @@ -20,45 +20,227 @@ import java.util.HashMap; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.crypto.SecretKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.security.client.BaseClientToAMTokenSecretManager; public class ClientToAMTokenSecretManagerInRM extends BaseClientToAMTokenSecretManager { + private static final Log LOG = LogFactory + .getLog(ClientToAMTokenSecretManagerInRM.class); + + private final long tokenRenewIntervel; + private final long tokenRenewDelay; + // global single timer (daemon) + private Timer renewalTimer; + // Per application master-keys for managing client-tokens - private Map masterKeys = - new HashMap(); + private Map masterKeys = + new HashMap(); + + private Map timerTasks = + new HashMap(); + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + private final Lock readLock = readWriteLock.readLock(); + private final Lock writeLock = readWriteLock.writeLock(); - public synchronized SecretKey createMasterKey( + public ClientToAMTokenSecretManagerInRM(Configuration conf) { + super(); + this.tokenRenewIntervel = + conf.getLong( + YarnConfiguration.CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS, + YarnConfiguration.DEFAULT_CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS) * 1000; + // Adding delay = 1.5 * expiry interval makes sure that all active AMs get + // the updated renewDate for the token. + this.tokenRenewDelay = + (long) (conf.getLong(YarnConfiguration.RM_AM_EXPIRY_INTERVAL_MS, + YarnConfiguration.DEFAULT_RM_AM_EXPIRY_INTERVAL_MS) * 1.5); + if (tokenRenewIntervel <= tokenRenewDelay * 2) { + throw new IllegalArgumentException( + YarnConfiguration.CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS + + " should be more than 2 X " + + YarnConfiguration.RM_AM_EXPIRY_INTERVAL_MS); + } + renewalTimer = new Timer(true); + } + + public SecretKey createMasterKey( ApplicationAttemptId applicationAttemptID) { - return generateSecret(); + try { + this.writeLock.lock(); + return generateSecret(); + } finally { + this.writeLock.unlock(); + } } - public synchronized void registerApplication( + public void registerApplication( ApplicationAttemptId applicationAttemptID, SecretKey key) { - this.masterKeys.put(applicationAttemptID, key); + try { + this.writeLock.lock(); + ClientToAMTokenInformation tokenInfo = + new ClientToAMTokenInformation(applicationAttemptID, Time.now() + + tokenRenewIntervel, key); + this.masterKeys.put(applicationAttemptID, tokenInfo); + setTimerForTokenRenewal(tokenInfo); + } finally { + this.writeLock.unlock(); + } } // Only for RM recovery - public synchronized SecretKey registerMasterKey( + public SecretKey registerMasterKey( ApplicationAttemptId applicationAttemptID, byte[] keyData) { - SecretKey key = createSecretKey(keyData); - registerApplication(applicationAttemptID, key); - return key; + try { + this.writeLock.lock(); + SecretKey key = createSecretKey(keyData); + registerApplication(applicationAttemptID, key); + return key; + } finally { + this.writeLock.unlock(); + } + } + + public void stop() { + this.renewalTimer.cancel(); } - public synchronized void unRegisterApplication( + public void unRegisterApplication( ApplicationAttemptId applicationAttemptID) { - this.masterKeys.remove(applicationAttemptID); + try { + this.writeLock.lock(); + cancelToken(applicationAttemptID); + } finally { + this.writeLock.unlock(); + } } @Override - public synchronized SecretKey getMasterKey( + public SecretKey getMasterKey( + ApplicationAttemptId applicationAttemptID) { + try { + this.readLock.lock(); + if (! this.masterKeys.containsKey(applicationAttemptID)) { + return null; + } + return this.masterKeys.get(applicationAttemptID).getSecretKey(); + } finally { + this.readLock.unlock(); + } + } + + /** + * Task - to renew a token + * + */ + private class RenewalTimerTask extends TimerTask { + private ClientToAMTokenInformation ct; + private AtomicBoolean cancelled = new AtomicBoolean(false); + + RenewalTimerTask(ClientToAMTokenInformation t) { + ct = t; + } + + @Override + public void run() { + if (cancelled.get()) { + return; + } + renewToken(ct); + } + + @Override + public boolean cancel() { + cancelled.set(true);; + return super.cancel(); + } + } + + private void renewToken(ClientToAMTokenInformation ct) { + try { + this.writeLock.lock(); + ct.setRenwDate(tokenRenewIntervel + Time.now()); + setTimerForTokenRenewal(ct);// set the next one + } finally { + this.writeLock.unlock(); + } + } + + private void setTimerForTokenRenewal(ClientToAMTokenInformation tokenInfo) { + RenewalTimerTask timerTask = new RenewalTimerTask(tokenInfo); + timerTasks.put(tokenInfo.getApplicationAttemptID(), timerTask); + renewalTimer.schedule(timerTask, tokenRenewIntervel - tokenRenewDelay); + } + + private void cancelToken( ApplicationAttemptId applicationAttemptID) { - return this.masterKeys.get(applicationAttemptID); + if (timerTasks.containsKey(applicationAttemptID)) { + timerTasks.get(applicationAttemptID).cancel(); + } + masterKeys.remove(applicationAttemptID); + } + + /** Class to encapsulate a token's renew date and SecretKey. */ + private static class ClientToAMTokenInformation { + ApplicationAttemptId applicationAttemptID; + long renewDate; + SecretKey secretKey; + + public ClientToAMTokenInformation( + ApplicationAttemptId applicationAttemptID, long renewDate, + SecretKey secretKey) { + this.applicationAttemptID = applicationAttemptID; + this.renewDate = renewDate; + this.secretKey = secretKey; + } + + /** returns applicationAttemptID */ + public ApplicationAttemptId getApplicationAttemptID() { + return applicationAttemptID; + } + + /** returns renew date */ + public long getRenewDate() { + return renewDate; + } + + public void setRenwDate(long renewDate) { + this.renewDate = renewDate; + } + + /** returns secretKey */ + SecretKey getSecretKey() { + return secretKey; + } + } + + @Override + public long getRenewDate(ApplicationAttemptId applicationAttemptId) + throws InvalidToken { + try { + this.readLock.lock(); + if (!masterKeys.containsKey(applicationAttemptId)) { + throw new InvalidToken("Client-token for " + applicationAttemptId + + " has not been created!"); + } + return masterKeys.get(applicationAttemptId).getRenewDate(); + } finally { + this.readLock.unlock(); + } } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestAppManager.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestAppManager.java index d720eb6..8f23fe1 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestAppManager.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestAppManager.java @@ -211,7 +211,7 @@ public void setUp() { ApplicationMasterService masterService = new ApplicationMasterService(rmContext, scheduler); appMonitor = new TestRMAppManager(rmContext, - new ClientToAMTokenSecretManagerInRM(), scheduler, masterService, + new ClientToAMTokenSecretManagerInRM(conf), scheduler, masterService, new ApplicationACLsManager(conf), conf); appId = MockApps.newAppID(1); @@ -485,7 +485,7 @@ public void testRMAppSubmitMaxAppAttempts() throws Exception { ApplicationMasterService masterService = new ApplicationMasterService(rmContext, scheduler); TestRMAppManager appMonitor = new TestRMAppManager(rmContext, - new ClientToAMTokenSecretManagerInRM(), scheduler, masterService, + new ClientToAMTokenSecretManagerInRM(conf), scheduler, masterService, new ApplicationACLsManager(conf), conf); ApplicationId appID = MockApps.newAppID(i * 4 + j + 1); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java index 620ba9f..fa88197 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/RMStateStoreTestBase.java @@ -185,7 +185,7 @@ void testRMAppStateStore(RMStateStoreHelper stateStoreHelper) when(appTokenMgr.getMasterKey()).thenReturn(masterKeyData); ClientToAMTokenSecretManagerInRM clientToAMTokenMgr = - new ClientToAMTokenSecretManagerInRM(); + new ClientToAMTokenSecretManagerInRM(conf); ApplicationAttemptId attemptId1 = ConverterUtils .toApplicationAttemptId("appattempt_1352994193343_0001_000001"); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java index 2fc4431..e995c0e 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java @@ -195,7 +195,7 @@ public void setUp() throws Exception { null, new AMRMTokenSecretManager(conf, this.rmContext), new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), + new ClientToAMTokenSecretManagerInRM(conf), writer); ((RMContextImpl)rmContext).setStateStore(store); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/TestRMAppAttemptTransitions.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/TestRMAppAttemptTransitions.java index efcecd9..7c03166 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/TestRMAppAttemptTransitions.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/TestRMAppAttemptTransitions.java @@ -136,7 +136,7 @@ private AMRMTokenSecretManager amRMTokenManager = spy(new AMRMTokenSecretManager(conf, rmContext)); private ClientToAMTokenSecretManagerInRM clientToAMTokenManager = - spy(new ClientToAMTokenSecretManagerInRM()); + spy(new ClientToAMTokenSecretManagerInRM(conf)); private NMTokenSecretManagerInRM nmTokenManager = spy(new NMTokenSecretManagerInRM(conf)); private boolean transferStateFromPreviousAttempt = false; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java index 0efd48f..ad3ebd3 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java @@ -378,7 +378,7 @@ public void testRefreshQueues() throws Exception { RMContextImpl rmContext = new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null); + new ClientToAMTokenSecretManagerInRM(conf), null); setupQueueConfiguration(conf); cs.setConf(new YarnConfiguration()); cs.setRMContext(resourceManager.getRMContext()); @@ -484,7 +484,7 @@ public void testParseQueue() throws IOException { cs.reinitialize(conf, new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null)); + new ClientToAMTokenSecretManagerInRM(conf), null)); } @Test @@ -500,7 +500,7 @@ public void testReconnectedNode() throws Exception { cs.reinitialize(csConf, new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(csConf), new NMTokenSecretManagerInRM(csConf), - new ClientToAMTokenSecretManagerInRM(), null)); + new ClientToAMTokenSecretManagerInRM(csConf), null)); RMNode n1 = MockNodes.newNodeInfo(0, MockNodes.newResource(4 * GB), 1); RMNode n2 = MockNodes.newNodeInfo(0, MockNodes.newResource(2 * GB), 2); @@ -531,7 +531,7 @@ public void testRefreshQueuesWithNewQueue() throws Exception { cs.reinitialize(conf, new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null)); + new ClientToAMTokenSecretManagerInRM(conf), null)); checkQueueCapacities(cs, A_CAPACITY, B_CAPACITY); // Add a new queue b4 @@ -852,7 +852,7 @@ public void testNumClusterNodes() throws Exception { RMContextImpl rmContext = new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null); + new ClientToAMTokenSecretManagerInRM(conf), null); cs.setRMContext(rmContext); CapacitySchedulerConfiguration csConf = new CapacitySchedulerConfiguration(); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestQueueParsing.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestQueueParsing.java index a3b990c..92460c3 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestQueueParsing.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestQueueParsing.java @@ -46,7 +46,7 @@ public void testQueueParsing() throws Exception { RMContextImpl rmContext = new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null); + new ClientToAMTokenSecretManagerInRM(conf), null); capacityScheduler.setConf(conf); capacityScheduler.setRMContext(rmContext); capacityScheduler.init(conf); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestUtils.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestUtils.java index e548661..0bb3781 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestUtils.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestUtils.java @@ -91,7 +91,7 @@ public EventHandler getEventHandler() { new AMRMTokenSecretManager(conf, null), new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), writer); + new ClientToAMTokenSecretManagerInRM(conf), writer); return rmContext; } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestClientToAMTokens.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestClientToAMTokens.java index 0dcd228..b62c720 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestClientToAMTokens.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestClientToAMTokens.java @@ -32,7 +32,6 @@ import javax.security.sasl.SaslException; import org.junit.Assert; - import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.ipc.RPC; @@ -43,11 +42,13 @@ import org.apache.hadoop.security.SecurityInfo; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.token.SecretManager.InvalidToken; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.security.token.TokenInfo; import org.apache.hadoop.security.token.TokenSelector; import org.apache.hadoop.service.AbstractService; +import org.apache.hadoop.util.Time; import org.apache.hadoop.yarn.api.ContainerManagementProtocol; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportRequest; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportResponse; @@ -56,6 +57,7 @@ import org.apache.hadoop.yarn.api.protocolrecords.StartContainersResponse; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ApplicationReport; +import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.event.Dispatcher; import org.apache.hadoop.yarn.event.DrainDispatcher; import org.apache.hadoop.yarn.exceptions.YarnException; @@ -115,11 +117,14 @@ public KerberosInfo getKerberosInfo(Class protocol, Configuration conf) { private final byte[] secretKey; private InetSocketAddress address; private boolean pinged = false; + private ClientToAMTokenSecretManager manager; - public CustomAM(ApplicationAttemptId appId, byte[] secretKey) { + public CustomAM(ApplicationAttemptId appId, byte[] secretKey, long renewDate) { super("CustomAM"); this.appAttemptId = appId; this.secretKey = secretKey; + this.manager = new ClientToAMTokenSecretManager( + this.appAttemptId, this.secretKey, renewDate); } @Override @@ -137,9 +142,7 @@ protected void serviceStart() throws Exception { new RPC.Builder(conf) .setProtocol(CustomProtocol.class) .setNumHandlers(1) - .setSecretManager( - new ClientToAMTokenSecretManager(this.appAttemptId, secretKey)) - .setInstance(this).build(); + .setSecretManager(manager).setInstance(this).build(); } catch (Exception e) { throw new YarnRuntimeException(e); } @@ -151,8 +154,11 @@ protected void serviceStart() throws Exception { @Test public void testClientToAMTokens() throws Exception { - + final int renewInterval = 13; final Configuration conf = new Configuration(); + conf.setLong(YarnConfiguration.CLIENT_TO_AM_TOKEN_RENEW_INTERVAL_KEY_SECS, + renewInterval); + conf.setLong(YarnConfiguration.RM_AM_EXPIRY_INTERVAL_MS, 4000); conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos"); UserGroupInformation.setConfiguration(conf); @@ -225,11 +231,13 @@ public RegisterApplicationMasterResponse run() { org.apache.hadoop.yarn.api.records.Token originalClientToAMToken = appReport.getClientToAMToken(); + long startTime = Time.now(); // ClientToAMToken master key should have been received on register // application master response. Assert.assertNotNull(response.getClientToAMTokenMasterKey()); Assert .assertTrue(response.getClientToAMTokenMasterKey().array().length > 0); + Assert.assertTrue(response.getClientToAMTokenRenewDate() > 0); // Start the AM with the correct shared-secret. ApplicationAttemptId appAttemptId = @@ -237,7 +245,7 @@ public RegisterApplicationMasterResponse run() { Assert.assertNotNull(appAttemptId); final CustomAM am = new CustomAM(appAttemptId, response.getClientToAMTokenMasterKey() - .array()); + .array(), response.getClientToAMTokenRenewDate()); am.init(conf); am.start(); @@ -267,6 +275,12 @@ public RegisterApplicationMasterResponse run() { // Now for an authenticated user verifyValidToken(conf, am, token); + + // reset the ClientToAMToken RenewDate + while(Time.now() - startTime < renewInterval * 1000) { + Thread.sleep(1000); + } + verifyValidExpiredToken(conf, am, token); } private void verifyTokenWithTamperedID(final Configuration conf, @@ -280,7 +294,7 @@ private void verifyTokenWithTamperedID(final Configuration conf, .getClusterTimestamp(), 42), 43), UserGroupInformation .getCurrentUser().getShortUserName()); - verifyTamperedToken(conf, am, token, ugi, maliciousID); + verifyTamperedToken(conf, am, token, ugi, maliciousID, false); } private void verifyTokenWithTamperedUserName(final Configuration conf, @@ -291,12 +305,12 @@ private void verifyTokenWithTamperedUserName(final Configuration conf, ClientToAMTokenIdentifier maliciousID = new ClientToAMTokenIdentifier(am.appAttemptId, "evilOrc"); - verifyTamperedToken(conf, am, token, ugi, maliciousID); + verifyTamperedToken(conf, am, token, ugi, maliciousID, true); } private void verifyTamperedToken(final Configuration conf, final CustomAM am, Token token, UserGroupInformation ugi, - ClientToAMTokenIdentifier maliciousID) { + ClientToAMTokenIdentifier maliciousID, boolean tamperedUserName) { Token maliciousToken = new Token(maliciousID.getBytes(), token.getPassword(), token.getKind(), @@ -326,6 +340,7 @@ public Void run() throws Exception { Assert.assertEquals(RemoteException.class.getName(), e.getClass() .getName()); e = ((RemoteException)e).unwrapRemoteException(); + if (tamperedUserName) { Assert .assertEquals(SaslException.class .getCanonicalName(), e.getClass().getCanonicalName()); @@ -334,6 +349,10 @@ public Void run() throws Exception { .contains( "DIGEST-MD5: digest response format violation. " + "Mismatched response.")); + } else { + Assert.assertTrue(e instanceof InvalidToken); + Assert.assertTrue(e.getMessage().contains("Illegal client-token!")); + } Assert.assertFalse(am.pinged); } } @@ -357,4 +376,30 @@ public Void run() throws Exception { } }); } + + private void verifyValidExpiredToken(final Configuration conf, + final CustomAM am, Token token) + throws IOException, InterruptedException { + UserGroupInformation ugi; + ugi = UserGroupInformation.createRemoteUser("me"); + ugi.addToken(token); + + try { + ugi.doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + CustomProtocol client = + (CustomProtocol) RPC.getProxy(CustomProtocol.class, 1L, + am.address, conf); + client.ping(); + fail("The token is expired"); + return null; + } + }); + } catch (Exception ex) { + ex = ((RemoteException) ex).unwrapRemoteException(); + Assert.assertTrue(ex instanceof InvalidToken); + Assert.assertTrue(ex.getMessage().contains("token is expired")); + } + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebApp.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebApp.java index 0df7c0d..7a30f40 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebApp.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebApp.java @@ -206,7 +206,7 @@ public static CapacityScheduler mockCapacityScheduler() throws IOException { cs.setRMContext(new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null)); + new ClientToAMTokenSecretManagerInRM(conf), null)); cs.init(conf); return cs; } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebAppFairScheduler.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebAppFairScheduler.java index 1de6489..26a31f9 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebAppFairScheduler.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebAppFairScheduler.java @@ -109,7 +109,7 @@ private static FairScheduler mockFairScheduler() throws IOException { fs.setRMContext(new RMContextImpl(null, null, null, null, null, null, new RMContainerTokenSecretManager(conf), new NMTokenSecretManagerInRM(conf), - new ClientToAMTokenSecretManagerInRM(), null)); + new ClientToAMTokenSecretManagerInRM(conf), null)); fs.init(conf); return fs; }