This is an automated email from the ASF dual-hosted git repository.
ritesh 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 1c48935dd9 HDDS-7035. Generate strToSign before applying virtual host
style filter. (#5123)
1c48935dd9 is described below
commit 1c48935dd9da72b45e32d3cccee4a2286174b0a8
Author: SaketaChalamchala <[email protected]>
AuthorDate: Thu Sep 21 09:53:46 2023 -0700
HDDS-7035. Generate strToSign before applying virtual host style filter.
(#5123)
---
.../dist/src/main/compose/common/ec-test.sh | 3 +-
.../dist/src/main/compose/ozone-ha/test.sh | 5 +-
.../main/compose/ozone/disabled-test-s3-haproxy.sh | 3 +-
.../s3g-virtual-host.yaml} | 42 ++-
.../test-s3g-virtual-host.sh} | 13 +-
.../dist/src/main/compose/ozonesecure-ha/test.sh | 6 +-
.../src/main/compose/ozonesecure/test-vault.sh | 3 +-
.../src/main/smoketest/s3/awss3virtualhost.robot | 56 ++++
.../dist/src/main/smoketest/s3/commonawslib.robot | 3 +
...lientProducer.java => AuthorizationFilter.java} | 107 +++----
.../org/apache/hadoop/ozone/s3/ClientIpFilter.java | 3 +-
.../apache/hadoop/ozone/s3/HeaderPreprocessor.java | 6 +-
.../hadoop/ozone/s3/OzoneClientProducer.java | 64 ----
.../hadoop/ozone/s3/VirtualHostStyleFilter.java | 15 +-
.../hadoop/ozone/s3/endpoint/EndpointBase.java | 8 +
.../ozone/s3/signature/AWSSignatureProcessor.java | 2 +
.../hadoop/ozone/s3/signature/SignatureInfo.java | 57 ++++
.../ozone/s3/signature/StringToSignProducer.java | 3 +-
.../org/apache/hadoop/ozone/s3/util/S3Consts.java | 1 +
.../hadoop/ozone/s3/TestAuthorizationFilter.java | 333 +++++++++++++++++++++
.../hadoop/ozone/s3/TestOzoneClientProducer.java | 160 +---------
.../ozone/s3/TestVirtualHostStyleFilter.java | 42 ++-
.../signature/TestAuthorizationV4QueryParser.java | 4 +-
.../s3/signature/TestStringToSignProducer.java | 4 +-
24 files changed, 615 insertions(+), 328 deletions(-)
diff --git a/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
b/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
index cfc46f058a..65a659563e 100755
--- a/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
+++ b/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
@@ -17,7 +17,8 @@
start_docker_env 5
-execute_robot_test scm -v BUCKET:erasure s3
+## Exclude virtual-host tests. This is tested separately as it requires
additional config.
+execute_robot_test scm -v BUCKET:erasure --exclude virtual-host s3
prefix=${RANDOM}
execute_robot_test scm -v PREFIX:${prefix} ec/basic.robot
diff --git a/hadoop-ozone/dist/src/main/compose/ozone-ha/test.sh
b/hadoop-ozone/dist/src/main/compose/ozone-ha/test.sh
index 2d73133fdd..08c8522696 100755
--- a/hadoop-ozone/dist/src/main/compose/ozone-ha/test.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozone-ha/test.sh
@@ -35,11 +35,12 @@ execute_robot_test ${SCM} basic/links.robot
execute_robot_test ${SCM} -v SCHEME:ofs -v BUCKET_TYPE:link -N
ozonefs-ofs-link ozonefs/ozonefs.robot
-exclude=""
+## Exclude virtual-host tests. This is tested separately as it requires
additional config.
+exclude="--exclude virtual-host"
for bucket in generated; do
execute_robot_test ${SCM} -v BUCKET:${bucket} -N s3-${bucket} ${exclude} s3
# some tests are independent of the bucket type, only need to be run once
- exclude="--exclude no-bucket-type"
+ exclude="--exclude virtual-host --exclude no-bucket-type"
done
execute_robot_test ${SCM} freon
diff --git
a/hadoop-ozone/dist/src/main/compose/ozone/disabled-test-s3-haproxy.sh
b/hadoop-ozone/dist/src/main/compose/ozone/disabled-test-s3-haproxy.sh
index 9612b92da4..6cf3901b9d 100755
--- a/hadoop-ozone/dist/src/main/compose/ozone/disabled-test-s3-haproxy.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozone/disabled-test-s3-haproxy.sh
@@ -26,4 +26,5 @@ source "$COMPOSE_DIR/../testlib.sh"
start_docker_env
-execute_robot_test scm s3
+## Exclude virtual-host tests. This is tested separately as it requires
additional config.
+execute_robot_test scm --exclude virtual-host s3
diff --git a/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/s3g-virtual-host.yaml
old mode 100755
new mode 100644
similarity index 53%
copy from hadoop-ozone/dist/src/main/compose/common/ec-test.sh
copy to hadoop-ozone/dist/src/main/compose/ozonesecure-ha/s3g-virtual-host.yaml
index cfc46f058a..708231ad20
--- a/hadoop-ozone/dist/src/main/compose/common/ec-test.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/s3g-virtual-host.yaml
@@ -1,4 +1,3 @@
-#!/usr/bin/env bash
# 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
@@ -15,15 +14,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-start_docker_env 5
+version: "3.8"
-execute_robot_test scm -v BUCKET:erasure s3
-
-prefix=${RANDOM}
-execute_robot_test scm -v PREFIX:${prefix} ec/basic.robot
-docker-compose up -d --no-recreate --scale datanode=4
-execute_robot_test scm -v PREFIX:${prefix} -N read-4-datanodes ec/read.robot
-docker-compose up -d --no-recreate --scale datanode=3
-execute_robot_test scm -v PREFIX:${prefix} -N read-3-datanodes ec/read.robot
-docker-compose up -d --no-recreate --scale datanode=5
-execute_robot_test scm -v container:1 -v count:5 -N EC-recovery
replication/wait.robot
+x-s3g-virtual-host-config:
+ &s3g-virtual-host-config
+ environment:
+ - OZONE-SITE.XML_ozone.s3g.domain.name=s3g.internal
+services:
+ datanode1:
+ <<: *s3g-virtual-host-config
+ datanode2:
+ <<: *s3g-virtual-host-config
+ datanode3:
+ <<: *s3g-virtual-host-config
+ om1:
+ <<: *s3g-virtual-host-config
+ om2:
+ <<: *s3g-virtual-host-config
+ om3:
+ <<: *s3g-virtual-host-config
+ scm1.org:
+ <<: *s3g-virtual-host-config
+ scm2.org:
+ <<: *s3g-virtual-host-config
+ scm3.org:
+ <<: *s3g-virtual-host-config
+ s3g:
+ <<: *s3g-virtual-host-config
+ extra_hosts:
+ - "bucket1.s3g.internal: 172.25.0.114"
+ recon:
+ <<: *s3g-virtual-host-config
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test-s3g-virtual-host.sh
similarity index 81%
copy from hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
copy to
hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test-s3g-virtual-host.sh
index f0af40792d..2bc3125801 100755
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test-s3g-virtual-host.sh
@@ -15,17 +15,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#suite:secure
+#suite:HA-secure
COMPOSE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export COMPOSE_DIR
+export SECURITY_ENABLED=true
+export OM_SERVICE_ID="omservice"
+export SCM=scm1.org
+export COMPOSE_FILE=docker-compose.yaml:s3g-virtual-host.yaml
+
# shellcheck source=/dev/null
source "$COMPOSE_DIR/../testlib.sh"
-export SECURITY_ENABLED=true
-export COMPOSE_FILE=docker-compose.yaml:vault.yaml
-
start_docker_env
-execute_robot_test scm s3
+## Run virtual host test cases
+execute_robot_test s3g -N s3-virtual-host s3/awss3virtualhost.robot
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test.sh
b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test.sh
index 70d45b33c6..f6ab69746c 100755
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure-ha/test.sh
@@ -41,11 +41,13 @@ execute_robot_test s3g -v SCHEME:o3fs -v BUCKET_TYPE:link
-N ozonefs-o3fs-link o
execute_robot_test s3g basic/links.robot
-exclude=""
+## Exclude virtual-host tests. This is tested separately as it requires
additional config.
+exclude="--exclude virtual-host"
for bucket in encrypted link; do
execute_robot_test s3g -v BUCKET:${bucket} -N s3-${bucket} ${exclude} s3
# some tests are independent of the bucket type, only need to be run once
- exclude="--exclude no-bucket-type"
+ ## Exclude virtual-host.robot
+ exclude="--exclude virtual-host --exclude no-bucket-type"
done
execute_robot_test s3g admincli
diff --git a/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
b/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
index f0af40792d..f2bc0948fe 100755
--- a/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
+++ b/hadoop-ozone/dist/src/main/compose/ozonesecure/test-vault.sh
@@ -28,4 +28,5 @@ export COMPOSE_FILE=docker-compose.yaml:vault.yaml
start_docker_env
-execute_robot_test scm s3
+## Exclude virtual-host tests. This is tested separately as it requires
additional config.
+execute_robot_test scm --exclude virtual-host s3
diff --git a/hadoop-ozone/dist/src/main/smoketest/s3/awss3virtualhost.robot
b/hadoop-ozone/dist/src/main/smoketest/s3/awss3virtualhost.robot
new file mode 100644
index 0000000000..8f77e2c3e8
--- /dev/null
+++ b/hadoop-ozone/dist/src/main/smoketest/s3/awss3virtualhost.robot
@@ -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
+#
+# 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.
+
+*** Settings ***
+Documentation S3 gateway test with aws cli using virtual host style
address
+Library OperatingSystem
+Library String
+Resource ../commonlib.robot
+Resource ./commonawslib.robot
+Test Timeout 5 minutes
+Suite Setup Setup s3 tests
+Default Tags virtual-host
+
+*** Variables ***
+${ENDPOINT_URL} http://s3g.internal:9878
+${BUCKET} bucket1
+${OZONE_S3_ADDRESS_STYLE} virtual
+
+*** Test Cases ***
+
+File upload and directory list with virtual style addressing
+ Create bucket with name ${BUCKET}
+ Execute date > /tmp/testfile
+ ${result} = Execute AWSS3Cli cp /tmp/testfile
s3://${BUCKET} --debug
+ Should contain ${result}
url=http://bucket1.s3g.internal:9878/
+ Should contain ${result} upload
+ ${result} = Execute AWSS3Cli cp /tmp/testfile
s3://${BUCKET}/dir1/dir2/file --debug
+ Should contain ${result}
url=http://bucket1.s3g.internal:9878/dir1/dir2/file
+ Should contain ${result} upload
+ ${result} = Execute AWSS3Cli ls s3://${BUCKET} --debug
+ Should contain ${result}
url=http://bucket1.s3g.internal:9878/
+ Should contain ${result} testfile
+ Should contain ${result} dir1
+ Should not contain ${result} dir2
+ ${result} = Execute AWSS3Cli ls s3://${BUCKET}/dir1/
--debug
+ Should contain ${result}
url=http://bucket1.s3g.internal:9878/
+ Should contain ${result} prefix=dir1
+ Should not contain ${result} testfile
+ Should contain ${result} dir2/
+ ${result} = Execute AWSS3Cli ls
s3://${BUCKET}/dir1/dir2/file
+ Should not contain ${result} testfile
+ Should not contain ${result} dir1
+ Should not contain ${result} dir2
+ Should contain ${result} file
diff --git a/hadoop-ozone/dist/src/main/smoketest/s3/commonawslib.robot
b/hadoop-ozone/dist/src/main/smoketest/s3/commonawslib.robot
index 0a6a42f005..98691876e1 100644
--- a/hadoop-ozone/dist/src/main/smoketest/s3/commonawslib.robot
+++ b/hadoop-ozone/dist/src/main/smoketest/s3/commonawslib.robot
@@ -26,6 +26,7 @@ ${BUCKET} generated
${KEY_NAME} key1
${OZONE_S3_TESTS_SET_UP} ${FALSE}
${OZONE_AWS_ACCESS_KEY_ID} ${EMPTY}
+${OZONE_S3_ADDRESS_STYLE} path
*** Keywords ***
Execute AWSS3APICli
@@ -85,6 +86,8 @@ Setup secure v4 headers
Execute aws configure set
aws_access_key_id ${accessKey}
Execute aws configure set
aws_secret_access_key ${secret}
Execute aws configure set region
us-west-1
+ Execute aws configure set
default.s3.addressing_style ${OZONE_S3_ADDRESS_STYLE}
+
Setup dummy credentials for S3
Execute aws configure set
default.s3.signature_version s3v4
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/AuthorizationFilter.java
similarity index 58%
copy from
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
copy to
hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/AuthorizationFilter.java
index bfab4559a3..d49ff17f3b 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/AuthorizationFilter.java
@@ -1,4 +1,4 @@
-/*
+/**
* 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
@@ -6,9 +6,9 @@
* 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
- *
+ * <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.
@@ -17,20 +17,16 @@
*/
package org.apache.hadoop.ozone.s3;
-import javax.annotation.PreDestroy;
-import javax.enterprise.context.RequestScoped;
-import javax.enterprise.inject.Produces;
+import javax.annotation.Priority;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.core.Context;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
-import java.io.IOException;
+import javax.ws.rs.ext.Provider;
import com.google.common.annotations.VisibleForTesting;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.ozone.client.OzoneClient;
-import org.apache.hadoop.ozone.om.protocol.S3Auth;
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
@@ -40,53 +36,50 @@ import
org.apache.hadoop.ozone.s3.signature.StringToSignProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.ACCESS_DENIED;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INTERNAL_ERROR;
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
/**
- * This class creates the OzoneClient for the Rest endpoints.
+ * Filter used to construct string to sign from unfiltered request.
+ * It should be executed before all other filters as the original
+ * could be enriched later.
*/
-@RequestScoped
-public class OzoneClientProducer {
- private static final Logger LOG =
- LoggerFactory.getLogger(OzoneClientProducer.class);
+@Provider
+@PreMatching
+@Priority(AuthorizationFilter.PRIORITY)
+public class AuthorizationFilter implements ContainerRequestFilter {
+ public static final int PRIORITY = 50;
- private OzoneClient client;
+ private static final Logger LOG = LoggerFactory.getLogger(
+ AuthorizationFilter.class);
@Inject
private SignatureProcessor signatureProcessor;
@Inject
- private OzoneConfiguration ozoneConfiguration;
-
- @Context
- private ContainerRequestContext context;
+ private SignatureInfo signatureInfo;
- @Produces
- public synchronized OzoneClient createClient() throws
WebApplicationException,
+ @Override
+ public void filter(ContainerRequestContext context) throws
IOException {
- ozoneConfiguration.set("ozone.om.group.rights", "NONE");
- client = getClient(ozoneConfiguration);
- return client;
- }
+ // Skip authentication if the uri is hitting S3Secret generation or
+ // revocation endpoint.
+ if (context.getUriInfo().getRequestUri().getPath().startsWith("/secret")) {
+ return;
+ }
- @PreDestroy
- public void destroy() throws IOException {
- client.getObjectStore().getClientProxy().clearThreadLocalS3Auth();
- }
- @Produces
- public S3Auth getSignature() {
try {
- SignatureInfo signatureInfo = signatureProcessor.parseSignature();
- String stringToSign = "";
+ signatureInfo.initialize(signatureProcessor.parseSignature());
if (signatureInfo.getVersion() == Version.V4) {
- stringToSign =
- StringToSignProducer.createSignatureBase(signatureInfo, context);
+ signatureInfo.setStrToSign(
+ StringToSignProducer.createSignatureBase(signatureInfo, context));
} else {
LOG.debug("Unsupported AWS signature version: {}",
- signatureInfo.getVersion());
+ signatureInfo.getVersion());
throw S3_AUTHINFO_CREATION_ERROR;
}
@@ -96,12 +89,6 @@ public class OzoneClientProducer {
LOG.debug("Malformed s3 header. awsAccessID: {}", awsAccessId);
throw ACCESS_DENIED;
}
-
- // Note: userPrincipal is initialized to be the same value as accessId,
- // could be updated later in RpcClient#getS3Volume
- return new S3Auth(stringToSign,
- signatureInfo.getSignature(),
- awsAccessId, awsAccessId);
} catch (OS3Exception ex) {
LOG.debug("Error during Client Creation: ", ex);
throw wrapOS3Exception(ex);
@@ -109,34 +96,24 @@ public class OzoneClientProducer {
// For any other critical errors during object creation throw Internal
// error.
LOG.debug("Error during Client Creation: ", e);
- throw wrapOS3Exception(S3ErrorTable.newError(INTERNAL_ERROR, null, e));
+ throw wrapOS3Exception(
+ S3ErrorTable.newError(INTERNAL_ERROR, null, e));
}
}
- private OzoneClient getClient(OzoneConfiguration config)
- throws IOException {
- OzoneClient ozoneClient = null;
- try {
- ozoneClient =
- OzoneClientCache.getOzoneClientInstance(ozoneConfiguration);
- } catch (Exception e) {
- // For any other critical errors during object creation throw Internal
- // error.
- if (LOG.isDebugEnabled()) {
- LOG.debug("Error during Client Creation: ", e);
- }
- throw e;
- }
- return ozoneClient;
+ @VisibleForTesting
+ public void setSignatureParser(SignatureProcessor awsSignatureProcessor) {
+ this.signatureProcessor = awsSignatureProcessor;
}
- public synchronized void setOzoneConfiguration(OzoneConfiguration config) {
- this.ozoneConfiguration = config;
+ @VisibleForTesting
+ public void setSignatureInfo(SignatureInfo signatureInfo) {
+ this.signatureInfo = signatureInfo;
}
@VisibleForTesting
- public void setSignatureParser(SignatureProcessor awsSignatureProcessor) {
- this.signatureProcessor = awsSignatureProcessor;
+ public SignatureInfo getSignatureInfo() {
+ return signatureInfo;
}
private WebApplicationException wrapOS3Exception(OS3Exception os3Exception) {
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/ClientIpFilter.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/ClientIpFilter.java
index 921b18d9b5..858ebbcd0b 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/ClientIpFilter.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/ClientIpFilter.java
@@ -38,7 +38,8 @@ import java.io.IOException;
@Priority(ClientIpFilter.PRIORITY)
public class ClientIpFilter implements ContainerRequestFilter {
- public static final int PRIORITY = 200;
+ public static final int PRIORITY = HeaderPreprocessor.PRIORITY +
+ S3GatewayHttpServer.FILTER_PRIORITY_DO_AFTER;
public static final String CLIENT_IP_HEADER = "client_ip";
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/HeaderPreprocessor.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/HeaderPreprocessor.java
index 344fd90971..c9deb22649 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/HeaderPreprocessor.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/HeaderPreprocessor.java
@@ -34,10 +34,12 @@ import java.io.IOException;
*/
@Provider
@PreMatching
-@Priority(VirtualHostStyleFilter.PRIORITY
- + S3GatewayHttpServer.FILTER_PRIORITY_DO_AFTER)
+@Priority(HeaderPreprocessor.PRIORITY)
public class HeaderPreprocessor implements ContainerRequestFilter {
+ public static final int PRIORITY = VirtualHostStyleFilter.PRIORITY +
+ S3GatewayHttpServer.FILTER_PRIORITY_DO_AFTER;
+
public static final String MULTIPART_UPLOAD_MARKER = "ozone/mpu";
public static final String CONTENT_TYPE = "Content-Type";
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
index bfab4559a3..50e1f80e9c 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/OzoneClientProducer.java
@@ -24,26 +24,13 @@ import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
import java.io.IOException;
-import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.client.OzoneClient;
-import org.apache.hadoop.ozone.om.protocol.S3Auth;
-import org.apache.hadoop.ozone.s3.exception.OS3Exception;
-import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
-import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
-import org.apache.hadoop.ozone.s3.signature.SignatureInfo.Version;
-import org.apache.hadoop.ozone.s3.signature.SignatureProcessor;
-import org.apache.hadoop.ozone.s3.signature.StringToSignProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.ACCESS_DENIED;
-import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INTERNAL_ERROR;
-import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
-
/**
* This class creates the OzoneClient for the Rest endpoints.
*/
@@ -55,9 +42,6 @@ public class OzoneClientProducer {
private OzoneClient client;
- @Inject
- private SignatureProcessor signatureProcessor;
-
@Inject
private OzoneConfiguration ozoneConfiguration;
@@ -76,42 +60,6 @@ public class OzoneClientProducer {
public void destroy() throws IOException {
client.getObjectStore().getClientProxy().clearThreadLocalS3Auth();
}
- @Produces
- public S3Auth getSignature() {
- try {
- SignatureInfo signatureInfo = signatureProcessor.parseSignature();
- String stringToSign = "";
- if (signatureInfo.getVersion() == Version.V4) {
- stringToSign =
- StringToSignProducer.createSignatureBase(signatureInfo, context);
- } else {
- LOG.debug("Unsupported AWS signature version: {}",
- signatureInfo.getVersion());
- throw S3_AUTHINFO_CREATION_ERROR;
- }
-
- String awsAccessId = signatureInfo.getAwsAccessId();
- // ONLY validate aws access id when needed.
- if (awsAccessId == null || awsAccessId.equals("")) {
- LOG.debug("Malformed s3 header. awsAccessID: {}", awsAccessId);
- throw ACCESS_DENIED;
- }
-
- // Note: userPrincipal is initialized to be the same value as accessId,
- // could be updated later in RpcClient#getS3Volume
- return new S3Auth(stringToSign,
- signatureInfo.getSignature(),
- awsAccessId, awsAccessId);
- } catch (OS3Exception ex) {
- LOG.debug("Error during Client Creation: ", ex);
- throw wrapOS3Exception(ex);
- } catch (Exception e) {
- // For any other critical errors during object creation throw Internal
- // error.
- LOG.debug("Error during Client Creation: ", e);
- throw wrapOS3Exception(S3ErrorTable.newError(INTERNAL_ERROR, null, e));
- }
- }
private OzoneClient getClient(OzoneConfiguration config)
throws IOException {
@@ -133,16 +81,4 @@ public class OzoneClientProducer {
public synchronized void setOzoneConfiguration(OzoneConfiguration config) {
this.ozoneConfiguration = config;
}
-
- @VisibleForTesting
- public void setSignatureParser(SignatureProcessor awsSignatureProcessor) {
- this.signatureProcessor = awsSignatureProcessor;
- }
-
- private WebApplicationException wrapOS3Exception(OS3Exception os3Exception) {
- return new WebApplicationException(os3Exception.getErrorMessage(),
- os3Exception,
- Response.status(os3Exception.getHttpCode())
- .entity(os3Exception.toXml()).build());
- }
}
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java
index a257155e76..32c1eb9eb2 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java
@@ -49,7 +49,8 @@ import static
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_DOMAIN_NA
@Priority(VirtualHostStyleFilter.PRIORITY)
public class VirtualHostStyleFilter implements ContainerRequestFilter {
- public static final int PRIORITY = 100;
+ public static final int PRIORITY = AuthorizationFilter.PRIORITY +
+ S3GatewayHttpServer.FILTER_PRIORITY_DO_AFTER;
private static final Logger LOG = LoggerFactory.getLogger(
VirtualHostStyleFilter.class);
@@ -62,6 +63,12 @@ public class VirtualHostStyleFilter implements
ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws
IOException {
+ // Skip this filter if the uri is hitting S3Secret generation or
+ // revocation endpoint.
+ if (requestContext.getUriInfo().getRequestUri().getPath()
+ .startsWith("/secret")) {
+ return;
+ }
domains = conf.getTrimmedStrings(OZONE_S3G_DOMAIN_NAME);
@@ -103,12 +110,12 @@ public class VirtualHostStyleFilter implements
ContainerRequestFilter {
URI baseURI = requestContext.getUriInfo().getBaseUri();
String currentPath = requestContext.getUriInfo().getPath();
String newPath = bucketName;
- if (currentPath != null) {
- newPath += String.format("%s", currentPath);
- }
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo()
.getQueryParameters();
UriBuilder requestAddrBuilder =
UriBuilder.fromUri(baseURI).path(newPath);
+ if (currentPath != null) {
+ requestAddrBuilder.path(currentPath);
+ }
queryParams.forEach((k, v) -> requestAddrBuilder.queryParam(k,
v.toArray()));
URI requestAddr = requestAddrBuilder.build();
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
index 66fb7df190..97027abd7c 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
@@ -57,6 +57,7 @@ import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.ozone.s3.metrics.S3GatewayMetrics;
+import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
import org.apache.hadoop.ozone.s3.util.AuditUtils;
import org.apache.hadoop.util.Time;
import org.slf4j.Logger;
@@ -75,6 +76,8 @@ public abstract class EndpointBase implements Auditor {
@Inject
private OzoneClient client;
@Inject
+ private SignatureInfo signatureInfo;
+
private S3Auth s3Auth;
@Context
private ContainerRequestContext context;
@@ -114,6 +117,11 @@ public abstract class EndpointBase implements Auditor {
*/
@PostConstruct
public void initialization() {
+ // Note: userPrincipal is initialized to be the same value as accessId,
+ // could be updated later in RpcClient#getS3Volume
+ s3Auth = new S3Auth(signatureInfo.getStringToSign(),
+ signatureInfo.getSignature(),
+ signatureInfo.getAwsAccessId(), signatureInfo.getAwsAccessId());
LOG.debug("S3 access id: {}", s3Auth.getAccessID());
getClient().getObjectStore()
.getClientProxy()
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
index 1ae8682186..43a1e6b713 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/AWSSignatureProcessor.java
@@ -95,6 +95,8 @@ public class AWSSignatureProcessor implements
SignatureProcessor {
"", "", "", "", "", "", "", false
);
}
+ signatureInfo.setUnfilteredURI(
+ context.getUriInfo().getRequestUri().getPath());
return signatureInfo;
}
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
index 0da8895921..d1db38a50a 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/SignatureInfo.java
@@ -17,11 +17,14 @@
*/
package org.apache.hadoop.ozone.s3.signature;
+import javax.enterprise.context.RequestScoped;
+
/**
* Signature and related information.
* <p>
* Required to create stringToSign and token.
*/
+@RequestScoped
public class SignatureInfo {
private Version version;
@@ -48,6 +51,13 @@ public class SignatureInfo {
private boolean signPayload = true;
+ private String unfilteredURI = null;
+
+
+ private String stringToSign = null;
+
+ public SignatureInfo() { }
+
@SuppressWarnings("checkstyle:ParameterNumber")
public SignatureInfo(
Version version,
@@ -59,6 +69,35 @@ public class SignatureInfo {
String credentialScope,
String algorithm,
boolean signPayload
+ ) {
+ initialize(version, date, dateTime, awsAccessId, signature, signedHeaders,
+ credentialScope, algorithm, signPayload, null, null);
+ }
+
+ public void initialize(
+ SignatureInfo signatureInfo
+ ) {
+ initialize(signatureInfo.getVersion(), signatureInfo.getDate(),
+ signatureInfo.getDateTime(), signatureInfo.getAwsAccessId(),
+ signatureInfo.getSignature(), signatureInfo.getSignedHeaders(),
+ signatureInfo.getCredentialScope(), signatureInfo.getAlgorithm(),
+ signatureInfo.isSignPayload(), signatureInfo.getUnfilteredURI(),
+ signatureInfo.getStringToSign());
+ }
+
+ @SuppressWarnings({"checkstyle:ParameterNumber", "checkstyle:HiddenField"})
+ public void initialize(
+ Version version,
+ String date,
+ String dateTime,
+ String awsAccessId,
+ String signature,
+ String signedHeaders,
+ String credentialScope,
+ String algorithm,
+ boolean signPayload,
+ String uri,
+ String stringToSign
) {
this.version = version;
this.date = date;
@@ -69,6 +108,8 @@ public class SignatureInfo {
this.credentialScope = credentialScope;
this.algorithm = algorithm;
this.signPayload = signPayload;
+ this.unfilteredURI = uri;
+ this.stringToSign = stringToSign;
}
public String getAwsAccessId() {
@@ -107,6 +148,22 @@ public class SignatureInfo {
return dateTime;
}
+ public String getUnfilteredURI() {
+ return unfilteredURI;
+ }
+
+ public String getStringToSign() {
+ return stringToSign;
+ }
+
+ public void setUnfilteredURI(String uri) {
+ this.unfilteredURI = uri;
+ }
+
+ public void setStrToSign(String strToSign) {
+ this.stringToSign = strToSign;
+ }
+
/**
* Signature version.
*/
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
index b3fec69ba1..a8a4f6072d 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/signature/StringToSignProducer.java
@@ -81,7 +81,6 @@ public final class StringToSignProducer {
return createSignatureBase(signatureInfo,
context.getUriInfo().getRequestUri().getScheme(),
context.getMethod(),
- context.getUriInfo().getRequestUri().getPath(),
LowerCaseKeyStringMap.fromHeaderMap(context.getHeaders()),
fromMultiValueToSingleValueMap(
context.getUriInfo().getQueryParameters()));
@@ -92,7 +91,6 @@ public final class StringToSignProducer {
SignatureInfo signatureInfo,
String scheme,
String method,
- String uri,
LowerCaseKeyStringMap headers,
Map<String, String> queryParams
) throws Exception {
@@ -111,6 +109,7 @@ public final class StringToSignProducer {
String credentialScope = signatureInfo.getCredentialScope();
// If the absolute path is empty, use a forward slash (/)
+ String uri = signatureInfo.getUnfilteredURI();
uri = (uri.trim().length() > 0) ? uri : "/";
// Encode URI and preserve forward slashes
strToSign.append(signatureInfo.getAlgorithm() + NEWLINE);
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java
index 05e9503225..df3d01936b 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java
@@ -64,6 +64,7 @@ public final class S3Consts {
public static final String CUSTOM_METADATA_HEADER_PREFIX = "x-amz-meta-";
+
public static final String DECODED_CONTENT_LENGTH_HEADER =
"x-amz-decoded-content-length";
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
new file mode 100644
index 0000000000..3bf3dcdd7b
--- /dev/null
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestAuthorizationFilter.java
@@ -0,0 +1,333 @@
+/*
+ * 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.s3;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.stream.Stream;
+
+import org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor;
+import org.apache.hadoop.ozone.s3.signature.SignatureInfo;
+import org.apache.hadoop.ozone.s3.signature.StringToSignProducer;
+import org.apache.kerby.util.Hex;
+
+import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+
+import static
org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor.DATE_FORMATTER;
+import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.MALFORMED_HEADER;
+import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
+import static
org.apache.hadoop.ozone.s3.signature.SignatureParser.AUTHORIZATION_HEADER;
+import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.CONTENT_MD5;
+import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.CONTENT_TYPE;
+import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.HOST_HEADER;
+import static
org.apache.hadoop.ozone.s3.signature.StringToSignProducer.X_AMAZ_DATE;
+import static
org.apache.hadoop.ozone.s3.signature.StringToSignProducer.X_AMZ_CONTENT_SHA256;
+import static org.junit.Assert.fail;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+import org.junit.Assert;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+
+/**
+ * This class test string to sign generation.
+ */
+public class TestAuthorizationFilter {
+
+ private AuthorizationFilter authorizationFilter = new AuthorizationFilter();
+
+ private MultivaluedMap<String, String> headerMap;
+ private MultivaluedMap<String, String> queryMap;
+ private MultivaluedMap<String, String> pathParamsMap;
+
+ private static final String DATETIME = StringToSignProducer.TIME_FORMATTER.
+ format(LocalDateTime.now());
+
+ private static final String CURDATE = DATE_FORMATTER.format(LocalDate.now());
+
+ private static Stream<Arguments>testAuthFilterFailuresInput() {
+ return Stream.of(
+ arguments(
+ "GET",
+ "AWS4-HMAC-SHA256 Credential=testuser1/20190221/us-west-1/s3" +
+ "/aws4_request, SignedHeaders=content-md5;host;" +
+ "x-amz-content-sha256;x-amz-date, " +
+ "Signature" +
+ "=56ec73ba1974f8feda8365c3caef89c5d4a688d5f9baccf47" +
+ "65f46a14cd745ad",
+ "Zi68x2nPDDXv5qfDC+ZWTg==",
+ "s3g:9878",
+ "e2bd43f11c97cde3465e0e8d1aad77af7ec7aa2ed8e213cd0e24" +
+ "1e28375860c6",
+ "20190221T002037Z",
+ "",
+ "/",
+ MALFORMED_HEADER.getErrorMessage()
+ ),
+ arguments(
+ "GET",
+ "AWS4-HMAC-SHA256 " +
+ "Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request," +
+ " SignedHeaders=content-type;host;x-amz-date, " +
+ "Signature=" +
+ "5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400" +
+ "e06b5924a6f2b5d7",
+ "",
+ "iam.amazonaws.com",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "20150830T123600Z",
+ "application/x-www-form-urlencoded; charset=utf-8",
+ "",
+ MALFORMED_HEADER.getErrorMessage()
+ ),
+ arguments(null, null, null, null, null, null, null, null,
+ S3_AUTHINFO_CREATION_ERROR.getErrorMessage()),
+ arguments(null, "", null, null, null, null, null, null,
+ S3_AUTHINFO_CREATION_ERROR.getErrorMessage()),
+ // AWS V2 signature
+ arguments(
+ "GET",
+ "AWS AKIDEXAMPLE:St7bHPOdkmsX/GITGe98rOQiUCg=",
+ "",
+ "s3g:9878",
+ "",
+ "Wed, 22 Mar 2023 17:00:06 +0000",
+ "application/octet-stream",
+ "/",
+ S3_AUTHINFO_CREATION_ERROR.getErrorMessage()
+ )
+ );
+ }
+
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ @ParameterizedTest
+ @MethodSource("testAuthFilterFailuresInput")
+ public void testAuthFilterFailures(
+ String method, String authHeader, String contentMd5,
+ String host, String amzContentSha256, String date, String contentType,
+ String path, String expectedErrorMsg
+ ) {
+ try {
+ ContainerRequestContext context = setupContext(method, authHeader,
+ contentMd5, host, amzContentSha256, date, contentType, path);
+
+ AWSSignatureProcessor awsSignatureProcessor = new
AWSSignatureProcessor();
+ awsSignatureProcessor.setContext(context);
+
+ SignatureInfo signatureInfo = new SignatureInfo();
+
+ authorizationFilter.setSignatureParser(awsSignatureProcessor);
+ authorizationFilter.setSignatureInfo(signatureInfo);
+
+ authorizationFilter.filter(context);
+ if ("".equals(authHeader)) {
+ fail("Empty AuthHeader must fail");
+ }
+ } catch (WebApplicationException ex) {
+ if (authHeader == null || authHeader.isEmpty() ||
+ authHeader.startsWith("AWS ")) {
+ // Empty auth header and unsupported AWS signature
+ // should fail with Invalid Request.
+ Assert.assertEquals(HTTP_FORBIDDEN, ex.getResponse().getStatus());
+ Assert.assertEquals(expectedErrorMsg,
+ ex.getMessage());
+ } else {
+ // Other requests have stale timestamp and
+ // should fail with Malformed Authorization Header.
+ Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
+ Assert.assertEquals(expectedErrorMsg,
+ ex.getMessage());
+
+ }
+
+ } catch (Exception ex) {
+ fail("Unexpected exception: " + ex);
+ }
+ }
+
+ private static Stream<Arguments>testAuthFilterInput() {
+ return Stream.of(
+ // Path style URI
+ arguments(
+ "GET",
+ "AWS4-HMAC-SHA256 Credential=testuser1/" + CURDATE +
+ "/us-east-1/s3/aws4_request, " +
+ "SignedHeaders=host;x-amz-content-sha256;" +
+ "x-amz-date, " +
+ "Signature" +
+ "=56ec73ba1974f8feda8365c3caef89c5d4a688d5f9baccf47" +
+ "65f46a14cd745ad",
+ "Content-SHA",
+ "s3g:9878",
+ "Content-SHA",
+ DATETIME,
+ "",
+ "/bucket1/key1"
+ ),
+ // Virtual style URI
+ arguments(
+ "GET",
+ "AWS4-HMAC-SHA256 Credential=testuser1/" + CURDATE +
+ "/us-east-1/s3/aws4_request, " +
+ "SignedHeaders=host;x-amz-content-sha256;" +
+ "x-amz-date, " +
+ "Signature" +
+ "=56ec73ba1974f8feda8365c3caef89c5d4a688d5f9baccf47" +
+ "65f46a14cd745ad",
+ "Content-SHA",
+ "bucket1.s3g.internal:9878",
+ "Content-SHA",
+ DATETIME,
+ "",
+ "/key1"
+ ),
+ // S3 secret generation endpoint
+ arguments(
+ "POST",
+ null,
+ null,
+ "s3g:9878",
+ null,
+ null,
+ "",
+ "/secret/generate"
+ ),
+ // S3 secret generation endpoint
+ arguments(
+ "POST",
+ null,
+ null,
+ "s3g:9878",
+ null,
+ null,
+ "",
+ "/secret/revoke"
+ )
+ );
+ }
+
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ @ParameterizedTest
+ @MethodSource("testAuthFilterInput")
+ public void testAuthFilter(
+ String method, String authHeader, String contentMd5,
+ String host, String amzContentSha256, String date, String contentType,
+ String path
+ ) {
+ try {
+ ContainerRequestContext context = setupContext(method, authHeader,
+ contentMd5, host, amzContentSha256, date, contentType, path);
+
+ AWSSignatureProcessor awsSignatureProcessor = new
AWSSignatureProcessor();
+ awsSignatureProcessor.setContext(context);
+
+ SignatureInfo signatureInfo = new SignatureInfo();
+
+ authorizationFilter.setSignatureParser(awsSignatureProcessor);
+ authorizationFilter.setSignatureInfo(signatureInfo);
+
+ authorizationFilter.filter(context);
+
+ if (path.startsWith("/secret")) {
+ Assert.assertNull(
+ authorizationFilter.getSignatureInfo().getUnfilteredURI());
+
+ Assert.assertNull(
+ authorizationFilter.getSignatureInfo().getStringToSign());
+ } else {
+ String canonicalRequest = method + "\n"
+ + path + "\n"
+ + "\n"
+ + "host:" + host + "\nx-amz-content-sha256:" + amzContentSha256 +
+ "\n"
+ + "x-amz-date:" + DATETIME + "\n"
+ + "\n"
+ + "host;x-amz-content-sha256;x-amz-date\n"
+ + amzContentSha256;
+
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(canonicalRequest.getBytes(StandardCharsets.UTF_8));
+
+ String expectedStrToSign = "AWS4-HMAC-SHA256\n"
+ + DATETIME + "\n"
+ + CURDATE + "/us-east-1/s3/aws4_request\n"
+ + Hex.encode(md.digest()).toLowerCase();
+
+ Assert.assertEquals("Unfiltered URI is not preserved",
+ path,
+ authorizationFilter.getSignatureInfo().getUnfilteredURI());
+
+ Assert.assertEquals("String to sign is invalid",
+ expectedStrToSign,
+ authorizationFilter.getSignatureInfo().getStringToSign());
+ }
+ } catch (Exception ex) {
+ fail("Unexpected exception: " + ex);
+ }
+ }
+
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ private ContainerRequestContext setupContext(
+ String method, String authHeader, String contentMd5,
+ String host, String amzContentSha256, String date, String contentType,
+ String path) throws URISyntaxException {
+ headerMap = new MultivaluedHashMap<>();
+ queryMap = new MultivaluedHashMap<>();
+ pathParamsMap = new MultivaluedHashMap<>();
+
+ System.err.println("Testing: " + authHeader);
+ headerMap.putSingle(AUTHORIZATION_HEADER, authHeader);
+ headerMap.putSingle(CONTENT_MD5, contentMd5);
+ headerMap.putSingle(HOST_HEADER, host);
+ headerMap.putSingle(X_AMZ_CONTENT_SHA256, amzContentSha256);
+ headerMap.putSingle(X_AMAZ_DATE, date);
+ headerMap.putSingle(CONTENT_TYPE, contentType);
+
+ UriInfo uriInfo = Mockito.mock(UriInfo.class);
+ ContainerRequestContext context = Mockito.mock(
+ ContainerRequestContext.class);
+ Mockito.when(uriInfo.getQueryParameters()).thenReturn(queryMap);
+ Mockito.when(uriInfo.getRequestUri()).thenReturn(
+ new URI("http://" + host + path));
+
+ Mockito.when(context.getMethod()).thenReturn(method);
+ Mockito.when(context.getUriInfo()).thenReturn(uriInfo);
+ Mockito.when(context.getHeaders()).thenReturn(headerMap);
+ Mockito.when(context.getHeaderString(AUTHORIZATION_HEADER))
+ .thenReturn(authHeader);
+ Mockito.when(context.getUriInfo().getQueryParameters())
+ .thenReturn(queryMap);
+ Mockito.when(context.getUriInfo().getPathParameters())
+ .thenReturn(pathParamsMap);
+
+ return context;
+ }
+
+}
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java
index 064d9836f6..ed7a8be944 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestOzoneClientProducer.java
@@ -17,131 +17,33 @@
*/
package org.apache.hadoop.ozone.s3;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.core.MultivaluedHashMap;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.UriInfo;
import java.io.IOException;
-import java.net.URI;
-import java.util.Arrays;
-import java.util.Collection;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.om.OMConfigKeys;
-import org.apache.hadoop.ozone.s3.signature.AWSSignatureProcessor;
-import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
-import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
-
-import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.MALFORMED_HEADER;
-import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.S3_AUTHINFO_CREATION_ERROR;
-import static
org.apache.hadoop.ozone.s3.signature.SignatureParser.AUTHORIZATION_HEADER;
-import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.CONTENT_MD5;
-import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.CONTENT_TYPE;
-import static
org.apache.hadoop.ozone.s3.signature.SignatureProcessor.HOST_HEADER;
-import static
org.apache.hadoop.ozone.s3.signature.StringToSignProducer.X_AMAZ_DATE;
-import static
org.apache.hadoop.ozone.s3.signature.StringToSignProducer.X_AMZ_CONTENT_SHA256;
import static org.junit.Assert.fail;
import org.junit.Assert;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.mockito.Mockito;
/**
* Test class for @{@link OzoneClientProducer}.
*/
-@RunWith(Parameterized.class)
public class TestOzoneClientProducer {
private OzoneClientProducer producer;
- private MultivaluedMap<String, String> headerMap;
- private MultivaluedMap<String, String> queryMap;
- private MultivaluedMap<String, String> pathParamsMap;
- private String authHeader;
- private String contentMd5;
- private String host;
- private String amzContentSha256;
- private String date;
- private String contentType;
- private ContainerRequestContext context;
- private UriInfo uriInfo;
- public TestOzoneClientProducer(
- String authHeader, String contentMd5,
- String host, String amzContentSha256, String date, String contentType
- )
+ public TestOzoneClientProducer()
throws Exception {
- this.authHeader = authHeader;
- this.contentMd5 = contentMd5;
- this.host = host;
- this.amzContentSha256 = amzContentSha256;
- this.date = date;
- this.contentType = contentType;
producer = new OzoneClientProducer();
- headerMap = new MultivaluedHashMap<>();
- queryMap = new MultivaluedHashMap<>();
- pathParamsMap = new MultivaluedHashMap<>();
- uriInfo = Mockito.mock(UriInfo.class);
- context = Mockito.mock(ContainerRequestContext.class);
OzoneConfiguration config = new OzoneConfiguration();
config.setBoolean(OzoneConfigKeys.OZONE_SECURITY_ENABLED_KEY, true);
config.set(OMConfigKeys.OZONE_OM_ADDRESS_KEY, "");
- setupContext();
producer.setOzoneConfiguration(config);
}
- @Parameterized.Parameters
- public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] {
- {
- "AWS4-HMAC-SHA256 Credential=testuser1/20190221/us-west-1/s3" +
- "/aws4_request, SignedHeaders=content-md5;host;" +
- "x-amz-content-sha256;x-amz-date, " +
- "Signature" +
- "=56ec73ba1974f8feda8365c3caef89c5d4a688d5f9baccf47" +
- "65f46a14cd745ad",
- "Zi68x2nPDDXv5qfDC+ZWTg==",
- "s3g:9878",
- "e2bd43f11c97cde3465e0e8d1aad77af7ec7aa2ed8e213cd0e24" +
- "1e28375860c6",
- "20190221T002037Z",
- ""
- },
- {
- "AWS4-HMAC-SHA256 " +
- "Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request," +
- " SignedHeaders=content-type;host;x-amz-date, " +
- "Signature=" +
- "5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400" +
- "e06b5924a6f2b5d7",
- "",
- "iam.amazonaws.com",
- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
- "20150830T123600Z",
- "application/x-www-form-urlencoded; charset=utf-8"
- },
- {
- null, null, null, null, null, null
- },
- {
- "", null, null, null, null, null
- },
- // AWS V2 signature
- {
- "AWS AKIDEXAMPLE:St7bHPOdkmsX/GITGe98rOQiUCg=",
- "",
- "s3g:9878",
- "",
- "Wed, 22 Mar 2023 17:00:06 +0000",
- "application/octet-stream"
- }
- });
- }
-
@Test
public void testGetClientFailure() {
try {
@@ -152,40 +54,6 @@ public class TestOzoneClientProducer {
}
}
- @Test
- public void testGetSignature() {
- try {
- System.err.println("Testing: " + authHeader);
- OzoneConfiguration configuration = new OzoneConfiguration();
- configuration.set(OMConfigKeys.OZONE_OM_SERVICE_IDS_KEY, "ozone1");
- configuration.set(OMConfigKeys.OZONE_OM_ADDRESS_KEY, "ozone1addr:9399");
- producer.setOzoneConfiguration(configuration);
- producer.getSignature();
- if ("".equals(authHeader)) {
- fail("Empty AuthHeader must fail");
- }
- } catch (WebApplicationException ex) {
- if (authHeader == null || authHeader.isEmpty() ||
- authHeader.startsWith("AWS ")) {
- // Empty auth header and unsupported AWS signature
- // should fail with Invalid Request.
- Assert.assertEquals(HTTP_FORBIDDEN, ex.getResponse().getStatus());
- Assert.assertEquals(S3_AUTHINFO_CREATION_ERROR.getErrorMessage(),
- ex.getMessage());
- } else {
- // Other requests have stale timestamp and
- // should fail with Malformed Authorization Header.
- Assert.assertEquals(HTTP_BAD_REQUEST, ex.getResponse().getStatus());
- Assert.assertEquals(MALFORMED_HEADER.getErrorMessage(),
- ex.getMessage());
-
- }
-
- } catch (Exception ex) {
- fail("Unexpected exception: " + ex);
- }
- }
-
@Test
public void testGetClientFailureWithMultipleServiceIds() {
try {
@@ -219,30 +87,4 @@ public class TestOzoneClientProducer {
}
}
- private void setupContext() throws Exception {
- headerMap.putSingle(AUTHORIZATION_HEADER, authHeader);
- headerMap.putSingle(CONTENT_MD5, contentMd5);
- headerMap.putSingle(HOST_HEADER, host);
- headerMap.putSingle(X_AMZ_CONTENT_SHA256, amzContentSha256);
- headerMap.putSingle(X_AMAZ_DATE, date);
- headerMap.putSingle(CONTENT_TYPE, contentType);
-
- Mockito.when(uriInfo.getQueryParameters()).thenReturn(queryMap);
- Mockito.when(uriInfo.getRequestUri()).thenReturn(new URI(""));
-
- Mockito.when(context.getUriInfo()).thenReturn(uriInfo);
- Mockito.when(context.getHeaders()).thenReturn(headerMap);
- Mockito.when(context.getHeaderString(AUTHORIZATION_HEADER))
- .thenReturn(authHeader);
- Mockito.when(context.getUriInfo().getQueryParameters())
- .thenReturn(queryMap);
- Mockito.when(context.getUriInfo().getPathParameters())
- .thenReturn(pathParamsMap);
-
- AWSSignatureProcessor awsSignatureProcessor = new AWSSignatureProcessor();
- awsSignatureProcessor.setContext(context);
-
- producer.setSignatureParser(awsSignatureProcessor);
- }
-
}
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java
index 19d9380de9..81b8e0c503 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java
@@ -105,8 +105,7 @@ public class TestVirtualHostStyleFilter {
ContainerRequest containerRequest = createContainerRequest("mybucket" +
".localhost:9878", "/myfile", null, true);
virtualHostStyleFilter.filter(containerRequest);
- URI expected = new URI("http://" + s3HttpAddr +
- "/mybucket/myfile");
+ URI expected = new URI("http://" + s3HttpAddr + "/mybucket/myfile");
Assert.assertEquals(expected, containerRequest.getRequestUri());
}
@@ -126,6 +125,29 @@ public class TestVirtualHostStyleFilter {
}
+ @Test
+ public void testS3SecretEndpoint() throws Exception {
+
+ VirtualHostStyleFilter virtualHostStyleFilter =
+ new VirtualHostStyleFilter();
+ virtualHostStyleFilter.setConfiguration(conf);
+
+ ContainerRequest containerRequest = createContainerRequest("mybucket" +
+ ".localhost:9878", "/secret/generate",
+ null, true);
+ virtualHostStyleFilter.filter(containerRequest);
+ URI expected = new URI("http://" + s3HttpAddr + "/secret/generate");
+ Assert.assertEquals(expected, containerRequest.getRequestUri());
+
+ containerRequest = createContainerRequest("mybucket" +
+ ".localhost:9878", "/secret/revoke",
+ null, true);
+ virtualHostStyleFilter.filter(containerRequest);
+ expected = new URI("http://" + s3HttpAddr + "/secret/revoke");
+ Assert.assertEquals(expected, containerRequest.getRequestUri());
+
+ }
+
@Test
public void testVirtualHostStyleWithCreateBucketRequest() throws Exception {
@@ -141,17 +163,29 @@ public class TestVirtualHostStyleFilter {
}
+ @Test
+ public void testVirtualHostStyleWithCreateKeyRequest() throws Exception {
+ VirtualHostStyleFilter virtualHostStyleFilter =
+ new VirtualHostStyleFilter();
+ virtualHostStyleFilter.setConfiguration(conf);
+
+ ContainerRequest containerRequest = createContainerRequest("mybucket" +
+ ".localhost:9878", "/key1", null, true);
+ virtualHostStyleFilter.filter(containerRequest);
+ URI expected = new URI("http://" + s3HttpAddr + "/mybucket/key1");
+ Assert.assertEquals(expected, containerRequest.getRequestUri());
+ }
+
@Test
public void testVirtualHostStyleWithQueryParams() throws Exception {
VirtualHostStyleFilter virtualHostStyleFilter =
new VirtualHostStyleFilter();
virtualHostStyleFilter.setConfiguration(conf);
-
+ URI expected = new URI("http://" + s3HttpAddr + "/mybucket?prefix=bh");
ContainerRequest containerRequest = createContainerRequest("mybucket" +
".localhost:9878", null, "?prefix=bh", true);
virtualHostStyleFilter.filter(containerRequest);
- URI expected = new URI("http://" + s3HttpAddr + "/mybucket?prefix=bh");
assertTrue(expected.toString().contains(containerRequest.getRequestUri()
.toString()));
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java
index 7221dfbb02..181e962e5c 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestAuthorizationV4QueryParser.java
@@ -284,14 +284,14 @@ public class TestAuthorizationV4QueryParser {
};
final SignatureInfo signatureInfo = parser.parseSignature();
+ signatureInfo.setUnfilteredURI("/test.txt");
LowerCaseKeyStringMap headers = new LowerCaseKeyStringMap();
headers.put("host", "localhost");
final String stringToSign =
StringToSignProducer.createSignatureBase(signatureInfo, "https", "GET",
- "/test.txt", headers,
- queryParams);
+ headers, queryParams);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(canonicalRequest.getBytes(StandardCharsets.UTF_8));
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
index 9d52172179..14aa649476 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/signature/TestStringToSignProducer.java
@@ -95,6 +95,7 @@ public class TestStringToSignProducer {
//NOOP
}
}.parseSignature();
+ signatureInfo.setUnfilteredURI("/buckets");
headers.fixContentType();
@@ -103,7 +104,6 @@ public class TestStringToSignProducer {
signatureInfo,
"http",
"GET",
- "/buckets",
headers,
queryParameters);
@@ -203,6 +203,7 @@ public class TestStringToSignProducer {
SignatureInfo signatureInfo = new AuthorizationV4HeaderParser(
headerMap.getFirst("Authorization"),
headerMap.getFirst("X-Amz-Date")).parseSignature();
+ signatureInfo.setUnfilteredURI("/");
try {
StringToSignProducer.createSignatureBase(signatureInfo, context);
} catch (OS3Exception e) {
@@ -260,6 +261,7 @@ public class TestStringToSignProducer {
SignatureInfo signatureInfo = new AuthorizationV4HeaderParser(
headerMap.getFirst("Authorization"),
headerMap.getFirst("x-amz-date")).parseSignature();
+ signatureInfo.setUnfilteredURI("/");
try {
StringToSignProducer.createSignatureBase(signatureInfo, context);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]