Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logical/aws: Run Acceptance Tests in Parallel #5383

Merged
merged 5 commits into from
Sep 26, 2018
Merged
Changes from 4 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
124 changes: 74 additions & 50 deletions builtin/logical/aws/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"context"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"reflect"
"sync"
"testing"
"time"

Expand All @@ -25,6 +27,8 @@ import (
"github.com/mitchellh/mapstructure"
)

var initSetup sync.Once

type mockIAMClient struct {
iamiface.IAMAPI
}
Expand All @@ -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) },
Expand All @@ -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 := generateUniqueRoleName(t.Name())
userName := generateUniqueUserName(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...")
Expand All @@ -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)
Expand All @@ -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{}

Expand Down Expand Up @@ -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) {
Expand All @@ -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": [
Expand All @@ -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 {
Expand All @@ -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)

Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand All @@ -297,15 +300,15 @@ 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 {
t.Fatalf("AWS AttachUserPolicy failed, %v", err)
}

createAccessKeyInput := &iam.CreateAccessKeyInput{
UserName: aws.String(testUserName),
UserName: aws.String(userName),
}
createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput)
if err != nil {
Expand All @@ -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(),
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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{
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -384,17 +387,17 @@ 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 {
log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err)
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)
Expand Down Expand Up @@ -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) },
Expand All @@ -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)
Expand Down Expand Up @@ -702,6 +707,8 @@ func TestBackend_iamUserManagedInlinePolicies(t *testing.T) {
}

func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) {
t.Parallel()
roleName := generateUniqueRoleName(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
Expand All @@ -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)
Expand All @@ -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),
Expand Down Expand Up @@ -785,16 +800,25 @@ 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 generateUniqueRoleName(prefix string) string {
joelthompson marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Sprintf("Vault-%s-%d", prefix, rand.Intn(1000000))
}

func generateUniqueUserName(prefix string) string {
return fmt.Sprintf("Vault-%s-%d", prefix, rand.Intn(1000000))

}

type awsAccessKey struct {
AccessKeyID string
SecretAccessKey string
Expand Down