| /** |
| * 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.server.nodemanager.security; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.IOException; |
| |
| import org.apache.hadoop.io.Text; |
| import org.apache.hadoop.security.token.SecretManager.InvalidToken; |
| import org.apache.hadoop.security.token.Token; |
| import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; |
| import org.apache.hadoop.yarn.api.records.ApplicationId; |
| import org.apache.hadoop.yarn.api.records.NodeId; |
| import org.apache.hadoop.yarn.conf.YarnConfiguration; |
| import org.apache.hadoop.yarn.security.NMTokenIdentifier; |
| import org.apache.hadoop.yarn.server.api.records.MasterKey; |
| import org.apache.hadoop.yarn.server.nodemanager.recovery.NMMemoryStateStoreService; |
| import org.apache.hadoop.yarn.server.security.BaseNMTokenSecretManager; |
| import org.apache.hadoop.yarn.util.ConverterUtils; |
| import org.junit.Test; |
| |
| public class TestNMTokenSecretManagerInNM { |
| |
| @Test |
| public void testRecovery() throws IOException { |
| YarnConfiguration conf = new YarnConfiguration(); |
| conf.setBoolean(YarnConfiguration.NM_RECOVERY_ENABLED, true); |
| final NodeId nodeId = NodeId.newInstance("somehost", 1234); |
| final ApplicationAttemptId attempt1 = |
| ApplicationAttemptId.newInstance(ApplicationId.newInstance(1, 1), 1); |
| final ApplicationAttemptId attempt2 = |
| ApplicationAttemptId.newInstance(ApplicationId.newInstance(2, 2), 2); |
| NMTokenKeyGeneratorForTest keygen = new NMTokenKeyGeneratorForTest(); |
| NMMemoryStateStoreService stateStore = new NMMemoryStateStoreService(); |
| stateStore.init(conf); |
| stateStore.start(); |
| NMTokenSecretManagerInNM secretMgr = |
| new NMTokenSecretManagerInNM(stateStore); |
| secretMgr.setNodeId(nodeId); |
| MasterKey currentKey = keygen.generateKey(); |
| secretMgr.setMasterKey(currentKey); |
| NMTokenIdentifier attemptToken1 = |
| getNMTokenId(secretMgr.createNMToken(attempt1, nodeId, "user1")); |
| NMTokenIdentifier attemptToken2 = |
| getNMTokenId(secretMgr.createNMToken(attempt2, nodeId, "user2")); |
| secretMgr.appAttemptStartContainer(attemptToken1); |
| secretMgr.appAttemptStartContainer(attemptToken2); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt1)); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt2)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken1)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken2)); |
| |
| // restart and verify key is still there and token still valid |
| secretMgr = new NMTokenSecretManagerInNM(stateStore); |
| secretMgr.recover(); |
| secretMgr.setNodeId(nodeId); |
| assertEquals(currentKey, secretMgr.getCurrentKey()); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt1)); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt2)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken1)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken2)); |
| |
| // roll master key and remove an app |
| currentKey = keygen.generateKey(); |
| secretMgr.setMasterKey(currentKey); |
| secretMgr.appFinished(attempt1.getApplicationId()); |
| |
| // restart and verify attempt1 key is still valid due to prev key persist |
| secretMgr = new NMTokenSecretManagerInNM(stateStore); |
| secretMgr.recover(); |
| secretMgr.setNodeId(nodeId); |
| assertEquals(currentKey, secretMgr.getCurrentKey()); |
| assertFalse(secretMgr.isAppAttemptNMTokenKeyPresent(attempt1)); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt2)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken1)); |
| assertNotNull(secretMgr.retrievePassword(attemptToken2)); |
| |
| // roll master key again, restart, and verify attempt1 key is bad but |
| // attempt2 is still good due to app key persist |
| currentKey = keygen.generateKey(); |
| secretMgr.setMasterKey(currentKey); |
| secretMgr = new NMTokenSecretManagerInNM(stateStore); |
| secretMgr.recover(); |
| secretMgr.setNodeId(nodeId); |
| assertEquals(currentKey, secretMgr.getCurrentKey()); |
| assertFalse(secretMgr.isAppAttemptNMTokenKeyPresent(attempt1)); |
| assertTrue(secretMgr.isAppAttemptNMTokenKeyPresent(attempt2)); |
| try { |
| secretMgr.retrievePassword(attemptToken1); |
| fail("attempt token should not still be valid"); |
| } catch (InvalidToken e) { |
| // expected |
| } |
| assertNotNull(secretMgr.retrievePassword(attemptToken2)); |
| |
| // remove last attempt, restart, verify both tokens are now bad |
| secretMgr.appFinished(attempt2.getApplicationId()); |
| secretMgr = new NMTokenSecretManagerInNM(stateStore); |
| secretMgr.recover(); |
| secretMgr.setNodeId(nodeId); |
| assertEquals(currentKey, secretMgr.getCurrentKey()); |
| assertFalse(secretMgr.isAppAttemptNMTokenKeyPresent(attempt1)); |
| assertFalse(secretMgr.isAppAttemptNMTokenKeyPresent(attempt2)); |
| try { |
| secretMgr.retrievePassword(attemptToken1); |
| fail("attempt token should not still be valid"); |
| } catch (InvalidToken e) { |
| // expected |
| } |
| try { |
| secretMgr.retrievePassword(attemptToken2); |
| fail("attempt token should not still be valid"); |
| } catch (InvalidToken e) { |
| // expected |
| } |
| |
| stateStore.close(); |
| } |
| |
| private NMTokenIdentifier getNMTokenId( |
| org.apache.hadoop.yarn.api.records.Token token) throws IOException { |
| Token<NMTokenIdentifier> convertedToken = |
| ConverterUtils.convertFromYarn(token, (Text) null); |
| return convertedToken.decodeIdentifier(); |
| } |
| |
| private static class NMTokenKeyGeneratorForTest extends |
| BaseNMTokenSecretManager { |
| public MasterKey generateKey() { |
| return createNewMasterKey().getMasterKey(); |
| } |
| } |
| } |