diff --git a/builtin/logical/aws/backend_test.go b/builtin/logical/aws/backend_test.go index dd90c1d3a3dd..8c27ec507d84 100644 --- a/builtin/logical/aws/backend_test.go +++ b/builtin/logical/aws/backend_test.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "reflect" + "sync" "testing" "time" @@ -20,11 +21,14 @@ import ( "github.com/aws/aws-sdk-go/service/iam/iamiface" "github.com/aws/aws-sdk-go/service/sts" "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/vault/helper/testhelpers" "github.com/hashicorp/vault/logical" logicaltest "github.com/hashicorp/vault/logical/testing" "github.com/mitchellh/mapstructure" ) +var initSetup sync.Once + type mockIAMClient struct { iamiface.IAMAPI } @@ -39,6 +43,7 @@ func getBackend(t *testing.T) logical.Backend { } func TestBackend_basic(t *testing.T) { + t.Parallel() logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, PreCheck: func() { testAccPreCheck(t) }, @@ -52,13 +57,21 @@ func TestBackend_basic(t *testing.T) { } func TestBackend_basicSTS(t *testing.T) { + t.Parallel() + awsAccountID, err := getAccountID() + if err != nil { + t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err) + t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping") + } + roleName := generateUniqueName(t.Name()) + userName := generateUniqueName(t.Name()) accessKey := &awsAccessKey{} logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, PreCheck: func() { testAccPreCheck(t) - createUser(t, accessKey) - createRole(t) + createUser(t, userName, accessKey) + createRole(t, roleName, awsAccountID) // Sleep sometime because AWS is eventually consistent // Both the createUser and createRole depend on this log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") @@ -71,16 +84,17 @@ func TestBackend_basicSTS(t *testing.T) { testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest}), testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn), testAccStepReadSTSWithArnPolicy(t, "test"), - testAccStepWriteArnRoleRef(t, testRoleName), - testAccStepRead(t, "sts", testRoleName, []credentialTestFunc{describeInstancesTest}), + testAccStepWriteArnRoleRef(t, "test2", roleName, awsAccountID), + testAccStepRead(t, "sts", "test2", []credentialTestFunc{describeInstancesTest}), }, Teardown: func() error { - return teardown(accessKey) + return teardown(accessKey, roleName, userName) }, }) } func TestBackend_policyCrud(t *testing.T) { + t.Parallel() compacted, err := compactJSON(testDynamoPolicy) if err != nil { t.Fatalf("bad: %s", err) @@ -100,6 +114,7 @@ func TestBackend_policyCrud(t *testing.T) { } func TestBackend_throttled(t *testing.T) { + t.Parallel() config := logical.TestBackendConfig() config.StorageView = &logical.InmemStorage{} @@ -155,20 +170,12 @@ func TestBackend_throttled(t *testing.T) { } func testAccPreCheck(t *testing.T) { - if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" { - log.Println("[INFO] Test: Using us-west-2 as test region") - os.Setenv("AWS_DEFAULT_REGION", "us-west-2") - } - - if v := os.Getenv("AWS_ACCOUNT_ID"); v == "" { - accountID, err := getAccountID() - if err != nil { - t.Logf("Unable to retrive user via iam:GetUser: %#v", err) - t.Skip("AWS_ACCOUNT_ID not explicitly set and could not be read from iam:GetUser for acceptance tests, skipping") + initSetup.Do(func() { + if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" { + log.Println("[INFO] Test: Using us-west-2 as test region") + os.Setenv("AWS_DEFAULT_REGION", "us-west-2") } - log.Printf("[INFO] Test: Used %s as AWS_ACCOUNT_ID", accountID) - os.Setenv("AWS_ACCOUNT_ID", accountID) - } + }) } func getAccountID() (string, error) { @@ -191,9 +198,7 @@ func getAccountID() (string, error) { return *res.Account, nil } -const testRoleName = "Vault-Acceptance-Test-AWS-Assume-Role" - -func createRole(t *testing.T) { +func createRole(t *testing.T, roleName, awsAccountID string) { const testRoleAssumePolicy = `{ "Version": "2012-10-17", "Statement": [ @@ -212,15 +217,15 @@ func createRole(t *testing.T) { HTTPClient: cleanhttp.DefaultClient(), } svc := iam.New(session.New(awsConfig)) - trustPolicy := fmt.Sprintf(testRoleAssumePolicy, os.Getenv("AWS_ACCOUNT_ID")) + trustPolicy := fmt.Sprintf(testRoleAssumePolicy, awsAccountID) params := &iam.CreateRoleInput{ AssumeRolePolicyDocument: aws.String(trustPolicy), - RoleName: aws.String(testRoleName), + RoleName: aws.String(roleName), Path: aws.String("/"), } - log.Printf("[INFO] AWS CreateRole: %s", testRoleName) + log.Printf("[INFO] AWS CreateRole: %s", roleName) _, err := svc.CreateRole(params) if err != nil { @@ -229,7 +234,7 @@ func createRole(t *testing.T) { attachment := &iam.AttachRolePolicyInput{ PolicyArn: aws.String(ec2PolicyArn), - RoleName: aws.String(testRoleName), // Required + RoleName: aws.String(roleName), // Required } _, err = svc.AttachRolePolicy(attachment) @@ -238,9 +243,7 @@ func createRole(t *testing.T) { } } -const testUserName = "Vault-Acceptance-Test-AWS-FederationToken" - -func createUser(t *testing.T, accessKey *awsAccessKey) { +func createUser(t *testing.T, userName string, accessKey *awsAccessKey) { // The sequence of user creation actions is carefully chosen to minimize // impact of stolen IAM user credentials // 1. Create user, without any permissions or credentials. At this point, @@ -277,9 +280,9 @@ func createUser(t *testing.T, accessKey *awsAccessKey) { svc := iam.New(session.New(awsConfig)) createUserInput := &iam.CreateUserInput{ - UserName: aws.String(testUserName), + UserName: aws.String(userName), } - log.Printf("[INFO] AWS CreateUser: %s", testUserName) + log.Printf("[INFO] AWS CreateUser: %s", userName) _, err := svc.CreateUser(createUserInput) if err != nil { t.Fatalf("AWS CreateUser failed: %v", err) @@ -288,7 +291,7 @@ func createUser(t *testing.T, accessKey *awsAccessKey) { putPolicyInput := &iam.PutUserPolicyInput{ PolicyDocument: aws.String(timebombPolicy), PolicyName: aws.String("SelfDestructionTimebomb"), - UserName: aws.String(testUserName), + UserName: aws.String(userName), } _, err = svc.PutUserPolicy(putPolicyInput) if err != nil { @@ -297,7 +300,7 @@ func createUser(t *testing.T, accessKey *awsAccessKey) { attachUserPolicyInput := &iam.AttachUserPolicyInput{ PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), - UserName: aws.String(testUserName), + UserName: aws.String(userName), } _, err = svc.AttachUserPolicy(attachUserPolicyInput) if err != nil { @@ -305,7 +308,7 @@ func createUser(t *testing.T, accessKey *awsAccessKey) { } createAccessKeyInput := &iam.CreateAccessKeyInput{ - UserName: aws.String(testUserName), + UserName: aws.String(userName), } createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput) if err != nil { @@ -320,7 +323,7 @@ func createUser(t *testing.T, accessKey *awsAccessKey) { accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey } -func deleteTestRole() error { +func deleteTestRole(roleName string) error { awsConfig := &aws.Config{ Region: aws.String("us-east-1"), HTTPClient: cleanhttp.DefaultClient(), @@ -329,7 +332,7 @@ func deleteTestRole() error { attachment := &iam.DetachRolePolicyInput{ PolicyArn: aws.String(ec2PolicyArn), - RoleName: aws.String(testRoleName), // Required + RoleName: aws.String(roleName), // Required } _, err := svc.DetachRolePolicy(attachment) if err != nil { @@ -338,10 +341,10 @@ func deleteTestRole() error { } params := &iam.DeleteRoleInput{ - RoleName: aws.String(testRoleName), + RoleName: aws.String(roleName), } - log.Printf("[INFO] AWS DeleteRole: %s", testRoleName) + log.Printf("[INFO] AWS DeleteRole: %s", roleName) _, err = svc.DeleteRole(params) if err != nil { @@ -351,9 +354,9 @@ func deleteTestRole() error { return nil } -func teardown(accessKey *awsAccessKey) error { +func teardown(accessKey *awsAccessKey, roleName, userName string) error { - if err := deleteTestRole(); err != nil { + if err := deleteTestRole(roleName); err != nil { return err } awsConfig := &aws.Config{ @@ -364,7 +367,7 @@ func teardown(accessKey *awsAccessKey) error { userDetachment := &iam.DetachUserPolicyInput{ PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), - UserName: aws.String(testUserName), + UserName: aws.String(userName), } _, err := svc.DetachUserPolicy(userDetachment) if err != nil { @@ -374,7 +377,7 @@ func teardown(accessKey *awsAccessKey) error { deleteAccessKeyInput := &iam.DeleteAccessKeyInput{ AccessKeyId: aws.String(accessKey.AccessKeyID), - UserName: aws.String(testUserName), + UserName: aws.String(userName), } _, err = svc.DeleteAccessKey(deleteAccessKeyInput) if err != nil { @@ -384,7 +387,7 @@ func teardown(accessKey *awsAccessKey) error { deleteUserPolicyInput := &iam.DeleteUserPolicyInput{ PolicyName: aws.String("SelfDestructionTimebomb"), - UserName: aws.String(testUserName), + UserName: aws.String(userName), } _, err = svc.DeleteUserPolicy(deleteUserPolicyInput) if err != nil { @@ -392,9 +395,9 @@ func teardown(accessKey *awsAccessKey) error { return err } deleteUserInput := &iam.DeleteUserInput{ - UserName: aws.String(testUserName), + UserName: aws.String(userName), } - log.Printf("[INFO] AWS DeleteUser: %s", testUserName) + log.Printf("[INFO] AWS DeleteUser: %s", userName) _, err = svc.DeleteUser(deleteUserInput) if err != nil { log.Printf("[WARN] AWS DeleteUser failed: %v", err) @@ -659,6 +662,7 @@ func testAccStepWriteArnPolicyRef(t *testing.T, name string, arn string) logical } func TestBackend_basicPolicyArnRef(t *testing.T) { + t.Parallel() logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, PreCheck: func() { testAccPreCheck(t) }, @@ -672,6 +676,7 @@ func TestBackend_basicPolicyArnRef(t *testing.T) { } func TestBackend_iamUserManagedInlinePolicies(t *testing.T) { + t.Parallel() compacted, err := compactJSON(testDynamoPolicy) if err != nil { t.Fatalf("bad: %#v", err) @@ -702,6 +707,8 @@ func TestBackend_iamUserManagedInlinePolicies(t *testing.T) { } func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) { + t.Parallel() + roleName := generateUniqueName(t.Name()) // This looks a bit curious. The policy document and the role document act // as a logical intersection of policies. The role allows ec2:Describe* // (among other permissions). This policy allows everything BUT @@ -718,16 +725,21 @@ func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) { }] } ` + awsAccountID, err := getAccountID() + if err != nil { + t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err) + t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping") + } roleData := map[string]interface{}{ "policy_document": allowAllButDescribeAzs, - "role_arns": []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", os.Getenv("AWS_ACCOUNT_ID"), testRoleName)}, + "role_arns": []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)}, "credential_type": assumedRoleCred, } logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, PreCheck: func() { testAccPreCheck(t) - createRole(t) + createRole(t, roleName, awsAccountID) // Sleep sometime because AWS is eventually consistent log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") time.Sleep(10 * time.Second) @@ -739,11 +751,14 @@ func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) { testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}), testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}), }, - Teardown: deleteTestRole, + Teardown: func() error { + return deleteTestRole(roleName) + }, }) } func TestBackend_policyArnCrud(t *testing.T) { + t.Parallel() logicaltest.Test(t, logicaltest.TestCase{ AcceptanceTest: true, Backend: getBackend(t), @@ -785,16 +800,20 @@ func testAccStepReadArnPolicy(t *testing.T, name string, value string) logicalte } } -func testAccStepWriteArnRoleRef(t *testing.T, roleName string) logicaltest.TestStep { +func testAccStepWriteArnRoleRef(t *testing.T, vaultRoleName, awsRoleName, awsAccountID string) logicaltest.TestStep { return logicaltest.TestStep{ Operation: logical.UpdateOperation, - Path: "roles/" + roleName, + Path: "roles/" + vaultRoleName, Data: map[string]interface{}{ - "arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", os.Getenv("AWS_ACCOUNT_ID"), roleName), + "arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, awsRoleName), }, } } +func generateUniqueName(prefix string) string { + return testhelpers.RandomWithPrefix(prefix) +} + type awsAccessKey struct { AccessKeyID string SecretAccessKey string