Package org.apache.hadoop.yarn.server.nodemanager.security

Source Code of org.apache.hadoop.yarn.server.nodemanager.security.NMContainerTokenSecretManager

/**
* 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 java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.token.SecretManager;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.security.ContainerTokenIdentifier;
import org.apache.hadoop.yarn.server.api.records.MasterKey;
import org.apache.hadoop.yarn.server.security.BaseContainerTokenSecretManager;
import org.apache.hadoop.yarn.server.security.MasterKeyData;

/**
* The NM maintains only two master-keys. The current key that RM knows and the
* key from the previous rolling-interval.
*
*/
public class NMContainerTokenSecretManager extends
    BaseContainerTokenSecretManager {

  private static final Log LOG = LogFactory
      .getLog(NMContainerTokenSecretManager.class);
 
  private MasterKeyData previousMasterKey;
  private final TreeMap<Long, List<ContainerId>> recentlyStartedContainerTracker;

 
  private String nodeHostAddr;
 
  public NMContainerTokenSecretManager(Configuration conf) {
    super(conf);
    recentlyStartedContainerTracker =
        new TreeMap<Long, List<ContainerId>>();
  }

  /**
   * Used by NodeManagers to create a token-secret-manager with the key obtained
   * from the RM. This can happen during registration or when the RM rolls the
   * master-key and signals the NM.
   *
   * @param masterKeyRecord
   */
  @Private
  public synchronized void setMasterKey(MasterKey masterKeyRecord) {
    LOG.info("Rolling master-key for container-tokens, got key with id "
        + masterKeyRecord.getKeyId());
    if (super.currentMasterKey == null) {
      super.currentMasterKey =
          new MasterKeyData(masterKeyRecord, createSecretKey(masterKeyRecord
            .getBytes().array()));
    } else {
      if (super.currentMasterKey.getMasterKey().getKeyId() != masterKeyRecord
          .getKeyId()) {
        // Update keys only if the key has changed.
        this.previousMasterKey = super.currentMasterKey;
        super.currentMasterKey =
            new MasterKeyData(masterKeyRecord, createSecretKey(masterKeyRecord
              .getBytes().array()));
      }
    }
  }

  /**
   * Override of this is to validate ContainerTokens generated by using
   * different {@link MasterKey}s.
   */
  @Override
  public synchronized byte[] retrievePassword(
      ContainerTokenIdentifier identifier) throws SecretManager.InvalidToken {
    int keyId = identifier.getMasterKeyId();

    MasterKeyData masterKeyToUse = null;
    if (this.previousMasterKey != null
        && keyId == this.previousMasterKey.getMasterKey().getKeyId()) {
      // A container-launch has come in with a token generated off the last
      // master-key
      masterKeyToUse = this.previousMasterKey;
    } else if (keyId == super.currentMasterKey.getMasterKey().getKeyId()) {
      // A container-launch has come in with a token generated off the current
      // master-key
      masterKeyToUse = super.currentMasterKey;
    }

    if (nodeHostAddr != null
        && !identifier.getNmHostAddress().equals(nodeHostAddr)) {
      // Valid container token used for incorrect node.
      throw new SecretManager.InvalidToken("Given Container "
          + identifier.getContainerID().toString()
          + " identifier is not valid for current Node manager. Expected : "
          + nodeHostAddr + " Found : " + identifier.getNmHostAddress());
    }
   
    if (masterKeyToUse != null) {
      return retrievePasswordInternal(identifier, masterKeyToUse);
    }

    // Invalid request. Like startContainer() with token generated off
    // old-master-keys.
    throw new SecretManager.InvalidToken("Given Container "
        + identifier.getContainerID().toString()
        + " seems to have an illegally generated token.");
  }

  /**
   * Container start has gone through. We need to store the containerId in order
   * to block future container start requests with same container token. This
   * container token needs to be saved till its container token expires.
   */
  public synchronized void startContainerSuccessful(
      ContainerTokenIdentifier tokenId) {

    removeAnyContainerTokenIfExpired();
   
    Long expTime = tokenId.getExpiryTimeStamp();
    // We might have multiple containers with same expiration time.
    if (!recentlyStartedContainerTracker.containsKey(expTime)) {
      recentlyStartedContainerTracker
        .put(expTime, new ArrayList<ContainerId>());
    }
    recentlyStartedContainerTracker.get(expTime).add(tokenId.getContainerID());

  }

  protected synchronized void removeAnyContainerTokenIfExpired() {
    // Trying to remove any container if its container token has expired.
    Iterator<Entry<Long, List<ContainerId>>> containersI =
        this.recentlyStartedContainerTracker.entrySet().iterator();
    Long currTime = System.currentTimeMillis();
    while (containersI.hasNext()) {
      Entry<Long, List<ContainerId>> containerEntry = containersI.next();
      if (containerEntry.getKey() < currTime) {
        containersI.remove();
      } else {
        break;
      }
    }
  }

  /**
   * Container will be remembered based on expiration time of the container
   * token used for starting the container. It is safe to use expiration time
   * as there is one to many mapping between expiration time and containerId.
   * @return true if the current token identifier is not present in cache.
   */
  public synchronized boolean isValidStartContainerRequest(
      ContainerTokenIdentifier containerTokenIdentifier) {

    removeAnyContainerTokenIfExpired();

    Long expTime = containerTokenIdentifier.getExpiryTimeStamp();
    List<ContainerId> containers =
        this.recentlyStartedContainerTracker.get(expTime);
    if (containers == null
        || !containers.contains(containerTokenIdentifier.getContainerID())) {
      return true;
    } else {
      return false;
    }
  }

  public synchronized void setNodeId(NodeId nodeId) {
    nodeHostAddr = nodeId.toString();
    LOG.info("Updating node address : " + nodeHostAddr);
  }
}
TOP

Related Classes of org.apache.hadoop.yarn.server.nodemanager.security.NMContainerTokenSecretManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.