You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by bh...@apache.org on 2019/08/22 22:00:24 UTC

[hadoop] branch trunk updated: HDDS-1347. In OM HA getS3Secret call Should happen only leader OM. (#670)

This is an automated email from the ASF dual-hosted git repository.

bharat pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 4028cac  HDDS-1347. In OM HA getS3Secret call Should happen only leader OM. (#670)
4028cac is described below

commit 4028cac56d469c566f2dbad9e9f11c36c53f5ee9
Author: Bharat Viswanadham <bh...@apache.org>
AuthorDate: Thu Aug 22 15:00:17 2019 -0700

    HDDS-1347. In OM HA getS3Secret call Should happen only leader OM. (#670)
---
 .../java/org/apache/hadoop/ozone/OzoneConsts.java  |   1 +
 .../org/apache/hadoop/ozone/audit/OMAction.java    |   4 +-
 .../hadoop/ozone/om/exceptions/OMException.java    |   5 +-
 .../src/main/proto/OzoneManagerProtocol.proto      |  14 ++
 .../hadoop/ozone/TestSecureOzoneCluster.java       |  12 +-
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |   8 +
 .../om/request/s3/security/S3GetSecretRequest.java | 193 +++++++++++++++++++++
 .../ozone/om/request/s3/security/package-info.java |  22 +++
 .../response/s3/security/S3GetSecretResponse.java  |  56 ++++++
 .../om/response/s3/security/package-info.java      |  22 +++
 10 files changed, 333 insertions(+), 4 deletions(-)

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 d9b33d8..80e9260 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
@@ -268,6 +268,7 @@ public final class OzoneConsts {
   public static final String PART_NUMBER_MARKER = "partNumberMarker";
   public static final String MAX_PARTS = "maxParts";
   public static final String S3_BUCKET = "s3Bucket";
+  public static final String S3_GETSECRET_USER = "S3GetSecretUser";
 
 
 
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
index ebcd439..97d4afc 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
@@ -69,7 +69,9 @@ public enum OMAction implements AuditAction {
   CREATE_DIRECTORY,
   CREATE_FILE,
   LOOKUP_FILE,
-  LIST_STATUS;
+  LIST_STATUS,
+
+  GET_S3_SECRET;
 
   @Override
   public String getAction() {
diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
index 1e291ed..268471a 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/exceptions/OMException.java
@@ -207,7 +207,10 @@ public class OMException extends IOException {
 
     RATIS_ERROR, // Error in Ratis server
 
-    INVALID_PATH_IN_ACL_REQUEST // Error code when path name is invalid during
+    INVALID_PATH_IN_ACL_REQUEST, // Error code when path name is invalid during
     // acl requests.
+
+    USER_MISMATCH // Error code when requested user name passed is different
+    // from remote user.
   }
 }
diff --git a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
index ded1607..7d5f098 100644
--- a/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
+++ b/hadoop-ozone/common/src/main/proto/OzoneManagerProtocol.proto
@@ -156,6 +156,8 @@ message OMRequest {
   optional GetAclRequest                    getAclRequest                  = 78;
 
   optional PurgeKeysRequest                 purgeKeysRequest               = 81;
+
+  optional UpdateGetS3SecretRequest         updateGetS3SecretRequest       = 82;
 }
 
 message OMResponse {
@@ -287,6 +289,9 @@ enum Status {
     RATIS_ERROR = 52;
 
     INVALID_PATH_IN_ACL_REQUEST = 53; // Invalid path name in acl request.
+
+    USER_MISMATCH = 54; // Error code when requested user name passed is
+    // different from remote user.
 }
 
 
@@ -1051,6 +1056,15 @@ message GetS3SecretResponse {
 }
 
 /**
+  This will be used internally by OM to replicate S3 Secret across quorum of
+  OM's.
+*/
+message UpdateGetS3SecretRequest {
+    required string kerberosID = 1;
+    required string awsSecret = 2;
+}
+
+/**
  The OM service that takes care of Ozone namespace.
 */
 service OzoneManagerService {
diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
index 853b6a2..709c43f 100644
--- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
+++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestSecureOzoneCluster.java
@@ -104,6 +104,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.slf4j.event.Level.INFO;
 
 /**
@@ -689,11 +690,11 @@ public final class TestSecureOzoneCluster {
 
       //Creates a secret since it does not exist
       S3SecretValue firstAttempt = omClient
-          .getS3Secret("HADOOP/JOHNDOE");
+          .getS3Secret(UserGroupInformation.getCurrentUser().getUserName());
 
       //Fetches the secret from db since it was created in previous step
       S3SecretValue secondAttempt = omClient
-          .getS3Secret("HADOOP/JOHNDOE");
+          .getS3Secret(UserGroupInformation.getCurrentUser().getUserName());
 
       //secret fetched on both attempts must be same
       assertTrue(firstAttempt.getAwsSecret()
@@ -703,6 +704,13 @@ public final class TestSecureOzoneCluster {
       assertTrue(firstAttempt.getAwsAccessKey()
           .equals(secondAttempt.getAwsAccessKey()));
 
+
+      try {
+        omClient.getS3Secret("HADOOP/JOHNDOE");
+        fail("testGetS3Secret failed");
+      } catch (IOException ex) {
+        GenericTestUtils.assertExceptionContains("USER_MISMATCH", ex);
+      }
     } finally {
       if(om != null){
         om.stop();
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index dbd5d39..bbbd61c 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -2683,6 +2683,14 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl
    * {@inheritDoc}
    */
   public S3SecretValue getS3Secret(String kerberosID) throws IOException{
+    UserGroupInformation user = ProtobufRpcEngine.Server.getRemoteUser();
+
+    // Check whether user name passed is matching with the current user or not.
+    if (!user.getUserName().equals(kerberosID)) {
+      throw new OMException("User mismatch. Requested user name is " +
+          "mismatched " + kerberosID +", with current user " +
+          user.getUserName(), OMException.ResultCodes.USER_MISMATCH);
+    }
     return s3SecretManager.getS3Secret(kerberosID);
   }
 
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
new file mode 100644
index 0000000..60f808c
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/S3GetSecretRequest.java
@@ -0,0 +1,193 @@
+/**
+ * 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.request.s3.security;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.base.Optional;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.ipc.ProtobufRpcEngine;
+import org.apache.hadoop.ozone.OmUtils;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.audit.OMAction;
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.OMClientRequest;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.om.response.s3.security.S3GetSecretResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UpdateGetS3SecretRequest;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.utils.db.cache.CacheKey;
+import org.apache.hadoop.utils.db.cache.CacheValue;
+
+import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_SECRET_LOCK;
+
+/**
+ * Handles GetS3Secret request.
+ */
+public class S3GetSecretRequest extends OMClientRequest {
+
+  private static final Logger LOG =
+      LoggerFactory.getLogger(S3GetSecretRequest.class);
+
+  public S3GetSecretRequest(OMRequest omRequest) {
+    super(omRequest);
+  }
+
+  @Override
+  public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
+    GetS3SecretRequest s3GetSecretRequest =
+        getOmRequest().getGetS3SecretRequest();
+
+    // Generate S3 Secret to be used by OM quorum.
+    String kerberosID = s3GetSecretRequest.getKerberosID();
+
+    UserGroupInformation user = ProtobufRpcEngine.Server.getRemoteUser();
+    if (!user.getUserName().equals(kerberosID)) {
+      throw new OMException("User mismatch. Requested user name is " +
+          "mismatched " + kerberosID +", with current user " +
+          user.getUserName(), OMException.ResultCodes.USER_MISMATCH);
+    }
+
+    String s3Secret = DigestUtils.sha256Hex(OmUtils.getSHADigest());
+
+    UpdateGetS3SecretRequest updateGetS3SecretRequest =
+        UpdateGetS3SecretRequest.newBuilder()
+            .setAwsSecret(s3Secret)
+            .setKerberosID(kerberosID).build();
+
+    // Client issues GetS3Secret request, when received by OM leader
+    // it will generate s3Secret. Original GetS3Secret request is
+    // converted to UpdateGetS3Secret request with the generated token
+    // information. This updated request will be submitted to Ratis. In this
+    // way S3Secret created by leader, will be replicated across all
+    // OMs. With this approach, original GetS3Secret request from
+    // client does not need any proto changes.
+    OMRequest.Builder omRequest = OMRequest.newBuilder()
+        .setUserInfo(getUserInfo())
+        .setUpdateGetS3SecretRequest(updateGetS3SecretRequest)
+        .setCmdType(getOmRequest().getCmdType())
+        .setClientId(getOmRequest().getClientId());
+
+    if (getOmRequest().hasTraceID()) {
+      omRequest.setTraceID(getOmRequest().getTraceID());
+    }
+
+    return omRequest.build();
+
+  }
+
+  @Override
+  public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager,
+      long transactionLogIndex,
+      OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
+
+
+    OMClientResponse omClientResponse = null;
+    OMResponse.Builder omResponse = OMResponse.newBuilder()
+            .setCmdType(OzoneManagerProtocolProtos.Type.GetS3Secret)
+            .setStatus(OzoneManagerProtocolProtos.Status.OK)
+            .setSuccess(true);
+    boolean acquiredLock = false;
+    IOException exception = null;
+    OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
+    UpdateGetS3SecretRequest updateGetS3SecretRequest =
+        getOmRequest().getUpdateGetS3SecretRequest();
+    String kerberosID = updateGetS3SecretRequest.getKerberosID();
+    try {
+      String awsSecret = updateGetS3SecretRequest.getAwsSecret();
+      acquiredLock =
+         omMetadataManager.getLock().acquireLock(S3_SECRET_LOCK, kerberosID);
+
+      S3SecretValue s3SecretValue =
+          omMetadataManager.getS3SecretTable().get(kerberosID);
+
+      // If s3Secret for user is not in S3Secret table, add the Secret to cache.
+      if (s3SecretValue == null) {
+        omMetadataManager.getS3SecretTable().addCacheEntry(
+            new CacheKey<>(kerberosID),
+            new CacheValue<>(Optional.of(new S3SecretValue(kerberosID,
+                awsSecret)), transactionLogIndex));
+      } else {
+        // If it already exists, use the existing one.
+        awsSecret = s3SecretValue.getAwsSecret();
+      }
+
+      GetS3SecretResponse.Builder getS3SecretResponse = GetS3SecretResponse
+          .newBuilder().setS3Secret(S3Secret.newBuilder()
+          .setAwsSecret(awsSecret).setKerberosID(kerberosID));
+
+      if (s3SecretValue == null) {
+        omClientResponse =
+            new S3GetSecretResponse(new S3SecretValue(kerberosID, awsSecret),
+            omResponse.setGetS3SecretResponse(getS3SecretResponse).build());
+      } else {
+        // As when it already exists, we don't need to add to DB again. So
+        // set the value to null.
+        omClientResponse = new S3GetSecretResponse(null,
+            omResponse.setGetS3SecretResponse(getS3SecretResponse).build());
+      }
+
+    } catch (IOException ex) {
+      exception = ex;
+      omClientResponse = new S3GetSecretResponse(null,
+          createErrorOMResponse(omResponse, ex));
+    } finally {
+      if (omClientResponse != null) {
+        omClientResponse.setFlushFuture(ozoneManagerDoubleBufferHelper.add(
+            omClientResponse, transactionLogIndex));
+      }
+      if (acquiredLock) {
+        omMetadataManager.getLock().releaseLock(S3_SECRET_LOCK, kerberosID);
+      }
+    }
+
+
+    Map<String, String> auditMap = new HashMap<>();
+    auditMap.put(OzoneConsts.S3_GETSECRET_USER, kerberosID);
+
+    // audit log
+    auditLog(ozoneManager.getAuditLogger(), buildAuditMessage(
+        OMAction.GET_S3_SECRET, auditMap,
+        exception, getOmRequest().getUserInfo()));
+
+    if (exception == null) {
+      LOG.debug("Secret for accessKey:{} is generated Successfully",
+          kerberosID);
+    } else {
+      LOG.error("Secret for accessKey:{} is generation failed", kerberosID,
+          exception);
+    }
+    return omClientResponse;
+  }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/package-info.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/package-info.java
new file mode 100644
index 0000000..94a6b11
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/security/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * 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 contains classes related to S3 security requests.
+ */
+package org.apache.hadoop.ozone.om.request.s3.security;
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
new file mode 100644
index 0000000..61e2016
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/S3GetSecretResponse.java
@@ -0,0 +1,56 @@
+/**
+ * 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.response.s3.security;
+
+import org.apache.hadoop.ozone.om.OMMetadataManager;
+import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.response.OMClientResponse;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import org.apache.hadoop.utils.db.BatchOperation;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.IOException;
+
+/**
+ * Response for GetS3Secret request.
+ */
+public class S3GetSecretResponse extends OMClientResponse {
+
+
+  private S3SecretValue s3SecretValue;
+
+  public S3GetSecretResponse(@Nullable S3SecretValue s3SecretValue,
+      @Nonnull OMResponse omResponse) {
+    super(omResponse);
+    this.s3SecretValue = s3SecretValue;
+  }
+
+  @Override
+  public void addToDBBatch(OMMetadataManager omMetadataManager,
+      BatchOperation batchOperation) throws IOException {
+
+    if (s3SecretValue != null &&
+        getOMResponse().getStatus() == OzoneManagerProtocolProtos.Status.OK) {
+      omMetadataManager.getS3SecretTable().putWithBatch(batchOperation,
+          s3SecretValue.getKerberosID(), s3SecretValue);
+    }
+  }
+}
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/package-info.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/package-info.java
new file mode 100644
index 0000000..d9024d1
--- /dev/null
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/security/package-info.java
@@ -0,0 +1,22 @@
+/**
+ * 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 contains classes related to S3 security responses.
+ */
+package org.apache.hadoop.ozone.om.request.s3.security;


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