1package aws
2
3import (
4	"context"
5	"fmt"
6	"log"
7	"net/http"
8	"os"
9	"reflect"
10	"strings"
11	"sync"
12	"testing"
13	"time"
14
15	"github.com/aws/aws-sdk-go/aws"
16	"github.com/aws/aws-sdk-go/aws/awserr"
17	"github.com/aws/aws-sdk-go/aws/credentials"
18	"github.com/aws/aws-sdk-go/aws/session"
19	"github.com/aws/aws-sdk-go/service/dynamodb"
20	"github.com/aws/aws-sdk-go/service/ec2"
21	"github.com/aws/aws-sdk-go/service/iam"
22	"github.com/aws/aws-sdk-go/service/iam/iamiface"
23	"github.com/aws/aws-sdk-go/service/sts"
24	cleanhttp "github.com/hashicorp/go-cleanhttp"
25	"github.com/hashicorp/vault/helper/testhelpers"
26	logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
27	"github.com/hashicorp/vault/sdk/logical"
28	"github.com/mitchellh/mapstructure"
29)
30
31var initSetup sync.Once
32
33type mockIAMClient struct {
34	iamiface.IAMAPI
35}
36
37func (m *mockIAMClient) CreateUser(input *iam.CreateUserInput) (*iam.CreateUserOutput, error) {
38	return nil, awserr.New("Throttling", "", nil)
39}
40
41func getBackend(t *testing.T) logical.Backend {
42	be, _ := Factory(context.Background(), logical.TestBackendConfig())
43	return be
44}
45
46func TestBackend_basic(t *testing.T) {
47	t.Parallel()
48	logicaltest.Test(t, logicaltest.TestCase{
49		AcceptanceTest: true,
50		PreCheck:       func() { testAccPreCheck(t) },
51		LogicalBackend: getBackend(t),
52		Steps: []logicaltest.TestStep{
53			testAccStepConfig(t),
54			testAccStepWritePolicy(t, "test", testDynamoPolicy),
55			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest}),
56		},
57	})
58}
59
60func TestBackend_IamUserWithPermissionsBoundary(t *testing.T) {
61	t.Parallel()
62	roleData := map[string]interface{}{
63		"credential_type":          iamUserCred,
64		"policy_arns":              adminAccessPolicyArn,
65		"permissions_boundary_arn": iamPolicyArn,
66	}
67	logicaltest.Test(t, logicaltest.TestCase{
68		AcceptanceTest: true,
69		PreCheck:       func() { testAccPreCheck(t) },
70		LogicalBackend: getBackend(t),
71		Steps: []logicaltest.TestStep{
72			testAccStepConfig(t),
73			testAccStepWriteRole(t, "test", roleData),
74			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
75		},
76	})
77}
78
79func TestBackend_basicSTS(t *testing.T) {
80	t.Parallel()
81	awsAccountID, err := getAccountID()
82	if err != nil {
83		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
84		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
85	}
86	roleName := generateUniqueName(t.Name())
87	userName := generateUniqueName(t.Name())
88	accessKey := &awsAccessKey{}
89	logicaltest.Test(t, logicaltest.TestCase{
90		AcceptanceTest: true,
91		PreCheck: func() {
92			testAccPreCheck(t)
93			createUser(t, userName, accessKey)
94			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
95			// Sleep sometime because AWS is eventually consistent
96			// Both the createUser and createRole depend on this
97			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
98			time.Sleep(10 * time.Second)
99		},
100		LogicalBackend: getBackend(t),
101		Steps: []logicaltest.TestStep{
102			testAccStepConfigWithCreds(t, accessKey),
103			testAccStepRotateRoot(accessKey),
104			testAccStepWritePolicy(t, "test", testDynamoPolicy),
105			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest}),
106			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
107			testAccStepReadSTSWithArnPolicy(t, "test"),
108			testAccStepWriteArnRoleRef(t, "test2", roleName, awsAccountID),
109			testAccStepRead(t, "sts", "test2", []credentialTestFunc{describeInstancesTest}),
110		},
111		Teardown: func() error {
112			if err := deleteTestRole(roleName); err != nil {
113				return err
114			}
115			return deleteTestUser(accessKey, userName)
116		},
117	})
118}
119
120func TestBackend_policyCrud(t *testing.T) {
121	t.Parallel()
122	compacted, err := compactJSON(testDynamoPolicy)
123	if err != nil {
124		t.Fatalf("bad: %s", err)
125	}
126
127	logicaltest.Test(t, logicaltest.TestCase{
128		AcceptanceTest: true,
129		LogicalBackend: getBackend(t),
130		Steps: []logicaltest.TestStep{
131			testAccStepConfig(t),
132			testAccStepWritePolicy(t, "test", testDynamoPolicy),
133			testAccStepReadPolicy(t, "test", compacted),
134			testAccStepDeletePolicy(t, "test"),
135			testAccStepReadPolicy(t, "test", ""),
136		},
137	})
138}
139
140func TestBackend_throttled(t *testing.T) {
141	t.Parallel()
142	config := logical.TestBackendConfig()
143	config.StorageView = &logical.InmemStorage{}
144
145	b := Backend()
146	if err := b.Setup(context.Background(), config); err != nil {
147		t.Fatal(err)
148	}
149
150	connData := map[string]interface{}{
151		"credential_type": "iam_user",
152	}
153
154	confReq := &logical.Request{
155		Operation: logical.UpdateOperation,
156		Path:      "roles/something",
157		Storage:   config.StorageView,
158		Data:      connData,
159	}
160
161	resp, err := b.HandleRequest(context.Background(), confReq)
162	if err != nil || (resp != nil && resp.IsError()) {
163		t.Fatalf("failed to write configuration: resp:%#v err:%s", resp, err)
164	}
165
166	// Mock the IAM API call to return a throttled response to the CreateUser API
167	// call
168	b.iamClient = &mockIAMClient{}
169
170	credReq := &logical.Request{
171		Operation: logical.UpdateOperation,
172		Path:      "creds/something",
173		Storage:   config.StorageView,
174	}
175
176	credResp, err := b.HandleRequest(context.Background(), credReq)
177	if err == nil {
178		t.Fatalf("failed to trigger expected throttling error condition: resp:%#v", credResp)
179	}
180	rErr := credResp.Error()
181	expected := "Error creating IAM user: Throttling: "
182	if rErr.Error() != expected {
183		t.Fatalf("error message did not match, expected (%s), got (%s)", expected, rErr.Error())
184	}
185
186	// verify the error we got back is returned with a http.StatusBadGateway
187	code, err := logical.RespondErrorCommon(credReq, credResp, err)
188	if err == nil {
189		t.Fatal("expected error after running req/resp/err through RespondErrorCommon, got nil")
190	}
191	if code != http.StatusBadGateway {
192		t.Fatalf("expected HTTP status 'bad gateway', got: (%d)", code)
193	}
194}
195
196func testAccPreCheck(t *testing.T) {
197	initSetup.Do(func() {
198		if v := os.Getenv("AWS_DEFAULT_REGION"); v == "" {
199			log.Println("[INFO] Test: Using us-west-2 as test region")
200			os.Setenv("AWS_DEFAULT_REGION", "us-west-2")
201		}
202	})
203}
204
205func getAccountID() (string, error) {
206	awsConfig := &aws.Config{
207		Region:     aws.String("us-east-1"),
208		HTTPClient: cleanhttp.DefaultClient(),
209	}
210	sess, err := session.NewSession(awsConfig)
211	if err != nil {
212		return "", err
213	}
214	svc := sts.New(sess)
215
216	params := &sts.GetCallerIdentityInput{}
217	res, err := svc.GetCallerIdentity(params)
218
219	if err != nil {
220		return "", err
221	}
222	if res == nil {
223		return "", fmt.Errorf("got nil response from GetCallerIdentity")
224	}
225
226	return *res.Account, nil
227}
228
229func createRole(t *testing.T, roleName, awsAccountID string, policyARNs []string) {
230	const testRoleAssumePolicy = `{
231      "Version": "2012-10-17",
232      "Statement": [
233          {
234              "Effect":"Allow",
235              "Principal": {
236                  "AWS": "arn:aws:iam::%s:root"
237              },
238              "Action": "sts:AssumeRole"
239           }
240      ]
241}
242`
243	awsConfig := &aws.Config{
244		Region:     aws.String("us-east-1"),
245		HTTPClient: cleanhttp.DefaultClient(),
246	}
247	sess, err := session.NewSession(awsConfig)
248	if err != nil {
249		t.Fatal(err)
250	}
251	svc := iam.New(sess)
252	trustPolicy := fmt.Sprintf(testRoleAssumePolicy, awsAccountID)
253
254	params := &iam.CreateRoleInput{
255		AssumeRolePolicyDocument: aws.String(trustPolicy),
256		RoleName:                 aws.String(roleName),
257		Path:                     aws.String("/"),
258	}
259
260	log.Printf("[INFO] AWS CreateRole: %s", roleName)
261	if _, err := svc.CreateRole(params); err != nil {
262		t.Fatalf("AWS CreateRole failed: %v", err)
263	}
264
265	for _, policyARN := range policyARNs {
266		attachment := &iam.AttachRolePolicyInput{
267			PolicyArn: aws.String(policyARN),
268			RoleName:  aws.String(roleName), // Required
269		}
270		_, err = svc.AttachRolePolicy(attachment)
271
272		if err != nil {
273			t.Fatalf("AWS AttachRolePolicy failed: %v", err)
274		}
275	}
276}
277
278func createUser(t *testing.T, userName string, accessKey *awsAccessKey) {
279	// The sequence of user creation actions is carefully chosen to minimize
280	// impact of stolen IAM user credentials
281	// 1. Create user, without any permissions or credentials. At this point,
282	//	  nobody cares if creds compromised because this user can do nothing.
283	// 2. Attach the timebomb policy. This grants no access but puts a time limit
284	//	  on validity of compromised credentials. If this fails, nobody cares
285	//	  because the user has no permissions to do anything anyway
286	// 3. Attach the AdminAccess policy. The IAM user still has no credentials to
287	//	  do anything
288	// 4. Generate API creds to get an actual access key and secret key
289	timebombPolicyTemplate := `{
290		"Version": "2012-10-17",
291		"Statement": [
292			{
293				"Effect": "Deny",
294				"Action": "*",
295				"Resource": "*",
296				"Condition": {
297					"DateGreaterThan": {
298						"aws:CurrentTime": "%s"
299					}
300				}
301			}
302		]
303	}
304	`
305	validity := time.Duration(2 * time.Hour)
306	expiry := time.Now().Add(validity)
307	timebombPolicy := fmt.Sprintf(timebombPolicyTemplate, expiry.Format(time.RFC3339))
308	awsConfig := &aws.Config{
309		Region:     aws.String("us-east-1"),
310		HTTPClient: cleanhttp.DefaultClient(),
311	}
312	sess, err := session.NewSession(awsConfig)
313	if err != nil {
314		t.Fatal(err)
315	}
316	svc := iam.New(sess)
317	createUserInput := &iam.CreateUserInput{
318		UserName: aws.String(userName),
319	}
320	log.Printf("[INFO] AWS CreateUser: %s", userName)
321	if _, err := svc.CreateUser(createUserInput); err != nil {
322		t.Fatalf("AWS CreateUser failed: %v", err)
323	}
324
325	putPolicyInput := &iam.PutUserPolicyInput{
326		PolicyDocument: aws.String(timebombPolicy),
327		PolicyName:     aws.String("SelfDestructionTimebomb"),
328		UserName:       aws.String(userName),
329	}
330	_, err = svc.PutUserPolicy(putPolicyInput)
331	if err != nil {
332		t.Fatalf("AWS PutUserPolicy failed: %v", err)
333	}
334
335	attachUserPolicyInput := &iam.AttachUserPolicyInput{
336		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
337		UserName:  aws.String(userName),
338	}
339	_, err = svc.AttachUserPolicy(attachUserPolicyInput)
340	if err != nil {
341		t.Fatalf("AWS AttachUserPolicy failed, %v", err)
342	}
343
344	createAccessKeyInput := &iam.CreateAccessKeyInput{
345		UserName: aws.String(userName),
346	}
347	createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput)
348	if err != nil {
349		t.Fatalf("AWS CreateAccessKey failed: %v", err)
350	}
351	if createAccessKeyOutput == nil {
352		t.Fatalf("AWS CreateAccessKey returned nil")
353	}
354	genAccessKey := createAccessKeyOutput.AccessKey
355
356	accessKey.AccessKeyID = *genAccessKey.AccessKeyId
357	accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey
358}
359
360func deleteTestRole(roleName string) error {
361	awsConfig := &aws.Config{
362		Region:     aws.String("us-east-1"),
363		HTTPClient: cleanhttp.DefaultClient(),
364	}
365	sess, err := session.NewSession(awsConfig)
366	if err != nil {
367		return err
368	}
369	svc := iam.New(sess)
370	listAttachmentsInput := &iam.ListAttachedRolePoliciesInput{
371		RoleName: aws.String(roleName),
372	}
373	detacher := func(result *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool {
374		for _, policy := range result.AttachedPolicies {
375			detachInput := &iam.DetachRolePolicyInput{
376				PolicyArn: policy.PolicyArn,
377				RoleName:  aws.String(roleName), // Required
378			}
379			_, err := svc.DetachRolePolicy(detachInput)
380			if err != nil {
381				log.Printf("[WARN] AWS DetachRolePolicy failed for policy %s: %v", *policy.PolicyArn, err)
382			}
383		}
384		return true
385	}
386	if err := svc.ListAttachedRolePoliciesPages(listAttachmentsInput, detacher); err != nil {
387		log.Printf("[WARN] AWS DetachRolePolicy failed: %v", err)
388	}
389
390	params := &iam.DeleteRoleInput{
391		RoleName: aws.String(roleName),
392	}
393
394	log.Printf("[INFO] AWS DeleteRole: %s", roleName)
395	_, err = svc.DeleteRole(params)
396
397	if err != nil {
398		log.Printf("[WARN] AWS DeleteRole failed: %v", err)
399		return err
400	}
401	return nil
402}
403
404func deleteTestUser(accessKey *awsAccessKey, userName string) error {
405	awsConfig := &aws.Config{
406		Region:     aws.String("us-east-1"),
407		HTTPClient: cleanhttp.DefaultClient(),
408	}
409	sess, err := session.NewSession(awsConfig)
410	if err != nil {
411		return err
412	}
413	svc := iam.New(sess)
414	userDetachment := &iam.DetachUserPolicyInput{
415		PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"),
416		UserName:  aws.String(userName),
417	}
418	if _, err := svc.DetachUserPolicy(userDetachment); err != nil {
419		log.Printf("[WARN] AWS DetachUserPolicy failed: %v", err)
420		return err
421	}
422
423	deleteAccessKeyInput := &iam.DeleteAccessKeyInput{
424		AccessKeyId: aws.String(accessKey.AccessKeyID),
425		UserName:    aws.String(userName),
426	}
427	_, err = svc.DeleteAccessKey(deleteAccessKeyInput)
428	if err != nil {
429		log.Printf("[WARN] AWS DeleteAccessKey failed: %v", err)
430		return err
431	}
432
433	deleteTestUserPolicyInput := &iam.DeleteUserPolicyInput{
434		PolicyName: aws.String("SelfDestructionTimebomb"),
435		UserName:   aws.String(userName),
436	}
437	_, err = svc.DeleteUserPolicy(deleteTestUserPolicyInput)
438	if err != nil {
439		log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err)
440		return err
441	}
442	deleteTestUserInput := &iam.DeleteUserInput{
443		UserName: aws.String(userName),
444	}
445	log.Printf("[INFO] AWS DeleteUser: %s", userName)
446	_, err = svc.DeleteUser(deleteTestUserInput)
447	if err != nil {
448		log.Printf("[WARN] AWS DeleteUser failed: %v", err)
449		return err
450	}
451
452	return nil
453}
454
455func testAccStepConfig(t *testing.T) logicaltest.TestStep {
456	return logicaltest.TestStep{
457		Operation: logical.UpdateOperation,
458		Path:      "config/root",
459		Data: map[string]interface{}{
460			"region": os.Getenv("AWS_DEFAULT_REGION"),
461		},
462	}
463}
464
465func testAccStepConfigWithCreds(t *testing.T, accessKey *awsAccessKey) logicaltest.TestStep {
466	return logicaltest.TestStep{
467		Operation: logical.UpdateOperation,
468		Path:      "config/root",
469		Data: map[string]interface{}{
470			"region": os.Getenv("AWS_DEFAULT_REGION"),
471		},
472		PreFlight: func(req *logical.Request) error {
473			// Values in Data above get eagerly evaluated due to the testing framework.
474			// In particular, they get evaluated before accessKey gets set by CreateUser
475			// and thus would fail. By moving to a closure in a PreFlight, we ensure that
476			// the creds get evaluated lazily after they've been properly set
477			req.Data["access_key"] = accessKey.AccessKeyID
478			req.Data["secret_key"] = accessKey.SecretAccessKey
479			return nil
480		},
481	}
482}
483
484func testAccStepRotateRoot(oldAccessKey *awsAccessKey) logicaltest.TestStep {
485	return logicaltest.TestStep{
486		Operation: logical.UpdateOperation,
487		Path:      "config/rotate-root",
488		Check: func(resp *logical.Response) error {
489			if resp == nil {
490				return fmt.Errorf("received nil response from config/rotate-root")
491			}
492			newAccessKeyID := resp.Data["access_key"].(string)
493			if newAccessKeyID == oldAccessKey.AccessKeyID {
494				return fmt.Errorf("rotate-root didn't rotate access key")
495			}
496			awsConfig := &aws.Config{
497				Region:      aws.String("us-east-1"),
498				HTTPClient:  cleanhttp.DefaultClient(),
499				Credentials: credentials.NewStaticCredentials(oldAccessKey.AccessKeyID, oldAccessKey.SecretAccessKey, ""),
500			}
501			// sigh....
502			oldAccessKey.AccessKeyID = newAccessKeyID
503			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
504			time.Sleep(10 * time.Second)
505			sess, err := session.NewSession(awsConfig)
506			if err != nil {
507				return err
508			}
509			svc := sts.New(sess)
510			params := &sts.GetCallerIdentityInput{}
511			if _, err := svc.GetCallerIdentity(params); err == nil {
512				return fmt.Errorf("bad: old credentials succeeded after rotate")
513			}
514			if aerr, ok := err.(awserr.Error); ok {
515				if aerr.Code() != "InvalidClientTokenId" {
516					return fmt.Errorf("Unknown error returned from AWS: %#v", aerr)
517				}
518				return nil
519			}
520			return err
521		},
522	}
523}
524
525func testAccStepRead(t *testing.T, path, name string, credentialTests []credentialTestFunc) logicaltest.TestStep {
526	return logicaltest.TestStep{
527		Operation: logical.ReadOperation,
528		Path:      path + "/" + name,
529		Check: func(resp *logical.Response) error {
530			var d struct {
531				AccessKey string `mapstructure:"access_key"`
532				SecretKey string `mapstructure:"secret_key"`
533				STSToken  string `mapstructure:"security_token"`
534			}
535			if err := mapstructure.Decode(resp.Data, &d); err != nil {
536				return err
537			}
538			log.Printf("[WARN] Generated credentials: %v", d)
539			for _, test := range credentialTests {
540				err := test(d.AccessKey, d.SecretKey, d.STSToken)
541				if err != nil {
542					return err
543				}
544			}
545			return nil
546		},
547	}
548}
549
550func testAccStepReadTTL(name string, maximumTTL time.Duration) logicaltest.TestStep {
551	return logicaltest.TestStep{
552		Operation: logical.ReadOperation,
553		Path:      "creds/" + name,
554		Check: func(resp *logical.Response) error {
555			if resp.Secret == nil {
556				return fmt.Errorf("bad: nil Secret returned")
557			}
558			ttl := resp.Secret.TTL
559			if ttl > maximumTTL {
560				return fmt.Errorf("bad: ttl of %d greater than maximum of %d", ttl/time.Second, maximumTTL/time.Second)
561			}
562			return nil
563		},
564	}
565}
566
567func describeInstancesTest(accessKey, secretKey, token string) error {
568	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
569	awsConfig := &aws.Config{
570		Credentials: creds,
571		Region:      aws.String("us-east-1"),
572		HTTPClient:  cleanhttp.DefaultClient(),
573	}
574	sess, err := session.NewSession(awsConfig)
575	if err != nil {
576		return err
577	}
578	client := ec2.New(sess)
579	log.Printf("[WARN] Verifying that the generated credentials work with ec2:DescribeInstances...")
580	return retryUntilSuccess(func() error {
581		_, err := client.DescribeInstances(&ec2.DescribeInstancesInput{})
582		return err
583	})
584}
585
586func describeAzsTestUnauthorized(accessKey, secretKey, token string) error {
587	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
588	awsConfig := &aws.Config{
589		Credentials: creds,
590		Region:      aws.String("us-east-1"),
591		HTTPClient:  cleanhttp.DefaultClient(),
592	}
593	sess, err := session.NewSession(awsConfig)
594	if err != nil {
595		return err
596	}
597	client := ec2.New(sess)
598	log.Printf("[WARN] Verifying that the generated credentials don't work with ec2:DescribeAvailabilityZones...")
599	return retryUntilSuccess(func() error {
600		_, err := client.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{})
601		// Need to make sure AWS authenticates the generated credentials but does not authorize the operation
602		if err == nil {
603			return fmt.Errorf("operation succeeded when expected failure")
604		}
605		if aerr, ok := err.(awserr.Error); ok {
606			if aerr.Code() == "UnauthorizedOperation" {
607				return nil
608			}
609		}
610		return err
611	})
612}
613
614func assertCreatedIAMUser(accessKey, secretKey, token string) error {
615	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
616	awsConfig := &aws.Config{
617		Credentials: creds,
618		Region:      aws.String("us-east-1"),
619		HTTPClient:  cleanhttp.DefaultClient(),
620	}
621	sess, err := session.NewSession(awsConfig)
622	if err != nil {
623		return err
624	}
625	client := iam.New(sess)
626	log.Printf("[WARN] Checking if IAM User is created properly...")
627	userOutput, err := client.GetUser(&iam.GetUserInput{})
628	if err != nil {
629		return err
630	}
631
632	if *userOutput.User.Path != "/path/" {
633		return fmt.Errorf("bad: got: %#v\nexpected: %#v", userOutput.User.Path, "/path/")
634	}
635
636	return nil
637}
638
639func listIamUsersTest(accessKey, secretKey, token string) error {
640	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
641	awsConfig := &aws.Config{
642		Credentials: creds,
643		Region:      aws.String("us-east-1"),
644		HTTPClient:  cleanhttp.DefaultClient(),
645	}
646	sess, err := session.NewSession(awsConfig)
647	if err != nil {
648		return err
649	}
650	client := iam.New(sess)
651	log.Printf("[WARN] Verifying that the generated credentials work with iam:ListUsers...")
652	return retryUntilSuccess(func() error {
653		_, err := client.ListUsers(&iam.ListUsersInput{})
654		return err
655	})
656}
657
658func listDynamoTablesTest(accessKey, secretKey, token string) error {
659	creds := credentials.NewStaticCredentials(accessKey, secretKey, token)
660	awsConfig := &aws.Config{
661		Credentials: creds,
662		Region:      aws.String("us-east-1"),
663		HTTPClient:  cleanhttp.DefaultClient(),
664	}
665	sess, err := session.NewSession(awsConfig)
666	if err != nil {
667		return err
668	}
669	client := dynamodb.New(sess)
670	log.Printf("[WARN] Verifying that the generated credentials work with dynamodb:ListTables...")
671	return retryUntilSuccess(func() error {
672		_, err := client.ListTables(&dynamodb.ListTablesInput{})
673		return err
674	})
675}
676
677func retryUntilSuccess(op func() error) error {
678	retryCount := 0
679	success := false
680	var err error
681	for !success && retryCount < 10 {
682		err = op()
683		if err == nil {
684			return nil
685		}
686		time.Sleep(time.Second)
687		retryCount++
688	}
689	return err
690}
691
692func testAccStepReadSTSWithArnPolicy(t *testing.T, name string) logicaltest.TestStep {
693	return logicaltest.TestStep{
694		Operation: logical.ReadOperation,
695		Path:      "sts/" + name,
696		ErrorOk:   true,
697		Check: func(resp *logical.Response) error {
698			if resp.Data["error"] !=
699				"attempted to retrieve iam_user credentials through the sts path; this is not allowed for legacy roles" {
700				t.Fatalf("bad: %v", resp)
701			}
702			return nil
703		},
704	}
705}
706
707func testAccStepWritePolicy(t *testing.T, name string, policy string) logicaltest.TestStep {
708	return logicaltest.TestStep{
709		Operation: logical.UpdateOperation,
710		Path:      "roles/" + name,
711		Data: map[string]interface{}{
712			"policy": policy,
713		},
714	}
715}
716
717func testAccStepDeletePolicy(t *testing.T, n string) logicaltest.TestStep {
718	return logicaltest.TestStep{
719		Operation: logical.DeleteOperation,
720		Path:      "roles/" + n,
721	}
722}
723
724func testAccStepReadPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
725	return logicaltest.TestStep{
726		Operation: logical.ReadOperation,
727		Path:      "roles/" + name,
728		Check: func(resp *logical.Response) error {
729			if resp == nil {
730				if value == "" {
731					return nil
732				}
733
734				return fmt.Errorf("bad: %#v", resp)
735			}
736
737			expected := map[string]interface{}{
738				"policy_arns":              []string(nil),
739				"role_arns":                []string(nil),
740				"policy_document":          value,
741				"credential_type":          strings.Join([]string{iamUserCred, federationTokenCred}, ","),
742				"default_sts_ttl":          int64(0),
743				"max_sts_ttl":              int64(0),
744				"user_path":                "",
745				"permissions_boundary_arn": "",
746			}
747			if !reflect.DeepEqual(resp.Data, expected) {
748				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
749			}
750			return nil
751		},
752	}
753}
754
755const testDynamoPolicy = `{
756    "Version": "2012-10-17",
757    "Statement": [
758        {
759            "Sid": "Stmt1426528957000",
760            "Effect": "Allow",
761            "Action": [
762                "dynamodb:List*"
763            ],
764            "Resource": [
765                "*"
766            ]
767        }
768    ]
769}
770`
771
772const adminAccessPolicyArn = "arn:aws:iam::aws:policy/AdministratorAccess"
773const ec2PolicyArn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
774const iamPolicyArn = "arn:aws:iam::aws:policy/IAMReadOnlyAccess"
775const dynamoPolicyArn = "arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess"
776
777func testAccStepWriteRole(t *testing.T, name string, data map[string]interface{}) logicaltest.TestStep {
778	return logicaltest.TestStep{
779		Operation: logical.UpdateOperation,
780		Path:      "roles/" + name,
781		Data:      data,
782	}
783}
784
785func testAccStepReadRole(t *testing.T, name string, expected map[string]interface{}) logicaltest.TestStep {
786	return logicaltest.TestStep{
787		Operation: logical.ReadOperation,
788		Path:      "roles/" + name,
789		Check: func(resp *logical.Response) error {
790			if resp == nil {
791				if expected == nil {
792					return nil
793				}
794				return fmt.Errorf("bad: nil response")
795			}
796			if !reflect.DeepEqual(resp.Data, expected) {
797				return fmt.Errorf("bad: got %#v\nexpected: %#v", resp.Data, expected)
798			}
799			return nil
800		},
801	}
802}
803
804func testAccStepWriteArnPolicyRef(t *testing.T, name string, arn string) logicaltest.TestStep {
805	return logicaltest.TestStep{
806		Operation: logical.UpdateOperation,
807		Path:      "roles/" + name,
808		Data: map[string]interface{}{
809			"arn": ec2PolicyArn,
810		},
811	}
812}
813
814func TestBackend_basicPolicyArnRef(t *testing.T) {
815	t.Parallel()
816	logicaltest.Test(t, logicaltest.TestCase{
817		AcceptanceTest: true,
818		PreCheck:       func() { testAccPreCheck(t) },
819		LogicalBackend: getBackend(t),
820		Steps: []logicaltest.TestStep{
821			testAccStepConfig(t),
822			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
823			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest}),
824		},
825	})
826}
827
828func TestBackend_iamUserManagedInlinePolicies(t *testing.T) {
829	t.Parallel()
830	compacted, err := compactJSON(testDynamoPolicy)
831	if err != nil {
832		t.Fatalf("bad: %#v", err)
833	}
834	roleData := map[string]interface{}{
835		"policy_document": testDynamoPolicy,
836		"policy_arns":     []string{ec2PolicyArn, iamPolicyArn},
837		"credential_type": iamUserCred,
838		"user_path":       "/path/",
839	}
840	expectedRoleData := map[string]interface{}{
841		"policy_document":          compacted,
842		"policy_arns":              []string{ec2PolicyArn, iamPolicyArn},
843		"credential_type":          iamUserCred,
844		"role_arns":                []string(nil),
845		"default_sts_ttl":          int64(0),
846		"max_sts_ttl":              int64(0),
847		"user_path":                "/path/",
848		"permissions_boundary_arn": "",
849	}
850
851	logicaltest.Test(t, logicaltest.TestCase{
852		AcceptanceTest: true,
853		PreCheck:       func() { testAccPreCheck(t) },
854		LogicalBackend: getBackend(t),
855		Steps: []logicaltest.TestStep{
856			testAccStepConfig(t),
857			testAccStepWriteRole(t, "test", roleData),
858			testAccStepReadRole(t, "test", expectedRoleData),
859			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, assertCreatedIAMUser}),
860			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest}),
861		},
862	})
863}
864
865func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) {
866	t.Parallel()
867	roleName := generateUniqueName(t.Name())
868	// This looks a bit curious. The policy document and the role document act
869	// as a logical intersection of policies. The role allows ec2:Describe*
870	// (among other permissions). This policy allows everything BUT
871	// ec2:DescribeAvailabilityZones. Thus, the logical intersection of the two
872	// is all ec2:Describe* EXCEPT ec2:DescribeAvailabilityZones, and so the
873	// describeAZs call should fail
874	allowAllButDescribeAzs := `
875{
876	"Version": "2012-10-17",
877	"Statement": [{
878			"Effect": "Allow",
879			"NotAction": "ec2:DescribeAvailabilityZones",
880			"Resource": "*"
881	}]
882}
883`
884	awsAccountID, err := getAccountID()
885	if err != nil {
886		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
887		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
888	}
889	roleData := map[string]interface{}{
890		"policy_document": allowAllButDescribeAzs,
891		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
892		"credential_type": assumedRoleCred,
893	}
894	logicaltest.Test(t, logicaltest.TestCase{
895		AcceptanceTest: true,
896		PreCheck: func() {
897			testAccPreCheck(t)
898			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
899			// Sleep sometime because AWS is eventually consistent
900			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
901			time.Sleep(10 * time.Second)
902		},
903		LogicalBackend: getBackend(t),
904		Steps: []logicaltest.TestStep{
905			testAccStepConfig(t),
906			testAccStepWriteRole(t, "test", roleData),
907			testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
908			testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}),
909		},
910		Teardown: func() error {
911			return deleteTestRole(roleName)
912		},
913	})
914}
915
916func TestBackend_AssumedRoleWithPolicyARN(t *testing.T) {
917	t.Parallel()
918	roleName := generateUniqueName(t.Name())
919
920	awsAccountID, err := getAccountID()
921	if err != nil {
922		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
923		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
924	}
925	roleData := map[string]interface{}{
926		"policy_arns":     iamPolicyArn,
927		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
928		"credential_type": assumedRoleCred,
929	}
930	logicaltest.Test(t, logicaltest.TestCase{
931		AcceptanceTest: true,
932		PreCheck: func() {
933			testAccPreCheck(t)
934			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn, iamPolicyArn})
935			log.Printf("[WARN] Sleeping for 10 seconds waiting for AWS...")
936			time.Sleep(10 * time.Second)
937		},
938		LogicalBackend: getBackend(t),
939		Steps: []logicaltest.TestStep{
940			testAccStepConfig(t),
941			testAccStepWriteRole(t, "test", roleData),
942			testAccStepRead(t, "sts", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
943			testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}),
944		},
945		Teardown: func() error {
946			return deleteTestRole(roleName)
947		},
948	})
949}
950
951func TestBackend_FederationTokenWithPolicyARN(t *testing.T) {
952	t.Parallel()
953	userName := generateUniqueName(t.Name())
954	accessKey := &awsAccessKey{}
955
956	roleData := map[string]interface{}{
957		"policy_arns":     dynamoPolicyArn,
958		"credential_type": federationTokenCred,
959	}
960	logicaltest.Test(t, logicaltest.TestCase{
961		AcceptanceTest: true,
962		PreCheck: func() {
963			testAccPreCheck(t)
964			createUser(t, userName, accessKey)
965			// Sleep sometime because AWS is eventually consistent
966			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
967			time.Sleep(10 * time.Second)
968		},
969		LogicalBackend: getBackend(t),
970		Steps: []logicaltest.TestStep{
971			testAccStepConfigWithCreds(t, accessKey),
972			testAccStepWriteRole(t, "test", roleData),
973			testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
974			testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}),
975		},
976		Teardown: func() error {
977			return deleteTestUser(accessKey, userName)
978		},
979	})
980}
981
982func TestBackend_RoleDefaultSTSTTL(t *testing.T) {
983	t.Parallel()
984	roleName := generateUniqueName(t.Name())
985	minAwsAssumeRoleDuration := 900
986	awsAccountID, err := getAccountID()
987	if err != nil {
988		t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err)
989		t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping")
990	}
991	roleData := map[string]interface{}{
992		"role_arns":       []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)},
993		"credential_type": assumedRoleCred,
994		"default_sts_ttl": minAwsAssumeRoleDuration,
995		"max_sts_ttl":     minAwsAssumeRoleDuration,
996	}
997	logicaltest.Test(t, logicaltest.TestCase{
998		AcceptanceTest: true,
999		PreCheck: func() {
1000			testAccPreCheck(t)
1001			createRole(t, roleName, awsAccountID, []string{ec2PolicyArn})
1002			log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...")
1003			time.Sleep(10 * time.Second)
1004		},
1005		LogicalBackend: getBackend(t),
1006		Steps: []logicaltest.TestStep{
1007			testAccStepConfig(t),
1008			testAccStepWriteRole(t, "test", roleData),
1009			testAccStepReadTTL("test", time.Duration(minAwsAssumeRoleDuration)*time.Second), // allow a little slack
1010		},
1011		Teardown: func() error {
1012			return deleteTestRole(roleName)
1013		},
1014	})
1015}
1016
1017func TestBackend_policyArnCrud(t *testing.T) {
1018	t.Parallel()
1019	logicaltest.Test(t, logicaltest.TestCase{
1020		AcceptanceTest: true,
1021		LogicalBackend: getBackend(t),
1022		Steps: []logicaltest.TestStep{
1023			testAccStepConfig(t),
1024			testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn),
1025			testAccStepReadArnPolicy(t, "test", ec2PolicyArn),
1026			testAccStepDeletePolicy(t, "test"),
1027			testAccStepReadArnPolicy(t, "test", ""),
1028		},
1029	})
1030}
1031
1032func testAccStepReadArnPolicy(t *testing.T, name string, value string) logicaltest.TestStep {
1033	return logicaltest.TestStep{
1034		Operation: logical.ReadOperation,
1035		Path:      "roles/" + name,
1036		Check: func(resp *logical.Response) error {
1037			if resp == nil {
1038				if value == "" {
1039					return nil
1040				}
1041
1042				return fmt.Errorf("bad: %#v", resp)
1043			}
1044
1045			expected := map[string]interface{}{
1046				"policy_arns":              []string{value},
1047				"role_arns":                []string(nil),
1048				"policy_document":          "",
1049				"credential_type":          iamUserCred,
1050				"default_sts_ttl":          int64(0),
1051				"max_sts_ttl":              int64(0),
1052				"user_path":                "",
1053				"permissions_boundary_arn": "",
1054			}
1055			if !reflect.DeepEqual(resp.Data, expected) {
1056				return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected)
1057			}
1058
1059			return nil
1060		},
1061	}
1062}
1063
1064func testAccStepWriteArnRoleRef(t *testing.T, vaultRoleName, awsRoleName, awsAccountID string) logicaltest.TestStep {
1065	return logicaltest.TestStep{
1066		Operation: logical.UpdateOperation,
1067		Path:      "roles/" + vaultRoleName,
1068		Data: map[string]interface{}{
1069			"arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, awsRoleName),
1070		},
1071	}
1072}
1073
1074func generateUniqueName(prefix string) string {
1075	return testhelpers.RandomWithPrefix(prefix)
1076}
1077
1078type awsAccessKey struct {
1079	AccessKeyID     string
1080	SecretAccessKey string
1081}
1082
1083type credentialTestFunc func(string, string, string) error
1084