Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions hadoop-ozone/dist/src/main/compose/common/init-kdc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export_keytab testuser/om testuser
export_keytab testuser/recon testuser
export_keytab testuser/s3g testuser
export_keytab testuser/scm testuser
export_keytab svc-iceberg-rest-catalog/s3g svc-iceberg-rest-catalog
export_keytab svc-iceberg-userA/s3g svc-iceberg-userA
export_keytab svc-iceberg-userB/s3g svc-iceberg-userB

export_keytab testuser2/dn testuser2
export_keytab testuser2/httpfs testuser2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ OZONE-SITE.XML_ozone.security.http.kerberos.enabled=true
OZONE-SITE.XML_ozone.s3g.secret.http.enabled=true
OZONE-SITE.XML_ozone.http.filter.initializers=org.apache.hadoop.security.AuthenticationFilterInitializer

# Enable S3 Gateway STS (AWS STS compatible) endpoint on s3g (http://s3g:9880/sts)
OZONE-SITE.XML_ozone.s3g.sts.http.enabled=true

OZONE-SITE.XML_ozone.om.http.auth.type=kerberos
OZONE-SITE.XML_hdds.scm.http.auth.type=kerberos
OZONE-SITE.XML_hdds.datanode.http.auth.type=kerberos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ execute_robot_test s3g freon/generate.robot
execute_robot_test s3g freon/validate.robot

execute_robot_test s3g -v RANGER_ENDPOINT_URL:"http://ranger:6080" -v USER:hdfs security/ozone-secure-tenant.robot
execute_robot_test s3g -v RANGER_ENDPOINT_URL:"http://ranger:6080" -v USER:hdfs security/ozone-secure-sts.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# 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 ***
Library OperatingSystem
Library String
Library BuiltIn
Library DateTime
Library Collections
Resource ../commonlib.robot
Resource ../s3/commonawslib.robot

*** Variables ***
${RANGER_ENDPOINT_URL} ${EMPTY}
${STS_ENDPOINT_URL} http://s3g:9880/sts
${S3G_ENDPOINT_URL} http://s3g:9878
${ROLE_SESSION_NAME} sts-session-name

*** Keywords ***
Configure AWS Profile
[Arguments] ${profile} ${access_key} ${secret_key} ${session_token}=${EMPTY} ${region}=us-east-1
Run Keyword Install aws cli
# Use v4 signatures so presign URL will work (Ozone rejects v2 signatures)
Execute aws configure set s3.signature_version s3v4 --profile ${profile}
Execute aws configure set aws_access_key_id ${access_key} --profile ${profile}
Execute aws configure set aws_secret_access_key ${secret_key} --profile ${profile}
Execute aws configure set region ${region} --profile ${profile}
Run Keyword If '${session_token}' != '${EMPTY}' Execute aws configure set aws_session_token ${session_token} --profile ${profile}

Configure STS Profile
[Arguments] ${access_key} ${secret_key} ${session_token}
Configure AWS Profile sts ${access_key} ${secret_key} ${session_token}

Create Ranger Artifact
[Arguments] ${json} ${endpoint_url}
Pass Execution If '${RANGER_ENDPOINT_URL}' == '' No Ranger
${result} = Execute curl --fail --include --location --netrc --request POST --header "Content-Type: application/json" --header "accept: application/json" --data '${json}' '${endpoint_url}'
Should Contain ${result} HTTP/1.1 200

Create Ranger User
[Arguments] ${user_json}
# Note: the /service/xusers/secure/users endpoint must be used below so that the userPermList can be set. Without
# the userPermList being set, the user cannot be added to a Ranger policy.
Create Ranger Artifact ${user_json} '${RANGER_ENDPOINT_URL}/service/xusers/secure/users'

Create Ranger Role
[Arguments] ${role_json}
Create Ranger Artifact ${role_json} '${RANGER_ENDPOINT_URL}/service/roles/roles'

Create Ranger Policy
[Arguments] ${policy_json}
Create Ranger Artifact ${policy_json} '${RANGER_ENDPOINT_URL}/service/public/v2/api/policy'

Assume Role And Get Temporary Credentials
[Arguments] ${perm_access_key_id} ${perm_secret_key} ${policy_json}=${EMPTY} ${role_arn}=${ROLE_ARN_OBS}
Configure AWS Profile permanent ${perm_access_key_id} ${perm_secret_key}

${now} = Get Current Date time_zone=UTC

${cmd} = Set Variable aws sts assume-role --endpoint-url ${STS_ENDPOINT_URL} --role-arn ${role_arn} --role-session-name ${ROLE_SESSION_NAME} --duration-seconds 900 --output json --profile permanent
${cmd} = Set Variable If '${policy_json}' != '${EMPTY}' ${cmd} --policy '${policy_json}' ${cmd}

${json} = Execute ${cmd}
Should Contain ${json} Credentials

${stsAccessKeyId} = Execute echo '${json}' | jq -r '.Credentials.AccessKeyId'
${stsSecretKey} = Execute echo '${json}' | jq -r '.Credentials.SecretAccessKey'
${stsSessionToken} = Execute echo '${json}' | jq -r '.Credentials.SessionToken'
Should Start With ${stsAccessKeyId} ASIA
Set Global Variable ${STS_ACCESS_KEY_ID} ${stsAccessKeyId}
Set Global Variable ${STS_SECRET_KEY} ${stsSecretKey}
Set Global Variable ${STS_SESSION_TOKEN} ${stsSessionToken}

# Verify Expiration is at least 900 seconds in the future, but allow 2 second grace on both sides for clock skew
${expiration} = Execute echo '${json}' | jq -r '.Credentials.Expiration'
${time_diff} = Subtract Date From Date ${expiration} ${now}
Should Be True ${time_diff} >= 898 Expected expiration to be at least 898s in the future, but was ${time_diff}s
Should Be True ${time_diff} <= 902 Expected expiration to be at most 902s in the future, but was ${time_diff}s

Assume Role Should Fail
[Arguments] ${perm_access_key_id} ${perm_secret_key} ${policy_json}=${EMPTY} ${expected_error}=AccessDenied ${expected_http_code}=${EMPTY} ${role_arn}=${ROLE_ARN_OBS}
Configure AWS Profile permanent ${perm_access_key_id} ${perm_secret_key}

IF '${expected_http_code}' != '${EMPTY}'
# Note: curl in the s3g container doesn't reliably support --aws-sigv4,
# so use awscli debug output to capture the HTTP response code.
${cmd} = Set Variable aws sts assume-role --endpoint-url ${STS_ENDPOINT_URL} --role-arn ${role_arn} --role-session-name ${ROLE_SESSION_NAME} --duration-seconds 900 --profile permanent --debug 2>&1
${cmd} = Set Variable If '${policy_json}' != '${EMPTY}' ${cmd} --policy '${policy_json}' ${cmd}

${output} = Execute And Ignore Error ${cmd}
Should Contain ${output} ${expected_error}

@{http_codes} = Get Regexp Matches ${output} (?m)^.*"POST .*" ([0-9]{3}) .* 1
${code_count} = Get Length ${http_codes}
Should Be True ${code_count} > 0 Expected to find an HTTP status code in awscli --debug output, but none was found.
${http_code} = Get From List ${http_codes} -1
Should Be Equal As Strings ${http_code} ${expected_http_code}
ELSE
${cmd} = Set Variable aws sts assume-role --endpoint-url ${STS_ENDPOINT_URL} --role-arn ${role_arn} --role-session-name ${ROLE_SESSION_NAME} --duration-seconds 900 --output json --profile permanent
${cmd} = Set Variable If '${policy_json}' != '${EMPTY}' ${cmd} --policy '${policy_json}' ${cmd}

${output} = Execute And Ignore Error ${cmd}
Should Contain ${output} ${expected_error}
END

Get Object Should Succeed
[Arguments] ${bucket} ${key} ${destination}=${TEMP_DIR}/${key} ${profile}=sts
${dest_parent_dir} = Evaluate __import__('os').path.dirname($destination)
Run Keyword If '${dest_parent_dir}' != '' Execute mkdir -p ${dest_parent_dir}
${output} = Execute And Ignore Error aws s3api --endpoint-url ${S3G_ENDPOINT_URL} get-object --bucket ${bucket} --key ${key} ${destination} --profile ${profile}
Should Contain ${output} "AcceptRanges": "bytes"

Get Object Should Fail
[Arguments] ${bucket} ${key} ${expectedFailureMessage} ${destination}=${TEMP_DIR}/${key} ${profile}=sts
${dest_parent_dir} = Evaluate __import__('os').path.dirname($destination)
Run Keyword If '${dest_parent_dir}' != '' Execute mkdir -p ${dest_parent_dir}
${output} = Execute And Ignore Error aws s3api --endpoint-url ${S3G_ENDPOINT_URL} get-object --bucket ${bucket} --key ${key} ${destination} --profile ${profile}
Should Contain ${output} ${expectedFailureMessage}

Put Object Should Succeed
[Arguments] ${bucket} ${key} ${body}=${TEMP_DIR}/${key} ${profile}=sts
${output} = Execute And Ignore Error aws s3api --endpoint-url ${S3G_ENDPOINT_URL} put-object --bucket ${bucket} --key ${key} --body ${body} --profile ${profile}
Should Contain ${output} "ETag"

Put Object Should Fail
[Arguments] ${bucket} ${key} ${expectedFailureMessage} ${body}=${TEMP_DIR}/${key} ${profile}=sts
${output} = Execute And Ignore Error aws s3api --endpoint-url ${S3G_ENDPOINT_URL} put-object --bucket ${bucket} --key ${key} --body ${body} --profile ${profile}
Should Contain ${output} ${expectedFailureMessage}

Create Bucket Should Fail
[Arguments] ${bucket} ${expectedFailureMessage}=AccessDenied ${profile}=sts
${output} = Execute And Ignore Error aws s3api --endpoint-url ${S3G_ENDPOINT_URL} create-bucket --bucket ${bucket} --profile ${profile}
Should Contain ${output} ${expectedFailureMessage}

List Object Keys Should Succeed
[Arguments] ${bucket} ${api}=list-objects ${request_prefix}=${EMPTY} ${profile}=sts ${delimiter}=${EMPTY}
${cmd} = Set Variable aws s3api --endpoint-url ${S3G_ENDPOINT_URL} ${api} --bucket ${bucket} --output json --profile ${profile}
${cmd} = Set Variable If '${request_prefix}' != '${EMPTY}' ${cmd} --prefix ${request_prefix} ${cmd}
${cmd} = Set Variable If '${delimiter}' != '${EMPTY}' ${cmd} --delimiter '${delimiter}' ${cmd}
${output} = Execute And Ignore Error ${cmd}
Should Not Contain ${output} AccessDenied
${keys_json} = Execute echo '${output}' | jq -c '[(.Contents // [])[] | .Key] | sort'
[Return] ${keys_json}

List Object Keys Should Fail
[Arguments] ${bucket} ${api}=list-objects ${expected_failure}=AccessDenied ${request_prefix}=${EMPTY} ${profile}=sts ${delimiter}=${EMPTY}
${cmd} = Set Variable aws s3api --endpoint-url ${S3G_ENDPOINT_URL} ${api} --bucket ${bucket} --output json --profile ${profile}
${cmd} = Set Variable If '${request_prefix}' != '${EMPTY}' ${cmd} --prefix ${request_prefix} ${cmd}
${cmd} = Set Variable If '${delimiter}' != '${EMPTY}' ${cmd} --delimiter '${delimiter}' ${cmd}
${output} = Execute And Ignore Error ${cmd}
Should Contain ${output} ${expected_failure}

Assert Listed Keys Json Should Equal
[Arguments] ${actual_keys_json} ${expected_keys_json}
${actual_list} = Evaluate json.loads($actual_keys_json) modules=json
${expected_list} = Evaluate json.loads($expected_keys_json) modules=json
Lists Should Be Equal ${actual_list} ${expected_list}

Assert Listed Keys Should Equal
[Arguments] ${actual_keys_json} @{expected_keys}
${expected_sorted} = Copy List ${expected_keys}
Sort List ${expected_sorted}
${actual_list} = Evaluate json.loads($actual_keys_json) modules=json
Lists Should Be Equal ${actual_list} ${expected_sorted}
Loading