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