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

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


The following commit(s) were added to refs/heads/master by this push:
     new 26824ca  HDDS-5884. OM Validate S3 Auth for write requests. (#2778)
26824ca is described below

commit 26824ca39f2d62bc3a8c4d510c0f87069d792f85
Author: Bharat Viswanadham <[email protected]>
AuthorDate: Thu Oct 28 15:24:49 2021 -0700

    HDDS-5884. OM Validate S3 Auth for write requests. (#2778)
    
    Co-authored-by: Neil Joshi <[email protected]>
---
 .../ozone/client/rpc/TestSecureOzoneRpcClient.java | 64 +++++++++++++++++
 .../org/apache/hadoop/ozone/om/OzoneManager.java   | 16 ++++-
 .../ozone/om/ratis/OzoneManagerRatisServer.java    |  6 +-
 .../hadoop/ozone/om/request/OMClientRequest.java   |  7 +-
 ...OzoneManagerProtocolServerSideTranslatorPB.java | 11 ++-
 .../hadoop/ozone/security/S3SecurityUtil.java      | 84 ++++++++++++++++++++++
 6 files changed, 182 insertions(+), 6 deletions(-)

diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java
index 0398536..f27184a 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/client/rpc/TestSecureOzoneRpcClient.java
@@ -43,7 +43,16 @@ import org.apache.hadoop.ozone.om.OzoneManager;
 import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OmKeyLocationInfo;
+import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.VolumeInfo;
 import org.apache.hadoop.ozone.security.OzoneBlockTokenSecretManager;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.ozone.test.GenericTestUtils;
 import org.apache.ozone.test.LambdaTestUtils;
 import org.junit.AfterClass;
@@ -60,6 +69,7 @@ import java.util.UUID;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.hadoop.hdds.HddsConfigKeys.OZONE_METADATA_DIRS;
+import static org.apache.hadoop.ozone.ClientVersions.CURRENT_VERSION;
 
 /**
  * This class is to test all the public facing APIs of Ozone Client.
@@ -207,6 +217,60 @@ public class TestSecureOzoneRpcClient extends 
TestOzoneRpcClient {
     }
   }
 
+
+  @Test
+  public void testS3Auth() throws Exception {
+
+    String volumeName = UUID.randomUUID().toString();
+
+    String strToSign = "AWS4-HMAC-SHA256\n" +
+        "20150830T123600Z\n" +
+        "20150830/us-east-1/iam/aws4_request\n" +
+        "f536975d06c0309214f805bb90ccff089219ecd68b2" +
+        "577efef23edd43b7e1a59";
+
+    String signature =  "5d672d79c15b13162d9279b0855cfba" +
+        "6789a8edb4c82c400e06b5924a6f2b5d7";
+
+    String secret = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
+
+
+    String accessKey = UserGroupInformation.getCurrentUser().getUserName();
+
+    // Add secret to S3Secret table.
+    cluster.getOzoneManager().getMetadataManager().getS3SecretTable().put(
+        accessKey, new S3SecretValue(accessKey, secret));
+
+    OMRequest omRequest = OMRequest.newBuilder()
+        .setCmdType(OzoneManagerProtocolProtos.Type.CreateVolume)
+        .setVersion(CURRENT_VERSION)
+        .setClientId(UUID.randomUUID().toString())
+        .setCreateVolumeRequest(CreateVolumeRequest.newBuilder().
+            setVolumeInfo(VolumeInfo.newBuilder().setVolume(volumeName)
+                .setAdminName(accessKey).setOwnerName(accessKey).build())
+            .build())
+        .setS3Authentication(S3Authentication.newBuilder()
+            .setAccessId(accessKey)
+            .setSignature(signature).setStringToSign(strToSign))
+        .build();
+
+    GenericTestUtils.waitFor(() -> cluster.getOzoneManager().isLeaderReady(),
+        100, 120000);
+    OMResponse omResponse = cluster.getOzoneManager().getOmServerProtocol()
+        .submitRequest(null, omRequest);
+
+    Assert.assertTrue(omResponse.getStatus() == Status.OK);
+
+
+    // Override secret to S3Secret table with some dummy value
+    cluster.getOzoneManager().getMetadataManager().getS3SecretTable().put(
+        accessKey, new S3SecretValue(accessKey, "dummy"));
+    omResponse = cluster.getOzoneManager().getOmServerProtocol()
+        .submitRequest(null, omRequest);
+    Assert.assertTrue(omResponse.getStatus() == Status.INVALID_TOKEN);
+
+  }
+
   private boolean verifyRatisReplication(String volumeName, String bucketName,
       String keyName, ReplicationType type, ReplicationFactor factor)
       throws IOException {
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 c95c042..7e49ab5 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
@@ -380,6 +380,8 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
 
   private static final int MSECS_PER_MINUTE = 60 * 1000;
 
+  private final boolean isSecurityEnabled;
+
   @SuppressWarnings("methodlength")
   private OzoneManager(OzoneConfiguration conf, StartupOption startupOption)
       throws IOException, AuthenticationException {
@@ -390,6 +392,7 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
     OMHANodeDetails omhaNodeDetails =
         OMHANodeDetails.loadOMHAConfig(configuration);
 
+    this.isSecurityEnabled = OzoneSecurityUtil.isSecurityEnabled(conf);
     this.peerNodesMap = omhaNodeDetails.getPeerNodesMap();
     this.omNodeDetails = omhaNodeDetails.getLocalNodeDetails();
 
@@ -600,7 +603,7 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
     volumeManager = new VolumeManagerImpl(metadataManager, configuration);
     bucketManager = new BucketManagerImpl(metadataManager, getKmsProvider(),
         isRatisEnabled);
-    if (secConfig.isSecurityEnabled()) {
+    if (secConfig.isSecurityEnabled() || testSecureOmFlag) {
       s3SecretManager = new S3SecretManagerImpl(configuration, 
metadataManager);
       delegationTokenMgr = createDelegationTokenSecretManager(configuration);
     }
@@ -698,6 +701,17 @@ public final class OzoneManager extends 
ServiceRuntimeInfoImpl
     return grpcBlockTokenEnabled;
   }
 
+  /**
+   * Return config value of {@link OzoneConfigKeys#OZONE_SECURITY_ENABLED_KEY}.
+   */
+  public boolean isSecurityEnabled() {
+    return isSecurityEnabled || testSecureOmFlag;
+  }
+
+  public boolean isTestSecureOmFlag() {
+    return testSecureOmFlag;
+  }
+
   private KeyProviderCryptoExtension createKeyProviderExt(
       OzoneConfiguration conf) throws IOException {
     KeyProvider keyProvider = KMSUtil.createKeyProvider(conf,
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
index f026d4c..56b36f0 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java
@@ -384,8 +384,10 @@ public final class OzoneManagerRatisServer {
    * ratis server.
    */
   private RaftClientRequest createWriteRaftClientRequest(OMRequest omRequest) {
-    Preconditions.checkArgument(Server.getClientId() != DUMMY_CLIENT_ID);
-    Preconditions.checkArgument(Server.getCallId() != INVALID_CALL_ID);
+    if (!ozoneManager.isTestSecureOmFlag()) {
+      Preconditions.checkArgument(Server.getClientId() != DUMMY_CLIENT_ID);
+      Preconditions.checkArgument(Server.getCallId() != INVALID_CALL_ID);
+    }
     return RaftClientRequest.newBuilder()
         .setClientId(
             ClientId.valueOf(UUID.nameUUIDFromBytes(Server.getClientId())))
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
index bc75291..5530b38 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/OMClientRequest.java
@@ -131,8 +131,11 @@ public abstract class OMClientRequest implements 
RequestAuditor {
     OzoneManagerProtocolProtos.UserInfo.Builder userInfo =
         OzoneManagerProtocolProtos.UserInfo.newBuilder();
 
-    // Added not null checks, as in UT's these values might be null.
-    if (user != null) {
+    // If S3 Authentication is set, use AccessId as user.
+    if (omRequest.hasS3Authentication()) {
+      userInfo.setUserName(omRequest.getS3Authentication().getAccessId());
+    } else if (user != null) {
+      // Added not null checks, as in UT's these values might be null.
       userInfo.setUserName(user.getUserName());
     }
 
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
index eb8370f..905ca67 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerProtocolServerSideTranslatorPB.java
@@ -45,6 +45,7 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRespo
 import com.google.protobuf.ProtocolMessageEnum;
 import com.google.protobuf.RpcController;
 import com.google.protobuf.ServiceException;
+import org.apache.hadoop.ozone.security.S3SecurityUtil;
 import org.apache.ratis.protocol.RaftPeerId;
 import org.apache.ratis.util.ExitUtils;
 import org.slf4j.Logger;
@@ -126,7 +127,6 @@ public class OzoneManagerProtocolServerSideTranslatorPB 
implements
 
   private OMResponse processRequest(OMRequest request) throws
       ServiceException {
-    RaftServerStatus raftServerStatus;
     if (isRatisEnabled) {
       // Check if the request is a read only request
       if (OmUtils.isReadOnly(request)) {
@@ -134,6 +134,11 @@ public class OzoneManagerProtocolServerSideTranslatorPB 
implements
       } else {
         checkLeaderStatus();
         try {
+          // If Request has S3Authentication validate S3 credentials and
+          // then proceed with processing the request.
+          if (request.hasS3Authentication()) {
+            S3SecurityUtil.validateS3Credential(request, ozoneManager);
+          }
           OMClientRequest omClientRequest =
               createClientRequest(request, ozoneManager);
           request = omClientRequest.preExecute(ozoneManager);
@@ -272,4 +277,8 @@ public class OzoneManagerProtocolServerSideTranslatorPB 
implements
       ozoneManagerDoubleBuffer.stop();
     }
   }
+
+  public static Logger getLog() {
+    return LOG;
+  }
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/S3SecurityUtil.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/S3SecurityUtil.java
new file mode 100644
index 0000000..b9e2a05
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/security/S3SecurityUtil.java
@@ -0,0 +1,84 @@
+/**
+ * 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.annotation.InterfaceAudience;
+import org.apache.hadoop.hdds.annotation.InterfaceStability;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.ozone.om.OzoneManager;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Authentication;
+import 
org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
+import org.apache.hadoop.security.token.SecretManager;
+
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INVALID_TOKEN;
+import static 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMTokenProto.Type.S3AUTHINFO;
+
+/**
+ * Utility class which holds methods required for parse/validation of
+ * S3 Authentication Information which is part of OMRequest.
+ */
[email protected]
[email protected]
+public final class S3SecurityUtil {
+
+  private S3SecurityUtil() {
+  }
+
+  /**
+   * Validate S3 Credentials which are part of {@link OMRequest}.
+   *
+   * If validation is successful returns, else throw {@link OMException}
+   * @throws OMException
+   */
+  public static void validateS3Credential(OMRequest omRequest,
+      OzoneManager ozoneManager) throws OMException {
+    if (ozoneManager.isSecurityEnabled()) {
+      if (omRequest.hasS3Authentication()) {
+        OzoneTokenIdentifier s3Token = constructS3Token(omRequest);
+        try {
+          // authenticate user with signature verification through
+          // delegationTokenMgr validateToken via retrievePassword
+          ozoneManager.getDelegationTokenMgr().retrievePassword(s3Token);
+        } catch (SecretManager.InvalidToken e) {
+          // TODO: Just check are we okay to log enitre token in failure case.
+          OzoneManagerProtocolServerSideTranslatorPB.getLog().error(
+              "signatures do NOT match for S3 identifier:{}", s3Token, e);
+          throw new OMException("User " + s3Token.getAwsAccessId()
+              + " request authorization failure: signatures do NOT match",
+              INVALID_TOKEN);
+        }
+      }
+    }
+  }
+
+  /**
+   * Construct and return {@link OzoneTokenIdentifier} from {@link OMRequest}.
+   */
+  private static OzoneTokenIdentifier constructS3Token(OMRequest omRequest) {
+    S3Authentication auth = omRequest.getS3Authentication();
+    OzoneTokenIdentifier s3Token = new OzoneTokenIdentifier();
+    s3Token.setTokenType(S3AUTHINFO);
+    s3Token.setStrToSign(auth.getStringToSign());
+    s3Token.setSignature(auth.getSignature());
+    s3Token.setAwsAccessId(auth.getAccessId());
+    s3Token.setOwner(new Text(auth.getAccessId()));
+    return s3Token;
+  }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to