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]