HDDS-8. Add OzoneManager Delegation Token support. Contributed by Ajay Kumar.


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/59767be1
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/59767be1
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/59767be1

Branch: refs/heads/HDDS-4
Commit: 59767be13a37557b6d51cd5356c58ebdfad63896
Parents: 9c0baf0
Author: Ajay Kumar <a...@apache.com>
Authored: Thu Nov 15 12:18:19 2018 -0800
Committer: Xiaoyu Yao <x...@apache.org>
Committed: Thu Nov 29 11:57:47 2018 -0800

----------------------------------------------------------------------
 .../apache/hadoop/ozone/OzoneConfigKeys.java    |   4 +
 .../org/apache/hadoop/ozone/OzoneConsts.java    |   1 +
 .../common/src/main/resources/ozone-default.xml |  33 +
 .../ozone/client/protocol/ClientProtocol.java   |  32 +
 .../hadoop/ozone/client/rest/RestClient.java    |  41 ++
 .../hadoop/ozone/client/rpc/RpcClient.java      |  41 ++
 .../ozone/client/TestHddsClientUtils.java       |   2 +-
 .../java/org/apache/hadoop/ozone/OmUtils.java   |  17 +-
 .../apache/hadoop/ozone/om/OMConfigKeys.java    |  13 +
 .../ozone/om/protocol/OzoneManagerProtocol.java |   2 +-
 .../protocol/OzoneManagerSecurityProtocol.java  |  67 +++
 ...neManagerProtocolClientSideTranslatorPB.java |  77 +++
 .../om/protocolPB/OzoneManagerProtocolPB.java   |   3 +
 .../hadoop/ozone/protocolPB/OMPBHelper.java     |  38 ++
 .../security/OzoneDelegationTokenSelector.java  |   3 +-
 .../ozone/security/OzoneSecretManager.java      | 598 +++++++++++++++++++
 .../hadoop/ozone/security/OzoneSecretStore.java | 250 ++++++++
 .../ozone/security/OzoneSecurityException.java  | 104 ++++
 .../src/main/proto/OzoneManagerProtocol.proto   |  19 +
 .../ozone/security/TestOzoneSecretManager.java  | 216 +++++++
 .../hadoop/ozone/TestSecureOzoneCluster.java    | 309 ++++++++--
 .../apache/hadoop/ozone/om/OzoneManager.java    | 300 ++++++++--
 ...neManagerProtocolServerSideTranslatorPB.java |  59 ++
 23 files changed, 2150 insertions(+), 79 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
----------------------------------------------------------------------
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
index ac97422..742fe3a 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConfigKeys.java
@@ -332,6 +332,10 @@ public final class OzoneConfigKeys {
   public static final String OZONE_CONTAINER_COPY_WORKDIR =
       "hdds.datanode.replication.work.dir";
 
+  public static final String OZONE_MAX_KEY_LEN =
+      "ozone.max.key.len";
+  public static final int OZONE_MAX_KEY_LEN_DEFAULT = 1024 * 1024;
+
   /**
    * Config properties to set client side checksum properties.
    */

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
----------------------------------------------------------------------
diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
index 096baee..e7c354c 100644
--- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
+++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/ozone/OzoneConsts.java
@@ -99,6 +99,7 @@ public final class OzoneConsts {
   public static final String DN_CONTAINER_DB = "-dn-"+ CONTAINER_DB_SUFFIX;
   public static final String DELETED_BLOCK_DB = "deletedBlock.db";
   public static final String OM_DB_NAME = "om.db";
+  public static final String OZONE_MANAGER_TOKEN_DB_NAME = "om-token.db";
 
   public static final String STORAGE_DIR_CHUNKS = "chunks";
 

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-hdds/common/src/main/resources/ozone-default.xml
----------------------------------------------------------------------
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml 
b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index 5fb594b..766f31d 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -984,6 +984,15 @@
       every principal specified in the keytab file.
     </description>
   </property>
+  <property>
+    <name>ozone.max.key.len</name>
+    <value>1048576</value>
+    <tag>OZONE, SECURITY</tag>
+    <description>
+      Maximum length of private key in Ozone. Used in Ozone delegation and
+      block tokens.
+    </description>
+  </property>
 
   <!--Client Settings-->
   <property>
@@ -1492,4 +1501,28 @@
       Name of file which stores public key generated for SCM CA.
     </description>
   </property>
+  <property>
+    <name>ozone.manager.delegation.remover.scan.interval</name>
+    <value>3600000</value>
+    <description>
+      Time interval after which ozone secret manger scans for expired
+      delegation token.
+    </description>
+  </property>
+  <property>
+    <name>ozone.manager.delegation.token.renew-interval</name>
+    <value>1d</value>
+    <description>
+      Default time interval after which ozone delegation token will
+      require renewal before any further use.
+    </description>
+  </property>
+  <property>
+    <name>ozone.manager.delegation.token.max-lifetime</name>
+    <value>7d</value>
+    <description>
+      Default max time interval after which ozone delegation token will
+      not be renewed.
+    </description>
+  </property>
 </configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 17634f2..2fe302e 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.ozone.client.protocol;
 
 import org.apache.hadoop.hdds.protocol.StorageType;
 import org.apache.hadoop.hdds.scm.ScmConfigKeys;
+import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ozone.OzoneAcl;
 import org.apache.hadoop.ozone.client.*;
 import org.apache.hadoop.hdds.client.OzoneQuota;
@@ -30,7 +31,9 @@ import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
 
 import java.io.IOException;
 import java.util.List;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
 import org.apache.hadoop.security.KerberosInfo;
+import org.apache.hadoop.security.token.Token;
 
 /**
  * An implementer of this interface is capable of connecting to Ozone Cluster
@@ -388,4 +391,33 @@ public interface ClientProtocol {
    */
   void close() throws IOException;
 
+  /**
+   * Get a valid Delegation Token.
+   *
+   * @param renewer the designated renewer for the token
+   * @return Token<OzoneDelegationTokenSelector>
+   * @throws IOException
+   */
+  Token<OzoneTokenIdentifier> getDelegationToken(Text renewer)
+      throws IOException;
+
+  /**
+   * Renew an existing delegation token.
+   *
+   * @param token delegation token obtained earlier
+   * @return the new expiration time
+   * @throws IOException
+   */
+  long renewDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException;
+
+  /**
+   * Cancel an existing delegation token.
+   *
+   * @param token delegation token
+   * @throws IOException
+   */
+  void cancelDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException;
+
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rest/RestClient.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rest/RestClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rest/RestClient.java
index 3fd0927..6e2841d 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rest/RestClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rest/RestClient.java
@@ -26,6 +26,7 @@ import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hdds.protocol.StorageType;
 import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.hdds.scm.client.HddsClientUtils;
+import org.apache.hadoop.io.Text;
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.ozone.OzoneAcl;
 import org.apache.hadoop.ozone.OzoneConfigKeys;
@@ -44,10 +45,12 @@ import 
org.apache.hadoop.ozone.client.rest.response.VolumeInfo;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
 import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServicePort;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
 import org.apache.hadoop.ozone.web.response.ListBuckets;
 import org.apache.hadoop.ozone.web.response.ListKeys;
 import org.apache.hadoop.ozone.web.response.ListVolumes;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.Time;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpHeaders;
@@ -667,6 +670,44 @@ public class RestClient implements ClientProtocol {
     }
   }
 
+  /**
+   * Get a valid Delegation Token. Not supported for RestClient.
+   *
+   * @param renewer the designated renewer for the token
+   * @return Token<OzoneDelegationTokenSelector>
+   * @throws IOException
+   */
+  @Override
+  public Token<OzoneTokenIdentifier> getDelegationToken(Text renewer)
+      throws IOException {
+    throw new IOException("Method not supported");
+  }
+
+  /**
+   * Renew an existing delegation token. Not supported for RestClient.
+   *
+   * @param token delegation token obtained earlier
+   * @return the new expiration time
+   * @throws IOException
+   */
+  @Override
+  public long renewDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    throw new IOException("Method not supported");
+  }
+
+  /**
+   * Cancel an existing delegation token. Not supported for RestClient.
+   *
+   * @param token delegation token
+   * @throws IOException
+   */
+  @Override
+  public void cancelDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    throw new IOException("Method not supported");
+  }
+
   @Override
   public OzoneInputStream getKey(
       String volumeName, String bucketName, String keyName)

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index 1b70f3b..3575f01 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -67,7 +67,10 @@ import org.apache.hadoop.hdds.scm.protocolPB
     .StorageContainerLocationProtocolClientSideTranslatorPB;
 import org.apache.hadoop.hdds.scm.protocolPB
     .StorageContainerLocationProtocolPB;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.io.Text;
 import org.apache.logging.log4j.util.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -404,6 +407,44 @@ public class RpcClient implements ClientProtocol {
     ozoneManagerClient.setBucketProperty(builder.build());
   }
 
+  /**
+   * Get a valid Delegation Token.
+   *
+   * @param renewer the designated renewer for the token
+   * @return Token<OzoneDelegationTokenSelector>
+   * @throws IOException
+   */
+  @Override
+  public Token<OzoneTokenIdentifier> getDelegationToken(Text renewer)
+      throws IOException {
+    return ozoneManagerClient.getDelegationToken(renewer);
+  }
+
+  /**
+   * Renew an existing delegation token.
+   *
+   * @param token delegation token obtained earlier
+   * @return the new expiration time
+   * @throws IOException
+   */
+  @Override
+  public long renewDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    return ozoneManagerClient.renewDelegationToken(token);
+  }
+
+  /**
+   * Cancel an existing delegation token.
+   *
+   * @param token delegation token
+   * @throws IOException
+   */
+  @Override
+  public void cancelDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    ozoneManagerClient.cancelDelegationToken(token);
+  }
+
   @Override
   public void setBucketVersioning(
       String volumeName, String bucketName, Boolean versioning)

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/TestHddsClientUtils.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/TestHddsClientUtils.java
 
b/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/TestHddsClientUtils.java
index 9e60e4e..ff4aeb3 100644
--- 
a/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/TestHddsClientUtils.java
+++ 
b/hadoop-ozone/client/src/test/java/org/apache/hadoop/ozone/client/TestHddsClientUtils.java
@@ -84,7 +84,7 @@ public class TestHddsClientUtils {
   }
 
   @Test
-  public void testGetOmAddress() {
+  public void testgetOmSocketAddress() {
     final Configuration conf = new OzoneConfiguration();
 
     // First try a client address with just a host name. Verify it falls

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 9280648..e7ed84f 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -54,14 +54,21 @@ public final class OmUtils {
    * @param conf
    * @return Target InetSocketAddress for the SCM service endpoint.
    */
-  public static InetSocketAddress getOmAddress(
-      Configuration conf) {
+  public static InetSocketAddress getOmAddress(Configuration conf) {
+    return NetUtils.createSocketAddr(getOmRpcAddress(conf));
+  }
+
+  /**
+   * Retrieve the socket address that is used by OM.
+   * @param conf
+   * @return Target InetSocketAddress for the SCM service endpoint.
+   */
+  public static String getOmRpcAddress(Configuration conf) {
     final Optional<String> host = getHostNameFromConfigKeys(conf,
         OZONE_OM_ADDRESS_KEY);
 
-    return NetUtils.createSocketAddr(
-        host.orElse(OZONE_OM_BIND_HOST_DEFAULT) + ":" +
-            getOmRpcPort(conf));
+    return host.orElse(OZONE_OM_BIND_HOST_DEFAULT) + ":" +
+        getOmRpcPort(conf);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
index 303ebd9..de24184 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/OMConfigKeys.java
@@ -95,4 +95,17 @@ public final class OMConfigKeys {
       "ozone.om.http.kerberos.keytab.file";
   public static final String OZONE_OM_HTTP_KERBEROS_PRINCIPAL_KEY
       = "ozone.om.http.kerberos.principal";
+  // Delegation token related keys
+  public static final String  DELEGATION_REMOVER_SCAN_INTERVAL_KEY =
+      "ozone.manager.delegation.remover.scan.interval";
+  public static final long    DELEGATION_REMOVER_SCAN_INTERVAL_DEFAULT =
+      60*60*1000;
+  public static final String  DELEGATION_TOKEN_RENEW_INTERVAL_KEY =
+      "ozone.manager.delegation.token.renew-interval";
+  public static final long    DELEGATION_TOKEN_RENEW_INTERVAL_DEFAULT =
+      24*60*60*1000;  // 1 day = 86400000 ms
+  public static final String  DELEGATION_TOKEN_MAX_LIFETIME_KEY =
+      "ozone.manager.delegation.token.max-lifetime";
+  public static final long    DELEGATION_TOKEN_MAX_LIFETIME_DEFAULT =
+      7*24*60*60*1000; // 7 days
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index 233666a..06933d2 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -37,7 +37,7 @@ import org.apache.hadoop.security.KerberosInfo;
  */
 @KerberosInfo(
     serverPrincipal = OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY)
-public interface OzoneManagerProtocol {
+public interface OzoneManagerProtocol  extends OzoneManagerSecurityProtocol {
 
   /**
    * Creates a volume.

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java
new file mode 100644
index 0000000..15873d0
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerSecurityProtocol.java
@@ -0,0 +1,67 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.om.protocol;
+
+import java.io.IOException;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.retry.Idempotent;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
+import org.apache.hadoop.security.KerberosInfo;
+import org.apache.hadoop.security.token.Token;
+
+/**
+ * Security protocol for a secure OzoneManager.
+ */
+@KerberosInfo(
+    serverPrincipal = OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY)
+public interface OzoneManagerSecurityProtocol {
+
+  /**
+   * Get a valid Delegation Token.
+   *
+   * @param renewer the designated renewer for the token
+   * @return Token<OzoneDelegationTokenSelector>
+   * @throws IOException
+   */
+  @Idempotent
+  Token<OzoneTokenIdentifier> getDelegationToken(Text renewer)
+      throws IOException;
+
+  /**
+   * Renew an existing delegation token.
+   *
+   * @param token delegation token obtained earlier
+   * @return the new expiration time
+   * @throws IOException
+   */
+  @Idempotent
+  long renewDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException;
+
+  /**
+   * Cancel an existing delegation token.
+   *
+   * @param token delegation token
+   * @throws IOException
+   */
+  @Idempotent
+  void cancelDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 6c8c932..85ae4a8 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -23,6 +23,7 @@ import com.google.common.collect.Lists;
 import com.google.protobuf.RpcController;
 import com.google.protobuf.ServiceException;
 import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ipc.ProtobufHelper;
 import org.apache.hadoop.ipc.ProtocolTranslator;
 import org.apache.hadoop.ozone.om.helpers.OmBucketArgs;
@@ -139,6 +140,13 @@ import java.io.IOException;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.stream.Collectors;
+import org.apache.hadoop.ozone.protocolPB.OMPBHelper;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
+import 
org.apache.hadoop.security.proto.SecurityProtos.CancelDelegationTokenRequestProto;
+import 
org.apache.hadoop.security.proto.SecurityProtos.GetDelegationTokenRequestProto;
+import 
org.apache.hadoop.security.proto.SecurityProtos.GetDelegationTokenResponseProto;
+import 
org.apache.hadoop.security.proto.SecurityProtos.RenewDelegationTokenRequestProto;
+import org.apache.hadoop.security.token.Token;
 
 /**
  *  The client side implementation of OzoneManagerProtocol.
@@ -884,4 +892,73 @@ public final class 
OzoneManagerProtocolClientSideTranslatorPB
   public Object getUnderlyingProxyObject() {
     return null;
   }
+
+  /**
+   * Get a valid Delegation Token.
+   *
+   * @param renewer the designated renewer for the token
+   * @return Token<OzoneDelegationTokenSelector>
+   * @throws IOException
+   */
+  @Override
+  public Token<OzoneTokenIdentifier> getDelegationToken(Text renewer)
+      throws IOException {
+    GetDelegationTokenRequestProto req = GetDelegationTokenRequestProto
+        .newBuilder()
+        .setRenewer(renewer == null ? "" : renewer.toString())
+        .build();
+    try {
+      GetDelegationTokenResponseProto resp =
+          rpcProxy.getDelegationToken(NULL_RPC_CONTROLLER, req);
+      return resp.hasToken() ?
+          OMPBHelper.convertToDelegationToken(resp.getToken()) : null;
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    } catch (Exception e){
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  /**
+   * Renew an existing delegation token.
+   *
+   * @param token delegation token obtained earlier
+   * @return the new expiration time
+   * @throws IOException
+   */
+  @Override
+  public long renewDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    RenewDelegationTokenRequestProto req =
+        RenewDelegationTokenRequestProto.newBuilder().
+            setToken(OMPBHelper.convertToTokenProto(token)).
+            build();
+    try {
+      return rpcProxy.renewDelegationToken(NULL_RPC_CONTROLLER, req)
+          .getNewExpiryTime();
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    }
+  }
+
+  /**
+   * Cancel an existing delegation token.
+   *
+   * @param token delegation token
+   * @throws IOException
+   */
+  @Override
+  public void cancelDelegationToken(Token<OzoneTokenIdentifier> token)
+      throws IOException {
+    CancelDelegationTokenRequestProto req = CancelDelegationTokenRequestProto
+        .newBuilder()
+        .setToken(OMPBHelper.convertToTokenProto(token))
+        .build();
+    try {
+      rpcProxy.cancelDelegationToken(NULL_RPC_CONTROLLER, req);
+    } catch (ServiceException e) {
+      throw ProtobufHelper.getRemoteException(e);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolPB.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolPB.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolPB.java
index 27e8f22..175527b 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolPB.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolPB.java
@@ -23,6 +23,8 @@ import org.apache.hadoop.ipc.ProtocolInfo;
 import org.apache.hadoop.ozone.protocol.proto
     .OzoneManagerProtocolProtos.OzoneManagerService;
 import org.apache.hadoop.security.KerberosInfo;
+import org.apache.hadoop.security.token.TokenInfo;
+import org.apache.hadoop.ozone.security.OzoneDelegationTokenSelector;
 
 /**
  * Protocol used to communicate with OM.
@@ -32,6 +34,7 @@ import org.apache.hadoop.security.KerberosInfo;
     protocolVersion = 1)
 @KerberosInfo(
     serverPrincipal = OMConfigKeys.OZONE_OM_KERBEROS_PRINCIPAL_KEY)
+@TokenInfo(OzoneDelegationTokenSelector.class)
 @InterfaceAudience.Private
 public interface OzoneManagerProtocolPB
     extends OzoneManagerService.BlockingInterface {

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/protocolPB/OMPBHelper.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/protocolPB/OMPBHelper.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/protocolPB/OMPBHelper.java
index d57d32e..df069ce 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/protocolPB/OMPBHelper.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/protocolPB/OMPBHelper.java
@@ -17,6 +17,8 @@
  */
 package org.apache.hadoop.ozone.protocolPB;
 
+import com.google.protobuf.ByteString;
+import org.apache.hadoop.io.Text;
 import org.apache.hadoop.ozone.OzoneAcl;
 import org.apache.hadoop.ozone.protocol.proto
     .OzoneManagerProtocolProtos.OzoneAclInfo;
@@ -24,6 +26,9 @@ import org.apache.hadoop.ozone.protocol.proto
     .OzoneManagerProtocolProtos.OzoneAclInfo.OzoneAclType;
 import org.apache.hadoop.ozone.protocol.proto
     .OzoneManagerProtocolProtos.OzoneAclInfo.OzoneAclRights;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
+import org.apache.hadoop.security.proto.SecurityProtos.TokenProto;
+import org.apache.hadoop.security.token.Token;
 
 /**
  * Utilities for converting protobuf classes.
@@ -110,4 +115,37 @@ public final class OMPBHelper {
 
     return new OzoneAcl(aclType, aclInfo.getName(), aclRights);
   }
+
+  /**
+   * Converts Ozone delegation token to @{@link TokenProto}.
+   * @return tokenProto
+   */
+  public static TokenProto convertToTokenProto(Token<?> tok) {
+    if(tok == null){
+      throw new IllegalArgumentException("Invalid argument: token is null");
+    }
+
+    return TokenProto.newBuilder().
+        setIdentifier(getByteString(tok.getIdentifier())).
+        setPassword(getByteString(tok.getPassword())).
+        setKind(tok.getKind().toString()).
+        setService(tok.getService().toString()).build();
+  }
+
+  public static ByteString getByteString(byte[] bytes) {
+    // return singleton to reduce object allocation
+    return (bytes.length == 0) ? ByteString.EMPTY : ByteString.copyFrom(bytes);
+  }
+
+  /**
+   * Converts @{@link TokenProto} to Ozone delegation token.
+   *
+   * @return Ozone
+   */
+  public static Token<OzoneTokenIdentifier> convertToDelegationToken(
+      TokenProto tokenProto) {
+    return new Token<>(tokenProto.getIdentifier()
+        .toByteArray(), tokenProto.getPassword().toByteArray(), new Text(
+        tokenProto.getKind()), new Text(tokenProto.getService()));
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSelector.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSelector.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSelector.java
index 0d63c4e..8e35f22 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSelector.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneDelegationTokenSelector.java
@@ -19,7 +19,6 @@ package org.apache.hadoop.ozone.security;
 
 import java.util.Collection;
 import org.apache.hadoop.classification.InterfaceAudience;
-import 
org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.security.token.TokenIdentifier;
@@ -32,7 +31,7 @@ import org.slf4j.LoggerFactory;
  */
 @InterfaceAudience.Private
 public class OzoneDelegationTokenSelector
-    extends AbstractDelegationTokenSelector<DelegationTokenIdentifier> {
+    extends AbstractDelegationTokenSelector<OzoneTokenIdentifier> {
 
   public OzoneDelegationTokenSelector() {
     super(OzoneTokenIdentifier.KIND_NAME);

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
new file mode 100644
index 0000000..0c84404
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretManager.java
@@ -0,0 +1,598 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.security;
+
+import com.google.common.base.Preconditions;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.ozone.OzoneConfigKeys;
+import 
org.apache.hadoop.ozone.security.OzoneSecretStore.OzoneManagerSecretState;
+import org.apache.hadoop.ozone.security.OzoneTokenIdentifier.TokenInfo;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.HadoopKerberosName;
+import org.apache.hadoop.security.token.SecretManager;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.Daemon;
+import org.apache.hadoop.util.Time;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * SecretManager for Ozone Master. Responsible for signing identifiers with
+ * private key,
+ */
+@InterfaceAudience.Private
+@InterfaceStability.Unstable
+public class OzoneSecretManager<T extends OzoneTokenIdentifier>
+    extends SecretManager<T> {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(OzoneSecretManager.class);
+  /**
+   * The name of the Private/Public Key based hashing algorithm.
+   */
+  private static final String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withRSA";
+  private final long tokenMaxLifetime;
+  private final long tokenRenewInterval;
+  private final long tokenRemoverScanInterval;
+  private final Text service;
+  private final Map<Integer, OzoneSecretKey> allKeys;
+  private final Map<T, TokenInfo> currentTokens;
+  private final OzoneSecretStore store;
+  private Thread tokenRemoverThread;
+  private volatile boolean running;
+  private AtomicInteger tokenSequenceNumber;
+  private OzoneSecretKey currentKey;
+  private AtomicInteger currentKeyId;
+  /**
+   * If the delegation token update thread holds this lock, it will not get
+   * interrupted.
+   */
+  private Object noInterruptsLock = new Object();
+  private int maxKeyLength;
+
+  /**
+   * Create a secret manager.
+   *
+   * @param conf configuration.
+   * @param tokenMaxLifetime the maximum lifetime of the delegation tokens in
+   * milliseconds
+   * @param tokenRenewInterval how often the tokens must be renewed in
+   * milliseconds
+   * @param dtRemoverScanInterval how often the tokens are scanned for expired
+   * tokens in milliseconds
+   */
+  public OzoneSecretManager(OzoneConfiguration conf, long tokenMaxLifetime,
+      long tokenRenewInterval, long dtRemoverScanInterval, Text service)
+      throws IOException {
+    this.tokenMaxLifetime = tokenMaxLifetime;
+    this.tokenRenewInterval = tokenRenewInterval;
+    this.tokenRemoverScanInterval = dtRemoverScanInterval;
+
+    currentTokens = new ConcurrentHashMap();
+    allKeys = new ConcurrentHashMap<>();
+    currentKeyId = new AtomicInteger();
+    tokenSequenceNumber = new AtomicInteger();
+    this.store = new OzoneSecretStore(conf);
+    loadTokenSecretState(store.loadState());
+    this.service = service;
+    this.maxKeyLength = conf.getInt(OzoneConfigKeys.OZONE_MAX_KEY_LEN,
+        OzoneConfigKeys.OZONE_MAX_KEY_LEN_DEFAULT);
+  }
+
+  @Override
+  public T createIdentifier() {
+    return (T) T.newInstance();
+  }
+
+  /**
+   * Create new Identifier with given,owner,renwer and realUser.
+   *
+   * @return T
+   */
+  public T createIdentifier(Text owner, Text renewer, Text realUser) {
+    return (T) T.newInstance(owner, renewer, realUser);
+  }
+
+  /**
+   * Returns {@link Token} for given identifier.
+   *
+   * @param owner
+   * @param renewer
+   * @param realUser
+   * @return Token
+   * @throws IOException to allow future exceptions to be added without 
breaking
+   *                     compatibility
+   */
+  public Token<T> createToken(Text owner, Text renewer, Text realUser)
+      throws IOException {
+    T identifier = createIdentifier(owner, renewer, realUser);
+    updateIdentifierDetails(identifier);
+
+    byte[] password = createPassword(identifier.getBytes(),
+        currentKey.getPrivateKey());
+    addToTokenStore(identifier, password);
+    Token<T> token = new Token<>(identifier.getBytes(), password,
+        identifier.getKind(), service);
+    if (LOG.isTraceEnabled()) {
+      long expiryTime = identifier.getIssueDate() + tokenRenewInterval;
+      String tokenId = identifier.toStringStable();
+      LOG.trace("Issued delegation token -> expiryTime:{},tokenId:{}",
+          expiryTime, tokenId);
+    }
+
+    return token;
+  }
+
+  /**
+   * Stores given identifier in token store.
+   *
+   * @param identifier
+   * @param password
+   * @throws IOException
+   */
+  private void addToTokenStore(T identifier, byte[] password)
+      throws IOException {
+    TokenInfo tokenInfo = new TokenInfo(identifier.getIssueDate()
+        + tokenRenewInterval, password, identifier.getTrackingId());
+    currentTokens.put(identifier, tokenInfo);
+    store.storeToken(identifier, tokenInfo.getRenewDate());
+  }
+
+  /**
+   * Updates issue date, master key id and sequence number for identifier.
+   *
+   * @param identifier the identifier to validate
+   */
+  private void updateIdentifierDetails(T identifier) {
+    int sequenceNum;
+    long now = Time.monotonicNow();
+    sequenceNum = incrementDelegationTokenSeqNum();
+    identifier.setIssueDate(now);
+    identifier.setMasterKeyId(currentKey.getKeyId());
+    identifier.setSequenceNumber(sequenceNum);
+    identifier.setMaxDate(Time.monotonicNow() + tokenMaxLifetime);
+  }
+
+  /**
+   * Compute HMAC of the identifier using the private key and return the output
+   * as password.
+   *
+   * @param identifier
+   * @param privateKey
+   * @return byte[] signed byte array
+   */
+  public byte[] createPassword(byte[] identifier, PrivateKey privateKey)
+      throws OzoneSecurityException {
+    try {
+      Signature rsaSignature = Signature.getInstance(
+          DEFAULT_SIGNATURE_ALGORITHM);
+      rsaSignature.initSign(privateKey);
+      rsaSignature.update(identifier);
+      return rsaSignature.sign();
+    } catch (InvalidKeyException | NoSuchAlgorithmException |
+        SignatureException ex) {
+      throw new OzoneSecurityException("Error while creating HMAC hash for " +
+          "token.", ex, OzoneSecurityException.ResultCodes
+          .SECRET_MANAGER_HMAC_ERROR);
+    }
+  }
+
+  @Override
+  public byte[] createPassword(T identifier) {
+    LOG.debug("Creating password for identifier: {}, currentKey: {}",
+        formatTokenId(identifier), currentKey.getKeyId());
+    byte[] password = null;
+    try {
+      password = createPassword(identifier.getBytes(),
+          currentKey.getPrivateKey());
+    } catch (IOException ioe) {
+      LOG.error("Could not store token {}!!", formatTokenId(identifier),
+          ioe);
+    }
+    return password;
+  }
+
+  @Override
+  public byte[] retrievePassword(T identifier) throws InvalidToken {
+    return checkToken(identifier).getPassword();
+  }
+
+  /**
+   * Renew a delegation token.
+   *
+   * @param token the token to renew
+   * @param renewer the full principal name of the user doing the renewal
+   * @return the new expiration time
+   * @throws InvalidToken           if the token is invalid
+   * @throws AccessControlException if the user can't renew token
+   */
+  public synchronized long renewToken(Token<T> token, String renewer)
+      throws IOException {
+    ByteArrayInputStream buf = new ByteArrayInputStream(token.getIdentifier());
+    DataInputStream in = new DataInputStream(buf);
+    T id = (T) T.readProtoBuf(in);
+    LOG.debug("Token renewal for identifier: {}, total currentTokens: {}",
+        formatTokenId(id), currentTokens.size());
+
+    long now = Time.monotonicNow();
+    if (id.getMaxDate() < now) {
+      throw new InvalidToken(renewer + " tried to renew an expired token "
+          + formatTokenId(id) + " max expiration date: "
+          + Time.formatTime(id.getMaxDate())
+          + " currentTime: " + Time.formatTime(now));
+    }
+    checkToken(id);
+    if ((id.getRenewer() == null) || (id.getRenewer().toString().isEmpty())) {
+      throw new AccessControlException(renewer +
+          " tried to renew a token " + formatTokenId(id)
+          + " without a renewer");
+    }
+    if (!id.getRenewer().toString().equals(renewer)) {
+      throw new AccessControlException(renewer
+          + " tries to renew a token " + formatTokenId(id)
+          + " with non-matching renewer " + id.getRenewer());
+    }
+    OzoneSecretKey key = allKeys.get(id.getMasterKeyId());
+    if (key == null) {
+      throw new InvalidToken("Unable to find master key for keyId="
+          + id.getMasterKeyId()
+          + " from cache. Failed to renew an unexpired token "
+          + formatTokenId(id) + " with sequenceNumber="
+          + id.getSequenceNumber());
+    }
+    byte[] password = createPassword(token.getIdentifier(),
+        key.getPrivateKey());
+
+    long renewTime = Math.min(id.getMaxDate(), now + tokenRenewInterval);
+    try {
+      addToTokenStore(id, password);
+    } catch (IOException e) {
+      LOG.error("Unable to update token " + id.getSequenceNumber(), e);
+    }
+    return renewTime;
+  }
+
+  /**
+   * Cancel a token by removing it from store and cache.
+   *
+   * @return Identifier of the canceled token
+   * @throws InvalidToken           for invalid token
+   * @throws AccessControlException if the user isn't allowed to cancel
+   */
+  public T cancelToken(Token<T> token, String canceller) throws IOException {
+    T id = (T) T.readProtoBuf(token.getIdentifier());
+    LOG.debug("Token cancellation requested for identifier: {}",
+        formatTokenId(id));
+
+    if (id.getUser() == null) {
+      throw new InvalidToken("Token with no owner " + formatTokenId(id));
+    }
+    String owner = id.getUser().getUserName();
+    Text renewer = id.getRenewer();
+    HadoopKerberosName cancelerKrbName = new HadoopKerberosName(canceller);
+    String cancelerShortName = cancelerKrbName.getShortName();
+    if (!canceller.equals(owner)
+        && (renewer == null || renewer.toString().isEmpty()
+        || !cancelerShortName
+        .equals(renewer.toString()))) {
+      throw new AccessControlException(canceller
+          + " is not authorized to cancel the token " + formatTokenId(id));
+    }
+    try {
+      store.removeToken(id);
+    } catch (IOException e) {
+      LOG.error("Unable to remove token " + id.getSequenceNumber(), e);
+    }
+    TokenInfo info = currentTokens.remove(id);
+    if (info == null) {
+      throw new InvalidToken("Token not found " + formatTokenId(id));
+    }
+    return id;
+  }
+
+  public int getCurrentKeyId() {
+    return currentKeyId.get();
+  }
+
+  public void setCurrentKeyId(int keyId) {
+    currentKeyId.set(keyId);
+  }
+
+  public int incrementCurrentKeyId() {
+    return currentKeyId.incrementAndGet();
+  }
+
+  public int getDelegationTokenSeqNum() {
+    return tokenSequenceNumber.get();
+  }
+
+  public void setDelegationTokenSeqNum(int seqNum) {
+    tokenSequenceNumber.set(seqNum);
+  }
+
+  public int incrementDelegationTokenSeqNum() {
+    return tokenSequenceNumber.incrementAndGet();
+  }
+
+  /**
+   * Validates if given token is valid.
+   *
+   * @param identifier
+   * @param password
+   */
+  private boolean validateToken(T identifier, byte[] password) {
+    try {
+      Signature rsaSignature = Signature.getInstance("SHA256withRSA");
+      rsaSignature.initVerify(currentKey.getPublicKey());
+      rsaSignature.update(identifier.getBytes());
+      return rsaSignature.verify(password);
+    } catch (NoSuchAlgorithmException | SignatureException |
+        InvalidKeyException e) {
+      return false;
+    }
+  }
+
+  /**
+   * Checks if TokenInfo for the given identifier exists in database and if the
+   * token is expired.
+   */
+  public TokenInfo checkToken(T identifier) throws InvalidToken {
+    TokenInfo info = currentTokens.get(identifier);
+    if (info == null) {
+      throw new InvalidToken("token " + formatTokenId(identifier)
+          + " can't be found in cache");
+    }
+    long now = Time.monotonicNow();
+    if (info.getRenewDate() < now) {
+      throw new InvalidToken("token " + formatTokenId(identifier) + " is " +
+          "expired, current time: " + Time.formatTime(now) +
+          " expected renewal time: " + Time.formatTime(info.getRenewDate()));
+    }
+    if (!validateToken(identifier, info.getPassword())) {
+      throw new InvalidToken("Tampared/Inavalid token.");
+    }
+    return info;
+  }
+
+  // TODO: handle roll private key/certificate
+  private synchronized void removeExpiredKeys() {
+    long now = Time.monotonicNow();
+    for (Iterator<Map.Entry<Integer, OzoneSecretKey>> it = allKeys.entrySet()
+        .iterator(); it.hasNext();) {
+      Map.Entry<Integer, OzoneSecretKey> e = it.next();
+      OzoneSecretKey key = e.getValue();
+      if (key.getExpiryDate() < now && key.getExpiryDate() != -1) {
+        if (!key.equals(currentKey)) {
+          it.remove();
+          try {
+            store.removeTokenMasterKey(key);
+          } catch (IOException ex) {
+            LOG.error("Unable to remove master key " + key.getKeyId(), ex);
+          }
+        }
+      }
+    }
+  }
+
+  private void loadTokenSecretState(OzoneManagerSecretState<T> state)
+      throws IOException {
+    LOG.info("Loading token state into token manager.");
+    for (OzoneSecretKey key : state.ozoneManagerSecretState()) {
+      allKeys.putIfAbsent(key.getKeyId(), key);
+    }
+    for (Map.Entry<T, Long> entry : state.getTokenState().entrySet()) {
+      addPersistedDelegationToken(entry.getKey(), entry.getValue());
+    }
+  }
+
+  private String formatTokenId(T id) {
+    return "(" + id + ")";
+  }
+
+  private void addPersistedDelegationToken(
+      T identifier, long renewDate)
+      throws IOException {
+    if (running) {
+      // a safety check
+      throw new IOException(
+          "Can't add persisted delegation token to a running SecretManager.");
+    }
+    int keyId = identifier.getMasterKeyId();
+    OzoneSecretKey dKey = allKeys.get(keyId);
+    if (dKey == null) {
+      LOG.warn("No KEY found for persisted identifier "
+          + formatTokenId(identifier));
+      return;
+    }
+
+    PrivateKey privateKey = dKey.getPrivateKey();
+    byte[] password = createPassword(identifier.getBytes(), privateKey);
+    if (identifier.getSequenceNumber() > getDelegationTokenSeqNum()) {
+      setDelegationTokenSeqNum(identifier.getSequenceNumber());
+    }
+    if (currentTokens.get(identifier) == null) {
+      currentTokens.put(identifier, new TokenInfo(renewDate,
+          password, identifier.getTrackingId()));
+    } else {
+      throw new IOException("Same delegation token being added twice: "
+          + formatTokenId(identifier));
+    }
+  }
+
+  /**
+   * Should be called before this object is used.
+   */
+  public void startThreads(KeyPair keyPair) throws IOException {
+    Preconditions.checkState(!running);
+    updateCurrentKey(keyPair);
+    removeExpiredKeys();
+    synchronized (this) {
+      running = true;
+      tokenRemoverThread = new Daemon(new ExpiredTokenRemover());
+      tokenRemoverThread.start();
+    }
+  }
+
+  public void stopThreads() {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Stopping expired delegation token remover thread");
+    }
+    running = false;
+
+    if (tokenRemoverThread != null) {
+      synchronized (noInterruptsLock) {
+        tokenRemoverThread.interrupt();
+      }
+      try {
+        tokenRemoverThread.join();
+      } catch (InterruptedException e) {
+        throw new RuntimeException(
+            "Unable to join on token removal thread", e);
+      }
+    }
+  }
+
+  /**
+   * Stops the OzoneSecretManager.
+   *
+   * @throws IOException
+   */
+  public void stop() throws IOException {
+    stopThreads();
+    if (this.store != null) {
+      this.store.close();
+    }
+  }
+
+  /**
+   * Update the current master key. This is called once by startThreads before
+   * tokenRemoverThread is created,
+   */
+  private void updateCurrentKey(KeyPair keyPair) throws IOException {
+    LOG.info("Updating the current master key for generating tokens");
+
+    // TODO: fix me based on the certificate expire time to set the key
+    // expire time.
+    int newCurrentId = incrementCurrentKeyId();
+    OzoneSecretKey newKey = new OzoneSecretKey(newCurrentId, -1,
+        keyPair, maxKeyLength);
+
+    store.storeTokenMasterKey(newKey);
+    if (!allKeys.containsKey(newKey.getKeyId())) {
+      allKeys.put(newKey.getKeyId(), newKey);
+    }
+
+    synchronized (this) {
+      currentKey = newKey;
+    }
+  }
+
+  /**
+   * Remove expired delegation tokens from cache and persisted store.
+   */
+  private void removeExpiredToken() throws IOException {
+    long now = Time.monotonicNow();
+    synchronized (this) {
+      Iterator<Map.Entry<T,
+          TokenInfo>> i = currentTokens.entrySet().iterator();
+      while (i.hasNext()) {
+        Map.Entry<T,
+            TokenInfo> entry = i.next();
+        long renewDate = entry.getValue().getRenewDate();
+        if (renewDate < now) {
+          i.remove();
+          store.removeToken(entry.getKey());
+        }
+      }
+    }
+  }
+
+  /**
+   * Is Secret Manager running.
+   *
+   * @return true if secret mgr is running
+   */
+  public synchronized boolean isRunning() {
+    return running;
+  }
+
+  /**
+   * Returns expiry time of a token given its identifier.
+   *
+   * @param dtId DelegationTokenIdentifier of a token
+   * @return Expiry time of the token
+   * @throws IOException
+   */
+  public long getTokenExpiryTime(T dtId)
+      throws IOException {
+    TokenInfo info = currentTokens.get(dtId);
+    if (info != null) {
+      return info.getRenewDate();
+    } else {
+      throw new IOException("No delegation token found for this identifier");
+    }
+  }
+
+  private class ExpiredTokenRemover extends Thread {
+    private long lastTokenCacheCleanup;
+
+    @Override
+    public void run() {
+      LOG.info("Starting expired delegation token remover thread, "
+          + "tokenRemoverScanInterval=" + tokenRemoverScanInterval
+          / (60 * 1000) + " min(s)");
+      try {
+        while (running) {
+          long now = Time.monotonicNow();
+          if (lastTokenCacheCleanup + tokenRemoverScanInterval
+              < now) {
+            removeExpiredToken();
+            lastTokenCacheCleanup = now;
+          }
+          try {
+            Thread.sleep(Math.min(5000,
+                tokenRemoverScanInterval)); // 5 seconds
+          } catch (InterruptedException ie) {
+            LOG.error("ExpiredTokenRemover received " + ie);
+          }
+        }
+      } catch (Throwable t) {
+        LOG.error("ExpiredTokenRemover thread received unexpected exception",
+            t);
+        Runtime.getRuntime().exit(-1);
+      }
+    }
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretStore.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretStore.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretStore.java
new file mode 100644
index 0000000..6528bcf
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecretStore.java
@@ -0,0 +1,250 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.security;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdfs.DFSUtil;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.utils.MetadataKeyFilters;
+import org.apache.hadoop.utils.MetadataStore;
+import org.apache.hadoop.utils.MetadataStoreBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.hadoop.hdds.server.ServerUtils.getOzoneMetaDirPath;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_MANAGER_TOKEN_DB_NAME;
+import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_CACHE_SIZE_DEFAULT;
+import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DB_CACHE_SIZE_MB;
+
+/**
+ * SecretStore for Ozone Master.
+ */
+public class OzoneSecretStore<T extends OzoneTokenIdentifier>
+    implements Closeable {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(OzoneSecretStore.class);
+  private static final String TOKEN_MASTER_KEY_KEY_PREFIX = "tokens/key_";
+  private static final String TOKEN_STATE_KEY_PREFIX = "tokens/token_";
+
+  @Override
+  public void close() throws IOException {
+    if (store != null) {
+      store.close();
+    }
+  }
+
+
+  /**
+   * Support class to maintain state of OzoneSecretStore.
+   */
+  public static class OzoneManagerSecretState<T> {
+
+    private Map<T, Long> tokenState = new HashMap<>();
+    private Set<OzoneSecretKey> tokenMasterKeyState = new HashSet<>();
+
+    public Map<T, Long> getTokenState() {
+      return tokenState;
+    }
+
+    public Set<OzoneSecretKey> ozoneManagerSecretState() {
+      return tokenMasterKeyState;
+    }
+  }
+
+  private MetadataStore store;
+
+  public OzoneSecretStore(OzoneConfiguration conf)
+      throws IOException {
+    File metaDir = getOzoneMetaDirPath(conf);
+    final int cacheSize = conf.getInt(OZONE_OM_DB_CACHE_SIZE_MB,
+        OZONE_OM_DB_CACHE_SIZE_DEFAULT);
+    File omTokenDBFile = new File(metaDir.getPath(),
+        OZONE_MANAGER_TOKEN_DB_NAME);
+    this.store = MetadataStoreBuilder.newBuilder()
+        .setConf(conf)
+        .setDbFile(omTokenDBFile)
+        .setCacheSize(cacheSize * OzoneConsts.MB)
+        .build();
+  }
+
+  public OzoneManagerSecretState loadState() throws IOException {
+    OzoneManagerSecretState state = new OzoneManagerSecretState();
+    int numKeys = loadMasterKeys(state);
+    LOG.info("Loaded " + numKeys + " token master keys");
+    int numTokens = loadTokens(state);
+    LOG.info("Loaded " + numTokens + " tokens");
+    return state;
+  }
+
+  public void storeTokenMasterKey(OzoneSecretKey key) throws IOException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Storing master key " + key.getKeyId());
+    }
+    ByteArrayOutputStream memStream = new ByteArrayOutputStream();
+    DataOutputStream dataStream = new DataOutputStream(memStream);
+    try {
+      key.write(dataStream);
+      dataStream.close();
+      dataStream = null;
+    } finally {
+      IOUtils.cleanupWithLogger(LOG, dataStream);
+    }
+    try {
+      byte[] dbKey = getMasterKeyDBKey(key);
+      store.put(dbKey, memStream.toByteArray());
+    } catch (IOException e) {
+      LOG.error("Unable to store master key " + key.getKeyId(), e);
+      throw e;
+    }
+  }
+
+
+  public void removeTokenMasterKey(OzoneSecretKey key)
+      throws IOException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Removing master key " + key.getKeyId());
+    }
+
+    byte[] dbKey = getMasterKeyDBKey(key);
+    try {
+      store.delete(dbKey);
+    } catch (IOException e) {
+      LOG.error("Unable to delete master key " + key.getKeyId(), e);
+      throw e;
+    }
+  }
+
+  public void storeToken(T tokenId, Long renewDate)
+      throws IOException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Storing token " + tokenId.getSequenceNumber());
+    }
+
+    ByteArrayOutputStream memStream = new ByteArrayOutputStream();
+    DataOutputStream dataStream = new DataOutputStream(memStream);
+    try {
+      tokenId.write(dataStream);
+      dataStream.writeLong(renewDate);
+      dataStream.close();
+      dataStream = null;
+    } finally {
+      IOUtils.cleanupWithLogger(LOG, dataStream);
+    }
+
+    byte[] dbKey = getTokenDBKey(tokenId);
+    try {
+      store.put(dbKey, memStream.toByteArray());
+    } catch (IOException e) {
+      LOG.error("Unable to store token " + tokenId.toString(), e);
+      throw e;
+    }
+  }
+
+  public void updateToken(T tokenId, Long renewDate)
+      throws IOException {
+    storeToken(tokenId, renewDate);
+  }
+
+  public void removeToken(T tokenId)
+      throws IOException {
+    byte[] dbKey = getTokenDBKey(tokenId);
+    try {
+      store.delete(dbKey);
+    } catch (IOException e) {
+      LOG.error("Unable to remove token " + tokenId.toString(), e);
+      throw e;
+    }
+  }
+
+  public int loadMasterKeys(OzoneManagerSecretState state) throws IOException {
+    MetadataKeyFilters.MetadataKeyFilter filter =
+        (preKey, currentKey, nextKey) -> DFSUtil.bytes2String(currentKey)
+            .startsWith(TOKEN_MASTER_KEY_KEY_PREFIX);
+    List<Map.Entry<byte[], byte[]>> kvs = store
+        .getRangeKVs(null, Integer.MAX_VALUE, filter);
+    kvs.forEach(entry -> {
+      try {
+        loadTokenMasterKey(state, entry.getValue());
+      } catch (IOException e) {
+        LOG.warn("Failed to load master key ",
+            DFSUtil.bytes2String(entry.getKey()), e);
+      }
+    });
+    return kvs.size();
+  }
+
+  private void loadTokenMasterKey(OzoneManagerSecretState state, byte[] data)
+      throws IOException {
+    OzoneSecretKey key = OzoneSecretKey.readProtoBuf(data);
+    state.tokenMasterKeyState.add(key);
+  }
+
+  public int loadTokens(OzoneManagerSecretState state) throws IOException {
+    MetadataKeyFilters.MetadataKeyFilter filter =
+        (preKey, currentKey, nextKey) -> DFSUtil.bytes2String(currentKey)
+            .startsWith(TOKEN_STATE_KEY_PREFIX);
+    List<Map.Entry<byte[], byte[]>> kvs =
+        store.getRangeKVs(null, Integer.MAX_VALUE, filter);
+    kvs.forEach(entry -> {
+      try {
+        loadToken(state, entry.getValue());
+      } catch (IOException e) {
+        LOG.warn("Failed to load token ",
+            DFSUtil.bytes2String(entry.getKey()), e);
+      }
+    });
+    return kvs.size();
+  }
+
+  private void loadToken(OzoneManagerSecretState state, byte[] data)
+      throws IOException {
+    long renewDate;
+    DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+    T tokenId = (T) T.readProtoBuf(in);
+    try {
+      tokenId.readFields(in);
+      renewDate = in.readLong();
+    } finally {
+      IOUtils.cleanupWithLogger(LOG, in);
+    }
+    state.tokenState.put(tokenId, renewDate);
+  }
+
+  private byte[] getMasterKeyDBKey(OzoneSecretKey masterKey) {
+    return DFSUtil.string2Bytes(
+        TOKEN_MASTER_KEY_KEY_PREFIX + masterKey.getKeyId());
+  }
+
+  private byte[] getTokenDBKey(T tokenId) {
+    return DFSUtil.string2Bytes(
+        TOKEN_STATE_KEY_PREFIX + tokenId.getSequenceNumber());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecurityException.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecurityException.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecurityException.java
new file mode 100644
index 0000000..66533f3
--- /dev/null
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/OzoneSecurityException.java
@@ -0,0 +1,104 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.ozone.security;
+
+import java.io.IOException;
+
+/**
+ * Security exceptions thrown at Ozone layer.
+ */
+public class OzoneSecurityException extends IOException {
+  private final OzoneSecurityException.ResultCodes result;
+
+  /**
+   * Constructs an {@code IOException} with {@code null}
+   * as its error detail message.
+   */
+  public OzoneSecurityException(OzoneSecurityException.ResultCodes result) {
+    this.result = result;
+  }
+
+  /**
+   * Constructs an {@code IOException} with the specified detail message.
+   *
+   * @param message The detail message (which is saved for later retrieval by
+   * the
+   * {@link #getMessage()} method)
+   */
+  public OzoneSecurityException(String message,
+      OzoneSecurityException.ResultCodes result) {
+    super(message);
+    this.result = result;
+  }
+
+  /**
+   * Constructs an {@code IOException} with the specified detail message
+   * and cause.
+   * <p>
+   * <p> Note that the detail message associated with {@code cause} is
+   * <i>not</i> automatically incorporated into this exception's detail
+   * message.
+   *
+   * @param message The detail message (which is saved for later retrieval by
+   * the
+   * {@link #getMessage()} method)
+   * @param cause The cause (which is saved for later retrieval by the {@link
+   * #getCause()} method).  (A null value is permitted, and indicates that the
+   * cause is nonexistent or unknown.)
+   * @since 1.6
+   */
+  public OzoneSecurityException(String message, Throwable cause,
+                     OzoneSecurityException.ResultCodes result) {
+    super(message, cause);
+    this.result = result;
+  }
+
+  /**
+   * Constructs an {@code IOException} with the specified cause and a
+   * detail message of {@code (cause==null ? null : cause.toString())}
+   * (which typically contains the class and detail message of {@code cause}).
+   * This constructor is useful for IO exceptions that are little more
+   * than wrappers for other throwables.
+   *
+   * @param cause The cause (which is saved for later retrieval by the {@link
+   * #getCause()} method).  (A null value is permitted, and indicates that the
+   * cause is nonexistent or unknown.)
+   * @since 1.6
+   */
+  public OzoneSecurityException(Throwable cause,
+      OzoneSecurityException.ResultCodes result) {
+    super(cause);
+    this.result = result;
+  }
+
+  /**
+   * Returns resultCode.
+   * @return ResultCode
+   */
+  public OzoneSecurityException.ResultCodes getResult() {
+    return result;
+  }
+
+  /**
+   * Error codes to make it easy to decode these exceptions.
+   */
+  public enum ResultCodes {
+    OM_PUBLIC_PRIVATE_KEY_FILE_NOT_EXIST,
+    SECRET_MANAGER_HMAC_ERROR
+  }
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
----------------------------------------------------------------------
diff --git a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto 
b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
index 5caebf5..9af8dd0 100644
--- a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
+++ b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
@@ -35,6 +35,7 @@ This is similar to Namenode for Ozone.
 */
 
 import "hdds.proto";
+import "Security.proto";
 
 enum Status {
     OK = 1;
@@ -544,6 +545,24 @@ service OzoneManagerService {
     rpc getServiceList(ServiceListRequest)
     returns(ServiceListResponse);
 
+
+    /**
+     Returns delegation token signed by OzoneManager.
+    */
+    rpc getDelegationToken(hadoop.common.GetDelegationTokenRequestProto)
+      returns(hadoop.common.GetDelegationTokenResponseProto);
+
+    /**
+     Renew delegation token signed by OzoneManager.
+    */
+    rpc renewDelegationToken(hadoop.common.RenewDelegationTokenRequestProto)
+    returns(hadoop.common.RenewDelegationTokenResponseProto);
+    /**
+     Cancels a delegation token signed by OzoneManager.
+    */
+    rpc cancelDelegationToken(hadoop.common.CancelDelegationTokenRequestProto)
+    returns(hadoop.common.CancelDelegationTokenResponseProto);
+
     /**
      Creates an S3 bucket and creates an ozone mapping for that bucket.
     */

http://git-wip-us.apache.org/repos/asf/hadoop/blob/59767be1/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
----------------------------------------------------------------------
diff --git 
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
new file mode 100644
index 0000000..e4a8f2b
--- /dev/null
+++ 
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/TestOzoneSecretManager.java
@@ -0,0 +1,216 @@
+/*
+ * 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.ozone.security;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.Signature;
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.hdds.HddsConfigKeys;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.security.x509.SecurityConfig;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
+import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.test.LambdaTestUtils;
+import org.apache.hadoop.util.Time;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link OzoneSecretManager}.
+ */
+public class TestOzoneSecretManager {
+
+  private OzoneSecretManager<OzoneTokenIdentifier> secretManager;
+  private SecurityConfig securityConfig;
+  private KeyPair keyPair;
+  private long expiryTime;
+  private Text serviceRpcAdd;
+  private OzoneConfiguration conf;
+  private static final String BASEDIR = GenericTestUtils
+      .getTempPath(TestOzoneSecretManager.class.getSimpleName());
+  private final static Text TEST_USER = new Text("testUser");
+  private long tokenMaxLifetime = 1000 * 20;
+  private long tokenRemoverScanInterval = 1000 * 20;
+
+  @Before
+  public void setUp() throws Exception {
+    conf = new OzoneConfiguration();
+    conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, BASEDIR);
+    securityConfig = new SecurityConfig(conf);
+    // Create Ozone Master key pair.
+    keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
+    expiryTime = Time.monotonicNow() + 60 * 60 * 24;
+    serviceRpcAdd = new Text("localhost");
+  }
+
+  @After
+  public void tearDown() throws IOException {
+    secretManager.stop();
+    FileUtils.deleteQuietly(new File(BASEDIR));
+  }
+
+  @Test
+  public void testCreateToken() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    OzoneTokenIdentifier identifier =
+        OzoneTokenIdentifier.readProtoBuf(token.getIdentifier());
+    // Check basic details.
+    Assert.assertTrue(identifier.getRealUser().equals(TEST_USER));
+    Assert.assertTrue(identifier.getRenewer().equals(TEST_USER));
+    Assert.assertTrue(identifier.getOwner().equals(TEST_USER));
+
+    validateHash(token.getPassword(), token.getIdentifier());
+  }
+
+  @Test
+  public void testRenewTokenSuccess() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(10 * 5);
+    long renewalTime = secretManager.renewToken(token, TEST_USER.toString());
+    Assert.assertTrue(renewalTime > 0);
+  }
+
+  /**
+   * Tests failure for mismatch in renewer.
+   */
+  @Test
+  public void testRenewTokenFailure() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    LambdaTestUtils.intercept(AccessControlException.class,
+        "rougeUser tries to renew a token", () -> {
+          secretManager.renewToken(token, "rougeUser");
+        });
+  }
+
+  /**
+   * Tests token renew failure due to max time.
+   */
+  @Test
+  public void testRenewTokenFailureMaxTime() throws Exception {
+    secretManager = createSecretManager(conf, 100,
+        100, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(101);
+    LambdaTestUtils.intercept(IOException.class,
+        "testUser tried to renew an expired token", () -> {
+          secretManager.renewToken(token, TEST_USER.toString());
+        });
+  }
+
+  /**
+   * Tests token renew failure due to renewal time.
+   */
+  @Test
+  public void testRenewTokenFailureRenewalTime() throws Exception {
+    secretManager = createSecretManager(conf, 1000 * 10,
+        10, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    Thread.sleep(15);
+    LambdaTestUtils.intercept(IOException.class, "is expired", () -> {
+      secretManager.renewToken(token, TEST_USER.toString());
+    });
+  }
+
+  @Test
+  public void testCreateIdentifier() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    OzoneTokenIdentifier identifier = secretManager.createIdentifier();
+    // Check basic details.
+    Assert.assertTrue(identifier.getOwner().equals(new Text("")));
+    Assert.assertTrue(identifier.getRealUser().equals(new Text("")));
+    Assert.assertTrue(identifier.getRenewer().equals(new Text("")));
+  }
+
+  @Test
+  public void testCancelTokenSuccess() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    secretManager.cancelToken(token, TEST_USER.toString());
+  }
+
+  @Test
+  public void testCancelTokenFailure() throws Exception {
+    secretManager = createSecretManager(conf, tokenMaxLifetime,
+        expiryTime, tokenRemoverScanInterval);
+    secretManager.startThreads(keyPair);
+    Token<OzoneTokenIdentifier> token = secretManager.createToken(TEST_USER,
+        TEST_USER,
+        TEST_USER);
+    LambdaTestUtils.intercept(AccessControlException.class,
+        "rougeUser is not authorized to cancel the token", () -> {
+          secretManager.cancelToken(token, "rougeUser");
+        });
+  }
+
+  /**
+   * Validate hash using public key of KeyPair.
+   */
+  private void validateHash(byte[] hash, byte[] identifier) throws Exception {
+    Signature rsaSignature =
+        Signature.getInstance(securityConfig.getSignatureAlgo(),
+            securityConfig.getProvider());
+    rsaSignature.initVerify(keyPair.getPublic());
+    rsaSignature.update(identifier);
+    Assert.assertTrue(rsaSignature.verify(hash));
+  }
+
+  /**
+   * Create instance of {@link OzoneSecretManager}.
+   */
+  private OzoneSecretManager<OzoneTokenIdentifier> createSecretManager(
+      OzoneConfiguration config, long tokenMaxLife, long expiry, long
+      tokenRemoverScanTime) throws IOException {
+    return new OzoneSecretManager<>(config, tokenMaxLife,
+        expiry, tokenRemoverScanTime, serviceRpcAdd);
+  }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to