Merge pull request #587 from versity/fix/worm-admin-users

WORM protection root/admin users
This commit is contained in:
Ben McClelland
2024-05-23 20:34:32 -07:00
committed by GitHub
5 changed files with 71 additions and 155 deletions
+19 -23
View File
@@ -135,7 +135,7 @@ func ParseObjectLegalHoldOutput(status *bool) *types.ObjectLockLegalHold {
}
}
func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, isAdminOrRoot bool, be backend.Backend) error {
func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects []string, be backend.Backend) error {
data, err := be.GetObjectLockConfiguration(ctx, bucket)
if err != nil {
if errors.Is(err, s3err.GetAPIError(s3err.ErrObjectLockConfigurationNotFound)) {
@@ -180,18 +180,16 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
if retention.RetainUntilDate.After(time.Now()) {
switch retention.Mode {
case types.ObjectLockRetentionModeGovernance:
if !isAdminOrRoot {
policy, err := be.GetBucketPolicy(ctx, bucket)
if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
if err != nil {
return err
}
err = verifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction)
if err != nil {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
policy, err := be.GetBucketPolicy(ctx, bucket)
if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchBucketPolicy)) {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
if err != nil {
return err
}
err = verifyBucketPolicy(policy, userAccess, bucket, obj, BypassGovernanceRetentionAction)
if err != nil {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
case types.ObjectLockRetentionModeCompliance:
return s3err.GetAPIError(s3err.ErrObjectLocked)
@@ -208,7 +206,7 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
return err
}
if *status && !isAdminOrRoot {
if *status {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
}
@@ -225,15 +223,13 @@ func CheckObjectAccess(ctx context.Context, bucket, userAccess string, objects [
if expirationDate.After(time.Now()) {
switch bucketLockConfig.DefaultRetention.Mode {
case types.ObjectLockRetentionModeGovernance:
if !isAdminOrRoot {
policy, err := be.GetBucketPolicy(ctx, bucket)
if err != nil {
return err
}
err = verifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction)
if err != nil {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
policy, err := be.GetBucketPolicy(ctx, bucket)
if err != nil {
return err
}
err = verifyBucketPolicy(policy, userAccess, bucket, "", BypassGovernanceRetentionAction)
if err != nil {
return s3err.GetAPIError(s3err.ErrObjectLocked)
}
case types.ObjectLockRetentionModeCompliance:
return s3err.GetAPIError(s3err.ErrObjectLocked)
+3 -3
View File
@@ -1797,7 +1797,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
})
}
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, isRoot || acct.Role == auth.RoleAdmin, c.be)
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{keyStart}, c.be)
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
@@ -2002,7 +2002,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
})
}
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), isRoot || acct.Role == auth.RoleAdmin, c.be)
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, utils.ParseDeleteObjects(dObj.Objects), c.be)
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
@@ -2137,7 +2137,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
})
}
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, isRoot || acct.Role == auth.RoleAdmin, c.be)
err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []string{key}, c.be)
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
-4
View File
@@ -372,9 +372,7 @@ func TestGetObjectLegalHold(s *S3Conf) {
func TestWORMProtection(s *S3Conf) {
WORMProtection_bucket_object_lock_configuration_compliance_mode(s)
WORMProtection_bucket_object_lock_governance_root_overwrite(s)
WORMProtection_object_lock_retention_compliance_root_access_denied(s)
WORMProtection_object_lock_retention_governance_root_overwrite(s)
WORMProtection_object_lock_retention_governance_user_access_denied(s)
WORMProtection_object_lock_legal_hold_user_access_denied(s)
WORMProtection_object_lock_legal_hold_root_overwrite(s)
@@ -690,9 +688,7 @@ func GetIntTests() IntTests {
"GetObjectLegalHold_unset_config": GetObjectLegalHold_unset_config,
"GetObjectLegalHold_success": GetObjectLegalHold_success,
"WORMProtection_bucket_object_lock_configuration_compliance_mode": WORMProtection_bucket_object_lock_configuration_compliance_mode,
"WORMProtection_bucket_object_lock_governance_root_overwrite": WORMProtection_bucket_object_lock_governance_root_overwrite,
"WORMProtection_object_lock_retention_compliance_root_access_denied": WORMProtection_object_lock_retention_compliance_root_access_denied,
"WORMProtection_object_lock_retention_governance_root_overwrite": WORMProtection_object_lock_retention_governance_root_overwrite,
"WORMProtection_object_lock_retention_governance_user_access_denied": WORMProtection_object_lock_retention_governance_user_access_denied,
"WORMProtection_object_lock_legal_hold_user_access_denied": WORMProtection_object_lock_legal_hold_user_access_denied,
"WORMProtection_object_lock_legal_hold_root_overwrite": WORMProtection_object_lock_legal_hold_root_overwrite,
+6 -82
View File
@@ -4465,6 +4465,10 @@ func CreateMultipartUpload_with_object_lock(s *S3Conf) error {
return fmt.Errorf("expected uploaded object lock mode to be %v, instead got %v", types.ObjectLockModeGovernance, resp.ObjectLockMode)
}
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
return err
}
return nil
}, withLock())
}
@@ -7682,47 +7686,6 @@ func WORMProtection_bucket_object_lock_configuration_compliance_mode(s *S3Conf)
}, withLock())
}
func WORMProtection_bucket_object_lock_governance_root_overwrite(s *S3Conf) error {
testName := "WORMProtection_bucket_object_lock_governance_root_overwrite"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
var days int32 = 10
object := "my-obj"
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{
Bucket: &bucket,
ObjectLockConfiguration: &types.ObjectLockConfiguration{
ObjectLockEnabled: types.ObjectLockEnabledEnabled,
Rule: &types.ObjectLockRule{
DefaultRetention: &types.DefaultRetention{
Mode: types.ObjectLockRetentionModeGovernance,
Days: &days,
},
},
},
})
cancel()
if err != nil {
return err
}
// create an object
if err := putObjects(s3client, []string{object}, bucket); err != nil {
return err
}
// overwrite the object
if err := putObjects(s3client, []string{object}, bucket); err != nil {
return err
}
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
return err
}
return nil
}, withLock())
}
func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Conf) error {
testName := "WORMProtection_object_lock_retention_compliance_root_access_denied"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
@@ -7762,46 +7725,6 @@ func WORMProtection_object_lock_retention_compliance_root_access_denied(s *S3Con
}, withLock())
}
func WORMProtection_object_lock_retention_governance_root_overwrite(s *S3Conf) error {
testName := "WORMProtection_object_lock_retention_governance_root_overwrite"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
if err := changeBucketObjectLockStatus(s3client, bucket, true); err != nil {
return err
}
object := "my-obj"
if err := putObjects(s3client, []string{object}, bucket); err != nil {
return err
}
date := time.Now().Add(time.Hour * 3)
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
_, err := s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
Bucket: &bucket,
Key: &object,
Retention: &types.ObjectLockRetention{
Mode: types.ObjectLockRetentionModeGovernance,
RetainUntilDate: &date,
},
})
cancel()
if err != nil {
return err
}
if err := putObjects(s3client, []string{object}, bucket); err != nil {
return err
}
if err := changeBucketObjectLockStatus(s3client, bucket, false); err != nil {
return err
}
return nil
}, withLock())
}
func WORMProtection_object_lock_retention_governance_user_access_denied(s *S3Conf) error {
testName := "WORMProtection_object_lock_retention_governance_user_access_denied"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
@@ -7940,7 +7863,8 @@ func WORMProtection_object_lock_legal_hold_root_overwrite(s *S3Conf) error {
return err
}
if err := putObjects(s3client, []string{object}, bucket); err != nil {
err = putObjects(s3client, []string{object}, bucket)
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLocked)); err != nil {
return err
}
+43 -43
View File
@@ -226,49 +226,49 @@ source ./tests/commands/select_object_content.sh
delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
}
@test "test_get_put_object_retention" {
# bucket must be created with lock for legal hold
if [[ $RECREATE_BUCKETS == false ]]; then
return
fi
bucket_file="bucket_file"
username="ABCDEFG"
secret_key="HIJKLMN"
legal_hold_retention_setup "$username" "$secret_key" "$bucket_file"
get_object_lock_configuration "$BUCKET_ONE_NAME" || fail "error getting lock configuration"
log 5 "$lock_config"
enabled=$(echo "$lock_config" | jq -r ".ObjectLockConfiguration.ObjectLockEnabled")
[[ $enabled == "Enabled" ]] || fail "ObjectLockEnabled should be 'Enabled', is '$enabled'"
if [[ "$OSTYPE" == "darwin"* ]]; then
retention_date=$(date -v+2d +"%Y-%m-%dT%H:%M:%S")
else
retention_date=$(date -d "+2 days" +"%Y-%m-%dT%H:%M:%S")
fi
put_object_retention "$BUCKET_ONE_NAME" "$bucket_file" "GOVERNANCE" "$retention_date" || fail "failed to add object retention"
get_object_retention "$BUCKET_ONE_NAME" "$bucket_file" || fail "failed to get object retention"
log 5 "$retention"
retention=$(echo "$retention" | grep -v "InsecureRequestWarning")
mode=$(echo "$retention" | jq -r ".Retention.Mode")
retain_until_date=$(echo "$retention" | jq -r ".Retention.RetainUntilDate")
[[ $mode == "GOVERNANCE" ]] || fail "retention mode should be governance, is $mode"
[[ $retain_until_date == "$retention_date"* ]] || fail "retain until date should be $retention_date, is $retain_until_date"
echo "fdkljafajkfs" > "$test_file_folder/$bucket_file"
put_object_with_user "s3api" "$test_file_folder/$bucket_file" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local put_result=$?
[[ $put_result -ne 0 ]] || fail "able to overwrite object with hold"
[[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
delete_object_with_user "s3api" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local delete_result=$?
[[ $delete_result -ne 0 ]] || fail "able to delete object with hold"
[[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
delete_object "s3api" "$BUCKET_ONE_NAME" "$bucket_file" || fail "error deleting object"
delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
}
#@test "test_get_put_object_retention" {
# # bucket must be created with lock for legal hold
# if [[ $RECREATE_BUCKETS == false ]]; then
# return
# fi
#
# bucket_file="bucket_file"
# username="ABCDEFG"
# secret_key="HIJKLMN"
#
# legal_hold_retention_setup "$username" "$secret_key" "$bucket_file"
#
# get_object_lock_configuration "$BUCKET_ONE_NAME" || fail "error getting lock configuration"
# log 5 "$lock_config"
# enabled=$(echo "$lock_config" | jq -r ".ObjectLockConfiguration.ObjectLockEnabled")
# [[ $enabled == "Enabled" ]] || fail "ObjectLockEnabled should be 'Enabled', is '$enabled'"
#
# if [[ "$OSTYPE" == "darwin"* ]]; then
# retention_date=$(date -v+2d +"%Y-%m-%dT%H:%M:%S")
# else
# retention_date=$(date -d "+2 days" +"%Y-%m-%dT%H:%M:%S")
# fi
# put_object_retention "$BUCKET_ONE_NAME" "$bucket_file" "GOVERNANCE" "$retention_date" || fail "failed to add object retention"
# get_object_retention "$BUCKET_ONE_NAME" "$bucket_file" || fail "failed to get object retention"
# log 5 "$retention"
# retention=$(echo "$retention" | grep -v "InsecureRequestWarning")
# mode=$(echo "$retention" | jq -r ".Retention.Mode")
# retain_until_date=$(echo "$retention" | jq -r ".Retention.RetainUntilDate")
# [[ $mode == "GOVERNANCE" ]] || fail "retention mode should be governance, is $mode"
# [[ $retain_until_date == "$retention_date"* ]] || fail "retain until date should be $retention_date, is $retain_until_date"
#
# echo "fdkljafajkfs" > "$test_file_folder/$bucket_file"
# put_object_with_user "s3api" "$test_file_folder/$bucket_file" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local put_result=$?
# [[ $put_result -ne 0 ]] || fail "able to overwrite object with hold"
# [[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
#
# delete_object_with_user "s3api" "$BUCKET_ONE_NAME" "$bucket_file" "$username" "$secret_key" || local delete_result=$?
# [[ $delete_result -ne 0 ]] || fail "able to delete object with hold"
# [[ $error == *"Object is WORM protected and cannot be overwritten"* ]] || fail "unexpected error message: $error"
#
# delete_object "s3api" "$BUCKET_ONE_NAME" "$bucket_file" || fail "error deleting object"
# delete_bucket_recursive "s3api" "$BUCKET_ONE_NAME"
#}
legal_hold_retention_setup() {
if [[ $# -ne 3 ]]; then