test: validate ipv6 compatibility (#50)
* test: validate ipv6 compatability * test: update test to include checking against SBC state
This commit is contained in:
1 parent
5f96422fbf
commit
7caff84c06
5 files changed
+239
-25
No files matched your search
+112
-7
@@ -17,12 +17,16 @@ import (
|
||||
"github.com/hashicorp/boundary-plugin-aws/plugin/service/storage"
|
||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
// NOTE: this test does not vaildate against dynamic credentials because
|
||||
// this credential type can only be tested on an EC2 instance.
|
||||
func TestStoragePlugin(t *testing.T) {
|
||||
region := os.Getenv("AWS_REGION")
|
||||
if region == "" {
|
||||
@@ -75,6 +79,12 @@ func TestStoragePlugin(t *testing.T) {
|
||||
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
|
||||
require.NoError(err)
|
||||
|
||||
iamAccessKeyMissingDeleteObject, err := tf.GetOutputString("iam_access_key_missing_delete_obj")
|
||||
require.NoError(err)
|
||||
|
||||
iamSecretAccessKeyMissingDeleteObject, err := tf.GetOutputString("iam_secret_access_key_missing_delete_obj")
|
||||
require.NoError(err)
|
||||
|
||||
// Start the workflow now. Set up the storage bucket. Note that this
|
||||
// will cause the state to go out of drift above in the sense that
|
||||
// the access key ID/secret access key will no longer be valid. We
|
||||
@@ -121,7 +131,7 @@ func TestStoragePlugin(t *testing.T) {
|
||||
testPluginOnDeleteStorageBucket(ctx, t, p, bucketName, region, keyid, secret, true)
|
||||
|
||||
// ********************
|
||||
// * Object Methods: PutObject, GetObject, HeadObject
|
||||
// * Object Methods: PutObject, GetObject, HeadObject, DeleteObjects
|
||||
// ********************
|
||||
//
|
||||
// Reassign the keyid and secret first.
|
||||
@@ -132,11 +142,59 @@ func TestStoragePlugin(t *testing.T) {
|
||||
// * Validate Permissions
|
||||
// ********************
|
||||
//
|
||||
var expectedSBCState *pb.StorageBucketCredentialState
|
||||
|
||||
// Validate error is returned for missing get object permission
|
||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingGetObject, iamSecretAccessKeyMissingGetObject)
|
||||
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||
State: &pb.Permissions{
|
||||
Write: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_OK,
|
||||
},
|
||||
Read: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_ERROR,
|
||||
},
|
||||
Delete: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_OK,
|
||||
},
|
||||
},
|
||||
}
|
||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingGetObject, iamSecretAccessKeyMissingGetObject, expectedSBCState)
|
||||
|
||||
// Validate error is returned for missing put object permission
|
||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingPutObject, iamSecretAccessKeyMissingPutObject)
|
||||
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||
State: &pb.Permissions{
|
||||
Write: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_ERROR,
|
||||
},
|
||||
Read: &pb.Permission{
|
||||
// Since we cannot write an object to S3,
|
||||
// we cannot test against the ability to read from S3.
|
||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||
},
|
||||
Delete: &pb.Permission{
|
||||
// We can still try deleting an object that does
|
||||
// not exist in the S3 bucket to validate access.
|
||||
State: pb.StateType_STATE_TYPE_OK,
|
||||
},
|
||||
},
|
||||
}
|
||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingPutObject, iamSecretAccessKeyMissingPutObject, expectedSBCState)
|
||||
|
||||
// Validate error is returned for missing delete object permission
|
||||
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||
State: &pb.Permissions{
|
||||
Write: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_OK,
|
||||
},
|
||||
Read: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_OK,
|
||||
},
|
||||
Delete: &pb.Permission{
|
||||
State: pb.StateType_STATE_TYPE_ERROR,
|
||||
},
|
||||
},
|
||||
}
|
||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingDeleteObject, iamSecretAccessKeyMissingDeleteObject, expectedSBCState)
|
||||
}
|
||||
|
||||
func testPluginOnCreateStorageBucket(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) {
|
||||
@@ -289,10 +347,12 @@ func testPluginOnDeleteStorageBucket(ctx context.Context, t *testing.T, p *stora
|
||||
}
|
||||
}
|
||||
|
||||
func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) {
|
||||
func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, expectedState *pb.StorageBucketCredentialState) {
|
||||
t.Helper()
|
||||
t.Logf("testing ValidatePermissions")
|
||||
require := require.New(t)
|
||||
require, assert := require.New(t), assert.New(t)
|
||||
|
||||
require.NotNil(expectedState)
|
||||
|
||||
reqAttrs, err := structpb.NewStruct(map[string]any{
|
||||
credential.ConstRegion: region,
|
||||
@@ -315,6 +375,51 @@ func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage
|
||||
|
||||
_, err = p.ValidatePermissions(ctx, req)
|
||||
require.Error(err)
|
||||
|
||||
var actualSBCState *pb.StorageBucketCredentialState
|
||||
if st, ok := status.FromError(err); ok {
|
||||
for _, detail := range st.Details() {
|
||||
if statusDetail, ok := detail.(*pb.StorageBucketCredentialState); ok {
|
||||
actualSBCState = statusDetail
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.NotNil(actualSBCState)
|
||||
require.NotNil(actualSBCState.GetState())
|
||||
|
||||
expectedReadState := expectedState.GetState().GetRead()
|
||||
if expectedReadState != nil {
|
||||
actualReadState := actualSBCState.GetState().GetRead()
|
||||
require.NotNil(actualReadState)
|
||||
assert.Equal(expectedReadState.GetState(), actualReadState.GetState())
|
||||
assert.NotEmpty(actualReadState.GetCheckedAt())
|
||||
if expectedReadState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||
assert.NotEmpty(actualReadState.GetErrorDetails())
|
||||
}
|
||||
}
|
||||
|
||||
expectedWriteState := expectedState.GetState().GetWrite()
|
||||
if expectedWriteState != nil {
|
||||
actualWriteState := actualSBCState.GetState().GetWrite()
|
||||
require.NotNil(actualWriteState)
|
||||
assert.Equal(expectedWriteState.GetState(), actualWriteState.GetState())
|
||||
assert.NotEmpty(actualWriteState.GetCheckedAt())
|
||||
if expectedWriteState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||
assert.NotEmpty(actualWriteState.GetErrorDetails())
|
||||
}
|
||||
}
|
||||
|
||||
expectedDeleteState := expectedState.GetState().GetDelete()
|
||||
if expectedDeleteState != nil {
|
||||
actualDeleteState := actualSBCState.GetState().GetDelete()
|
||||
require.NotNil(actualDeleteState)
|
||||
assert.Equal(expectedDeleteState.GetState(), actualDeleteState.GetState())
|
||||
assert.NotEmpty(actualDeleteState.GetCheckedAt())
|
||||
if expectedDeleteState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||
assert.NotEmpty(actualDeleteState.GetErrorDetails())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) {
|
||||
@@ -369,7 +474,7 @@ func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.Stora
|
||||
|
||||
// Need to create a GRPC server to test GetObject
|
||||
t.Logf("testing GetObject")
|
||||
lis, err := net.Listen("tcp", "localhost:2030")
|
||||
lis, err := net.Listen("tcp", "[::1]:2030")
|
||||
require.NoError(err)
|
||||
grpcServer := grpc.NewServer()
|
||||
pb.RegisterStoragePluginServiceServer(grpcServer, &storage.StoragePlugin{})
|
||||
@@ -377,7 +482,7 @@ func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.Stora
|
||||
grpcServer.Serve(lis)
|
||||
}()
|
||||
|
||||
conn, err := grpc.Dial("localhost:2030", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
conn, err := grpc.Dial("[::1]:2030", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
require.NoError(err)
|
||||
defer conn.Close()
|
||||
client := pb.NewStoragePluginServiceClient(conn)
|
||||
|
||||
Vendored
+97
-12
@@ -32,7 +32,9 @@ resource "aws_iam_policy" "valid" {
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
"s3:GetObjectAttributes"
|
||||
"s3:DeleteObject",
|
||||
"s3:GetObjectAttributes",
|
||||
"s3:ListBucket"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||
@@ -79,7 +81,9 @@ resource "aws_iam_policy" "missing_put_obj" {
|
||||
{
|
||||
"Action": [
|
||||
"s3:GetObject",
|
||||
"s3:GetObjectAttributes"
|
||||
"s3:GetObjectAttributes",
|
||||
"s3:ListBucket",
|
||||
"s3:DeleteObject"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||
@@ -125,7 +129,7 @@ resource "aws_iam_policy" "missing_get_obj" {
|
||||
{
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObjectAttributes"
|
||||
"s3:DeleteObject"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||
@@ -141,18 +145,69 @@ resource "aws_iam_role_policy_attachment" "missing_get_obj" {
|
||||
policy_arn = aws_iam_policy.missing_get_obj.arn
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "missing_delete_obj" {
|
||||
name = "${random_id.prefix.dec}-missing-delete-obj"
|
||||
tags = local.tags
|
||||
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Action = "sts:AssumeRole"
|
||||
Effect = "Allow"
|
||||
Sid = ""
|
||||
Principal = {
|
||||
AWS: data.aws_caller_identity.current.user_id
|
||||
}
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
resource "aws_iam_policy" "missing_delete_obj" {
|
||||
name = "${random_id.prefix.dec}-missing-delete-obj"
|
||||
tags = local.tags
|
||||
|
||||
policy = <<EOT
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
"s3:GetObjectAttributes",
|
||||
"s3:ListBucket"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy_attachment" "missing_delete_obj" {
|
||||
role = aws_iam_role.missing_delete_obj.name
|
||||
policy_arn = aws_iam_policy.missing_delete_obj.arn
|
||||
}
|
||||
|
||||
# Static Credential Testing
|
||||
resource "random_id" "user_name" {
|
||||
count = var.iam_user_count
|
||||
prefix = random_id.prefix.dec
|
||||
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||
byte_length = 4
|
||||
}
|
||||
|
||||
resource "aws_iam_user" "test" {
|
||||
count = var.iam_user_count
|
||||
name = random_id.user_name[count.index].dec
|
||||
force_destroy = true
|
||||
tags = local.tags
|
||||
count = var.iam_user_count
|
||||
name = random_id.user_name[count.index].dec
|
||||
force_destroy = true
|
||||
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||
tags = {
|
||||
"boundary-demo" = local.hashicorp_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_access_key" "test" {
|
||||
@@ -195,17 +250,44 @@ resource "aws_iam_user_policy_attachment" "test_credentials" {
|
||||
policy_arn = aws_iam_policy.credentials[count.index].arn
|
||||
}
|
||||
|
||||
# Edge case for missing DeleteObject permission
|
||||
resource "random_id" "missing_delete_obj" {
|
||||
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||
byte_length = 4
|
||||
}
|
||||
|
||||
resource "aws_iam_user" "missing_delete_obj" {
|
||||
name = random_id.missing_delete_obj.dec
|
||||
force_destroy = true
|
||||
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||
tags = {
|
||||
"boundary-demo" = local.hashicorp_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_access_key" "missing_delete_obj" {
|
||||
user = aws_iam_user.missing_delete_obj.name
|
||||
}
|
||||
|
||||
resource "aws_iam_user_policy_attachment" "missing_delete_obj" {
|
||||
user = aws_iam_user.missing_delete_obj.name
|
||||
policy_arn = aws_iam_policy.missing_delete_obj.arn
|
||||
}
|
||||
|
||||
# Edge case for missing GetObject permission
|
||||
|
||||
resource "random_id" "missing_get_obj" {
|
||||
prefix = random_id.prefix.dec
|
||||
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||
byte_length = 4
|
||||
}
|
||||
|
||||
resource "aws_iam_user" "missing_get_obj" {
|
||||
name = random_id.missing_get_obj.dec
|
||||
force_destroy = true
|
||||
tags = local.tags
|
||||
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||
tags = {
|
||||
"boundary-demo" = local.hashicorp_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_access_key" "missing_get_obj" {
|
||||
@@ -220,14 +302,17 @@ resource "aws_iam_user_policy_attachment" "missing_get_obj" {
|
||||
# Edge case for missing PutObject permission
|
||||
|
||||
resource "random_id" "missing_put_obj" {
|
||||
prefix = random_id.prefix.dec
|
||||
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||
byte_length = 4
|
||||
}
|
||||
|
||||
resource "aws_iam_user" "missing_put_obj" {
|
||||
name = random_id.missing_put_obj.dec
|
||||
force_destroy = true
|
||||
tags = local.tags
|
||||
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||
tags = {
|
||||
"boundary-demo" = local.hashicorp_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_access_key" "missing_put_obj" {
|
||||
|
||||
Vendored
+15
-1
@@ -13,10 +13,14 @@ output "missing_put_obj_policy_arn" {
|
||||
value = aws_iam_role.missing_put_obj.arn
|
||||
}
|
||||
|
||||
output "missing_put_get_policy_arn" {
|
||||
output "missing_get_obj_policy_arn" {
|
||||
value = aws_iam_role.missing_get_obj.arn
|
||||
}
|
||||
|
||||
output "missing_delete_obj_policy_arn" {
|
||||
value = aws_iam_role.missing_delete_obj.arn
|
||||
}
|
||||
|
||||
output "iam_user_names" {
|
||||
value = aws_iam_user.test.*.name
|
||||
}
|
||||
@@ -53,4 +57,14 @@ output "iam_access_key_missing_get_obj" {
|
||||
output "iam_secret_access_key_missing_get_obj" {
|
||||
value = aws_iam_access_key.missing_get_obj.secret
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "iam_access_key_missing_delete_obj" {
|
||||
value = aws_iam_access_key.missing_delete_obj.id
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "iam_secret_access_key_missing_delete_obj" {
|
||||
value = aws_iam_access_key.missing_delete_obj.secret
|
||||
sensitive = true
|
||||
}
|
||||
Vendored
+9
-1
@@ -4,6 +4,14 @@
|
||||
resource "aws_s3_bucket" "test" {
|
||||
bucket = random_id.prefix.dec
|
||||
force_destroy = true
|
||||
acl = "private"
|
||||
tags = local.tags
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_public_access_block" "secure_access" {
|
||||
bucket = aws_s3_bucket.test.id
|
||||
|
||||
block_public_acls = true
|
||||
block_public_policy = true
|
||||
ignore_public_acls = true
|
||||
restrict_public_buckets = true
|
||||
}
|
||||
Vendored
+6
-4
@@ -4,7 +4,7 @@
|
||||
data "aws_caller_identity" "current" {}
|
||||
|
||||
resource "random_id" "prefix" {
|
||||
prefix = "storage-e2e-test"
|
||||
prefix = "aws-plugin-e2e-test"
|
||||
byte_length = 4
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@ variable "iam_user_count" {
|
||||
}
|
||||
|
||||
locals {
|
||||
hashicorp_email = split(":", data.aws_caller_identity.current.user_id)[1]
|
||||
tags = {
|
||||
user_id = split(":", data.aws_caller_identity.current.user_id)[1]
|
||||
repo = "boundary-plugin-aws"
|
||||
User = split(":", data.aws_caller_identity.current.user_id)[1]
|
||||
Repo = "boundary-plugin-aws"
|
||||
Environment = "Demo"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user