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 svc := sts.New(session.New(awsConfig)) 211 212 params := &sts.GetCallerIdentityInput{} 213 res, err := svc.GetCallerIdentity(params) 214 215 if err != nil { 216 return "", err 217 } 218 if res == nil { 219 return "", fmt.Errorf("got nil response from GetCallerIdentity") 220 } 221 222 return *res.Account, nil 223} 224 225func createRole(t *testing.T, roleName, awsAccountID string, policyARNs []string) { 226 const testRoleAssumePolicy = `{ 227 "Version": "2012-10-17", 228 "Statement": [ 229 { 230 "Effect":"Allow", 231 "Principal": { 232 "AWS": "arn:aws:iam::%s:root" 233 }, 234 "Action": "sts:AssumeRole" 235 } 236 ] 237} 238` 239 awsConfig := &aws.Config{ 240 Region: aws.String("us-east-1"), 241 HTTPClient: cleanhttp.DefaultClient(), 242 } 243 svc := iam.New(session.New(awsConfig)) 244 trustPolicy := fmt.Sprintf(testRoleAssumePolicy, awsAccountID) 245 246 params := &iam.CreateRoleInput{ 247 AssumeRolePolicyDocument: aws.String(trustPolicy), 248 RoleName: aws.String(roleName), 249 Path: aws.String("/"), 250 } 251 252 log.Printf("[INFO] AWS CreateRole: %s", roleName) 253 _, err := svc.CreateRole(params) 254 255 if err != nil { 256 t.Fatalf("AWS CreateRole failed: %v", err) 257 } 258 259 for _, policyARN := range policyARNs { 260 attachment := &iam.AttachRolePolicyInput{ 261 PolicyArn: aws.String(policyARN), 262 RoleName: aws.String(roleName), // Required 263 } 264 _, err = svc.AttachRolePolicy(attachment) 265 266 if err != nil { 267 t.Fatalf("AWS AttachRolePolicy failed: %v", err) 268 } 269 } 270} 271 272func createUser(t *testing.T, userName string, accessKey *awsAccessKey) { 273 // The sequence of user creation actions is carefully chosen to minimize 274 // impact of stolen IAM user credentials 275 // 1. Create user, without any permissions or credentials. At this point, 276 // nobody cares if creds compromised because this user can do nothing. 277 // 2. Attach the timebomb policy. This grants no access but puts a time limit 278 // on validity of compromised credentials. If this fails, nobody cares 279 // because the user has no permissions to do anything anyway 280 // 3. Attach the AdminAccess policy. The IAM user still has no credentials to 281 // do anything 282 // 4. Generate API creds to get an actual access key and secret key 283 timebombPolicyTemplate := `{ 284 "Version": "2012-10-17", 285 "Statement": [ 286 { 287 "Effect": "Deny", 288 "Action": "*", 289 "Resource": "*", 290 "Condition": { 291 "DateGreaterThan": { 292 "aws:CurrentTime": "%s" 293 } 294 } 295 } 296 ] 297 } 298 ` 299 validity := time.Duration(2 * time.Hour) 300 expiry := time.Now().Add(validity) 301 timebombPolicy := fmt.Sprintf(timebombPolicyTemplate, expiry.Format(time.RFC3339)) 302 awsConfig := &aws.Config{ 303 Region: aws.String("us-east-1"), 304 HTTPClient: cleanhttp.DefaultClient(), 305 } 306 svc := iam.New(session.New(awsConfig)) 307 308 createUserInput := &iam.CreateUserInput{ 309 UserName: aws.String(userName), 310 } 311 log.Printf("[INFO] AWS CreateUser: %s", userName) 312 _, err := svc.CreateUser(createUserInput) 313 if err != nil { 314 t.Fatalf("AWS CreateUser failed: %v", err) 315 } 316 317 putPolicyInput := &iam.PutUserPolicyInput{ 318 PolicyDocument: aws.String(timebombPolicy), 319 PolicyName: aws.String("SelfDestructionTimebomb"), 320 UserName: aws.String(userName), 321 } 322 _, err = svc.PutUserPolicy(putPolicyInput) 323 if err != nil { 324 t.Fatalf("AWS PutUserPolicy failed: %v", err) 325 } 326 327 attachUserPolicyInput := &iam.AttachUserPolicyInput{ 328 PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), 329 UserName: aws.String(userName), 330 } 331 _, err = svc.AttachUserPolicy(attachUserPolicyInput) 332 if err != nil { 333 t.Fatalf("AWS AttachUserPolicy failed, %v", err) 334 } 335 336 createAccessKeyInput := &iam.CreateAccessKeyInput{ 337 UserName: aws.String(userName), 338 } 339 createAccessKeyOutput, err := svc.CreateAccessKey(createAccessKeyInput) 340 if err != nil { 341 t.Fatalf("AWS CreateAccessKey failed: %v", err) 342 } 343 if createAccessKeyOutput == nil { 344 t.Fatalf("AWS CreateAccessKey returned nil") 345 } 346 genAccessKey := createAccessKeyOutput.AccessKey 347 348 accessKey.AccessKeyID = *genAccessKey.AccessKeyId 349 accessKey.SecretAccessKey = *genAccessKey.SecretAccessKey 350} 351 352func deleteTestRole(roleName string) error { 353 awsConfig := &aws.Config{ 354 Region: aws.String("us-east-1"), 355 HTTPClient: cleanhttp.DefaultClient(), 356 } 357 svc := iam.New(session.New(awsConfig)) 358 359 listAttachmentsInput := &iam.ListAttachedRolePoliciesInput{ 360 RoleName: aws.String(roleName), 361 } 362 detacher := func(result *iam.ListAttachedRolePoliciesOutput, lastPage bool) bool { 363 for _, policy := range result.AttachedPolicies { 364 detachInput := &iam.DetachRolePolicyInput{ 365 PolicyArn: policy.PolicyArn, 366 RoleName: aws.String(roleName), // Required 367 } 368 _, err := svc.DetachRolePolicy(detachInput) 369 if err != nil { 370 log.Printf("[WARN] AWS DetachRolePolicy failed for policy %s: %v", *policy.PolicyArn, err) 371 } 372 } 373 return true 374 } 375 err := svc.ListAttachedRolePoliciesPages(listAttachmentsInput, detacher) 376 if err != nil { 377 log.Printf("[WARN] AWS DetachRolePolicy failed: %v", err) 378 } 379 380 params := &iam.DeleteRoleInput{ 381 RoleName: aws.String(roleName), 382 } 383 384 log.Printf("[INFO] AWS DeleteRole: %s", roleName) 385 _, err = svc.DeleteRole(params) 386 387 if err != nil { 388 log.Printf("[WARN] AWS DeleteRole failed: %v", err) 389 return err 390 } 391 return nil 392} 393 394func deleteTestUser(accessKey *awsAccessKey, userName string) error { 395 awsConfig := &aws.Config{ 396 Region: aws.String("us-east-1"), 397 HTTPClient: cleanhttp.DefaultClient(), 398 } 399 svc := iam.New(session.New(awsConfig)) 400 401 userDetachment := &iam.DetachUserPolicyInput{ 402 PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess"), 403 UserName: aws.String(userName), 404 } 405 _, err := svc.DetachUserPolicy(userDetachment) 406 if err != nil { 407 log.Printf("[WARN] AWS DetachUserPolicy failed: %v", err) 408 return err 409 } 410 411 deleteAccessKeyInput := &iam.DeleteAccessKeyInput{ 412 AccessKeyId: aws.String(accessKey.AccessKeyID), 413 UserName: aws.String(userName), 414 } 415 _, err = svc.DeleteAccessKey(deleteAccessKeyInput) 416 if err != nil { 417 log.Printf("[WARN] AWS DeleteAccessKey failed: %v", err) 418 return err 419 } 420 421 deleteTestUserPolicyInput := &iam.DeleteUserPolicyInput{ 422 PolicyName: aws.String("SelfDestructionTimebomb"), 423 UserName: aws.String(userName), 424 } 425 _, err = svc.DeleteUserPolicy(deleteTestUserPolicyInput) 426 if err != nil { 427 log.Printf("[WARN] AWS DeleteUserPolicy failed: %v", err) 428 return err 429 } 430 deleteTestUserInput := &iam.DeleteUserInput{ 431 UserName: aws.String(userName), 432 } 433 log.Printf("[INFO] AWS DeleteUser: %s", userName) 434 _, err = svc.DeleteUser(deleteTestUserInput) 435 if err != nil { 436 log.Printf("[WARN] AWS DeleteUser failed: %v", err) 437 return err 438 } 439 440 return nil 441} 442 443func testAccStepConfig(t *testing.T) logicaltest.TestStep { 444 return logicaltest.TestStep{ 445 Operation: logical.UpdateOperation, 446 Path: "config/root", 447 Data: map[string]interface{}{ 448 "region": os.Getenv("AWS_DEFAULT_REGION"), 449 }, 450 } 451} 452 453func testAccStepConfigWithCreds(t *testing.T, accessKey *awsAccessKey) logicaltest.TestStep { 454 return logicaltest.TestStep{ 455 Operation: logical.UpdateOperation, 456 Path: "config/root", 457 Data: map[string]interface{}{ 458 "region": os.Getenv("AWS_DEFAULT_REGION"), 459 }, 460 PreFlight: func(req *logical.Request) error { 461 // Values in Data above get eagerly evaluated due to the testing framework. 462 // In particular, they get evaluated before accessKey gets set by CreateUser 463 // and thus would fail. By moving to a closure in a PreFlight, we ensure that 464 // the creds get evaluated lazily after they've been properly set 465 req.Data["access_key"] = accessKey.AccessKeyID 466 req.Data["secret_key"] = accessKey.SecretAccessKey 467 return nil 468 }, 469 } 470} 471 472func testAccStepRotateRoot(oldAccessKey *awsAccessKey) logicaltest.TestStep { 473 return logicaltest.TestStep{ 474 Operation: logical.UpdateOperation, 475 Path: "config/rotate-root", 476 Check: func(resp *logical.Response) error { 477 if resp == nil { 478 return fmt.Errorf("received nil response from config/rotate-root") 479 } 480 newAccessKeyID := resp.Data["access_key"].(string) 481 if newAccessKeyID == oldAccessKey.AccessKeyID { 482 return fmt.Errorf("rotate-root didn't rotate access key") 483 } 484 awsConfig := &aws.Config{ 485 Region: aws.String("us-east-1"), 486 HTTPClient: cleanhttp.DefaultClient(), 487 Credentials: credentials.NewStaticCredentials(oldAccessKey.AccessKeyID, oldAccessKey.SecretAccessKey, ""), 488 } 489 // sigh.... 490 oldAccessKey.AccessKeyID = newAccessKeyID 491 log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") 492 time.Sleep(10 * time.Second) 493 svc := sts.New(session.New(awsConfig)) 494 params := &sts.GetCallerIdentityInput{} 495 _, err := svc.GetCallerIdentity(params) 496 if err == nil { 497 return fmt.Errorf("bad: old credentials succeeded after rotate") 498 } 499 if aerr, ok := err.(awserr.Error); ok { 500 if aerr.Code() != "InvalidClientTokenId" { 501 return fmt.Errorf("Unknown error returned from AWS: %#v", aerr) 502 } 503 return nil 504 } 505 return err 506 }, 507 } 508} 509 510func testAccStepRead(t *testing.T, path, name string, credentialTests []credentialTestFunc) logicaltest.TestStep { 511 return logicaltest.TestStep{ 512 Operation: logical.ReadOperation, 513 Path: path + "/" + name, 514 Check: func(resp *logical.Response) error { 515 var d struct { 516 AccessKey string `mapstructure:"access_key"` 517 SecretKey string `mapstructure:"secret_key"` 518 STSToken string `mapstructure:"security_token"` 519 } 520 if err := mapstructure.Decode(resp.Data, &d); err != nil { 521 return err 522 } 523 log.Printf("[WARN] Generated credentials: %v", d) 524 for _, test := range credentialTests { 525 err := test(d.AccessKey, d.SecretKey, d.STSToken) 526 if err != nil { 527 return err 528 } 529 } 530 return nil 531 }, 532 } 533} 534 535func testAccStepReadTTL(name string, maximumTTL time.Duration) logicaltest.TestStep { 536 return logicaltest.TestStep{ 537 Operation: logical.ReadOperation, 538 Path: "creds/" + name, 539 Check: func(resp *logical.Response) error { 540 if resp.Secret == nil { 541 return fmt.Errorf("bad: nil Secret returned") 542 } 543 ttl := resp.Secret.TTL 544 if ttl > maximumTTL { 545 return fmt.Errorf("bad: ttl of %d greater than maximum of %d", ttl/time.Second, maximumTTL/time.Second) 546 } 547 return nil 548 }, 549 } 550} 551 552func describeInstancesTest(accessKey, secretKey, token string) error { 553 creds := credentials.NewStaticCredentials(accessKey, secretKey, token) 554 awsConfig := &aws.Config{ 555 Credentials: creds, 556 Region: aws.String("us-east-1"), 557 HTTPClient: cleanhttp.DefaultClient(), 558 } 559 client := ec2.New(session.New(awsConfig)) 560 log.Printf("[WARN] Verifying that the generated credentials work with ec2:DescribeInstances...") 561 return retryUntilSuccess(func() error { 562 _, err := client.DescribeInstances(&ec2.DescribeInstancesInput{}) 563 return err 564 }) 565} 566 567func describeAzsTestUnauthorized(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 client := ec2.New(session.New(awsConfig)) 575 log.Printf("[WARN] Verifying that the generated credentials don't work with ec2:DescribeAvailabilityZones...") 576 return retryUntilSuccess(func() error { 577 _, err := client.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{}) 578 // Need to make sure AWS authenticates the generated credentials but does not authorize the operation 579 if err == nil { 580 return fmt.Errorf("operation succeeded when expected failure") 581 } 582 if aerr, ok := err.(awserr.Error); ok { 583 if aerr.Code() == "UnauthorizedOperation" { 584 return nil 585 } 586 } 587 return err 588 }) 589} 590 591func assertCreatedIAMUser(accessKey, secretKey, token string) error { 592 creds := credentials.NewStaticCredentials(accessKey, secretKey, token) 593 awsConfig := &aws.Config{ 594 Credentials: creds, 595 Region: aws.String("us-east-1"), 596 HTTPClient: cleanhttp.DefaultClient(), 597 } 598 client := iam.New(session.New(awsConfig)) 599 log.Printf("[WARN] Checking if IAM User is created properly...") 600 userOutput, err := client.GetUser(&iam.GetUserInput{}) 601 if err != nil { 602 return err 603 } 604 605 if *userOutput.User.Path != "/path/" { 606 return fmt.Errorf("bad: got: %#v\nexpected: %#v", userOutput.User.Path, "/path/") 607 } 608 609 return nil 610} 611 612func listIamUsersTest(accessKey, secretKey, token string) error { 613 creds := credentials.NewStaticCredentials(accessKey, secretKey, token) 614 awsConfig := &aws.Config{ 615 Credentials: creds, 616 Region: aws.String("us-east-1"), 617 HTTPClient: cleanhttp.DefaultClient(), 618 } 619 client := iam.New(session.New(awsConfig)) 620 log.Printf("[WARN] Verifying that the generated credentials work with iam:ListUsers...") 621 return retryUntilSuccess(func() error { 622 _, err := client.ListUsers(&iam.ListUsersInput{}) 623 return err 624 }) 625} 626 627func listDynamoTablesTest(accessKey, secretKey, token string) error { 628 creds := credentials.NewStaticCredentials(accessKey, secretKey, token) 629 awsConfig := &aws.Config{ 630 Credentials: creds, 631 Region: aws.String("us-east-1"), 632 HTTPClient: cleanhttp.DefaultClient(), 633 } 634 client := dynamodb.New(session.New(awsConfig)) 635 log.Printf("[WARN] Verifying that the generated credentials work with dynamodb:ListTables...") 636 return retryUntilSuccess(func() error { 637 _, err := client.ListTables(&dynamodb.ListTablesInput{}) 638 return err 639 }) 640} 641 642func retryUntilSuccess(op func() error) error { 643 retryCount := 0 644 success := false 645 var err error 646 for !success && retryCount < 10 { 647 err = op() 648 if err == nil { 649 return nil 650 } 651 time.Sleep(time.Second) 652 retryCount++ 653 } 654 return err 655} 656 657func testAccStepReadSTSWithArnPolicy(t *testing.T, name string) logicaltest.TestStep { 658 return logicaltest.TestStep{ 659 Operation: logical.ReadOperation, 660 Path: "sts/" + name, 661 ErrorOk: true, 662 Check: func(resp *logical.Response) error { 663 if resp.Data["error"] != 664 "attempted to retrieve iam_user credentials through the sts path; this is not allowed for legacy roles" { 665 t.Fatalf("bad: %v", resp) 666 } 667 return nil 668 }, 669 } 670} 671 672func testAccStepWritePolicy(t *testing.T, name string, policy string) logicaltest.TestStep { 673 return logicaltest.TestStep{ 674 Operation: logical.UpdateOperation, 675 Path: "roles/" + name, 676 Data: map[string]interface{}{ 677 "policy": policy, 678 }, 679 } 680} 681 682func testAccStepDeletePolicy(t *testing.T, n string) logicaltest.TestStep { 683 return logicaltest.TestStep{ 684 Operation: logical.DeleteOperation, 685 Path: "roles/" + n, 686 } 687} 688 689func testAccStepReadPolicy(t *testing.T, name string, value string) logicaltest.TestStep { 690 return logicaltest.TestStep{ 691 Operation: logical.ReadOperation, 692 Path: "roles/" + name, 693 Check: func(resp *logical.Response) error { 694 if resp == nil { 695 if value == "" { 696 return nil 697 } 698 699 return fmt.Errorf("bad: %#v", resp) 700 } 701 702 expected := map[string]interface{}{ 703 "policy_arns": []string(nil), 704 "role_arns": []string(nil), 705 "policy_document": value, 706 "credential_type": strings.Join([]string{iamUserCred, federationTokenCred}, ","), 707 "default_sts_ttl": int64(0), 708 "max_sts_ttl": int64(0), 709 "user_path": "", 710 "permissions_boundary_arn": "", 711 } 712 if !reflect.DeepEqual(resp.Data, expected) { 713 return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected) 714 } 715 return nil 716 }, 717 } 718} 719 720const testDynamoPolicy = `{ 721 "Version": "2012-10-17", 722 "Statement": [ 723 { 724 "Sid": "Stmt1426528957000", 725 "Effect": "Allow", 726 "Action": [ 727 "dynamodb:List*" 728 ], 729 "Resource": [ 730 "*" 731 ] 732 } 733 ] 734} 735` 736 737const adminAccessPolicyArn = "arn:aws:iam::aws:policy/AdministratorAccess" 738const ec2PolicyArn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess" 739const iamPolicyArn = "arn:aws:iam::aws:policy/IAMReadOnlyAccess" 740const dynamoPolicyArn = "arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess" 741 742func testAccStepWriteRole(t *testing.T, name string, data map[string]interface{}) logicaltest.TestStep { 743 return logicaltest.TestStep{ 744 Operation: logical.UpdateOperation, 745 Path: "roles/" + name, 746 Data: data, 747 } 748} 749 750func testAccStepReadRole(t *testing.T, name string, expected map[string]interface{}) logicaltest.TestStep { 751 return logicaltest.TestStep{ 752 Operation: logical.ReadOperation, 753 Path: "roles/" + name, 754 Check: func(resp *logical.Response) error { 755 if resp == nil { 756 if expected == nil { 757 return nil 758 } 759 return fmt.Errorf("bad: nil response") 760 } 761 if !reflect.DeepEqual(resp.Data, expected) { 762 return fmt.Errorf("bad: got %#v\nexpected: %#v", resp.Data, expected) 763 } 764 return nil 765 }, 766 } 767} 768 769func testAccStepWriteArnPolicyRef(t *testing.T, name string, arn string) logicaltest.TestStep { 770 return logicaltest.TestStep{ 771 Operation: logical.UpdateOperation, 772 Path: "roles/" + name, 773 Data: map[string]interface{}{ 774 "arn": ec2PolicyArn, 775 }, 776 } 777} 778 779func TestBackend_basicPolicyArnRef(t *testing.T) { 780 t.Parallel() 781 logicaltest.Test(t, logicaltest.TestCase{ 782 AcceptanceTest: true, 783 PreCheck: func() { testAccPreCheck(t) }, 784 LogicalBackend: getBackend(t), 785 Steps: []logicaltest.TestStep{ 786 testAccStepConfig(t), 787 testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn), 788 testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest}), 789 }, 790 }) 791} 792 793func TestBackend_iamUserManagedInlinePolicies(t *testing.T) { 794 t.Parallel() 795 compacted, err := compactJSON(testDynamoPolicy) 796 if err != nil { 797 t.Fatalf("bad: %#v", err) 798 } 799 roleData := map[string]interface{}{ 800 "policy_document": testDynamoPolicy, 801 "policy_arns": []string{ec2PolicyArn, iamPolicyArn}, 802 "credential_type": iamUserCred, 803 "user_path": "/path/", 804 } 805 expectedRoleData := map[string]interface{}{ 806 "policy_document": compacted, 807 "policy_arns": []string{ec2PolicyArn, iamPolicyArn}, 808 "credential_type": iamUserCred, 809 "role_arns": []string(nil), 810 "default_sts_ttl": int64(0), 811 "max_sts_ttl": int64(0), 812 "user_path": "/path/", 813 "permissions_boundary_arn": "", 814 } 815 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 testAccStepWriteRole(t, "test", roleData), 823 testAccStepReadRole(t, "test", expectedRoleData), 824 testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest, assertCreatedIAMUser}), 825 testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, listIamUsersTest, listDynamoTablesTest}), 826 }, 827 }) 828} 829 830func TestBackend_AssumedRoleWithPolicyDoc(t *testing.T) { 831 t.Parallel() 832 roleName := generateUniqueName(t.Name()) 833 // This looks a bit curious. The policy document and the role document act 834 // as a logical intersection of policies. The role allows ec2:Describe* 835 // (among other permissions). This policy allows everything BUT 836 // ec2:DescribeAvailabilityZones. Thus, the logical intersection of the two 837 // is all ec2:Describe* EXCEPT ec2:DescribeAvailabilityZones, and so the 838 // describeAZs call should fail 839 allowAllButDescribeAzs := ` 840{ 841 "Version": "2012-10-17", 842 "Statement": [{ 843 "Effect": "Allow", 844 "NotAction": "ec2:DescribeAvailabilityZones", 845 "Resource": "*" 846 }] 847} 848` 849 awsAccountID, err := getAccountID() 850 if err != nil { 851 t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err) 852 t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping") 853 } 854 roleData := map[string]interface{}{ 855 "policy_document": allowAllButDescribeAzs, 856 "role_arns": []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)}, 857 "credential_type": assumedRoleCred, 858 } 859 logicaltest.Test(t, logicaltest.TestCase{ 860 AcceptanceTest: true, 861 PreCheck: func() { 862 testAccPreCheck(t) 863 createRole(t, roleName, awsAccountID, []string{ec2PolicyArn}) 864 // Sleep sometime because AWS is eventually consistent 865 log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") 866 time.Sleep(10 * time.Second) 867 }, 868 LogicalBackend: getBackend(t), 869 Steps: []logicaltest.TestStep{ 870 testAccStepConfig(t), 871 testAccStepWriteRole(t, "test", roleData), 872 testAccStepRead(t, "sts", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}), 873 testAccStepRead(t, "creds", "test", []credentialTestFunc{describeInstancesTest, describeAzsTestUnauthorized}), 874 }, 875 Teardown: func() error { 876 return deleteTestRole(roleName) 877 }, 878 }) 879} 880 881func TestBackend_AssumedRoleWithPolicyARN(t *testing.T) { 882 t.Parallel() 883 roleName := generateUniqueName(t.Name()) 884 885 awsAccountID, err := getAccountID() 886 if err != nil { 887 t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err) 888 t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping") 889 } 890 roleData := map[string]interface{}{ 891 "policy_arns": iamPolicyArn, 892 "role_arns": []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)}, 893 "credential_type": assumedRoleCred, 894 } 895 logicaltest.Test(t, logicaltest.TestCase{ 896 AcceptanceTest: true, 897 PreCheck: func() { 898 testAccPreCheck(t) 899 createRole(t, roleName, awsAccountID, []string{ec2PolicyArn, iamPolicyArn}) 900 log.Printf("[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{listIamUsersTest, describeAzsTestUnauthorized}), 908 testAccStepRead(t, "creds", "test", []credentialTestFunc{listIamUsersTest, describeAzsTestUnauthorized}), 909 }, 910 Teardown: func() error { 911 return deleteTestRole(roleName) 912 }, 913 }) 914} 915 916func TestBackend_FederationTokenWithPolicyARN(t *testing.T) { 917 t.Parallel() 918 userName := generateUniqueName(t.Name()) 919 accessKey := &awsAccessKey{} 920 921 roleData := map[string]interface{}{ 922 "policy_arns": dynamoPolicyArn, 923 "credential_type": federationTokenCred, 924 } 925 logicaltest.Test(t, logicaltest.TestCase{ 926 AcceptanceTest: true, 927 PreCheck: func() { 928 testAccPreCheck(t) 929 createUser(t, userName, accessKey) 930 // Sleep sometime because AWS is eventually consistent 931 log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") 932 time.Sleep(10 * time.Second) 933 }, 934 LogicalBackend: getBackend(t), 935 Steps: []logicaltest.TestStep{ 936 testAccStepConfigWithCreds(t, accessKey), 937 testAccStepWriteRole(t, "test", roleData), 938 testAccStepRead(t, "sts", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}), 939 testAccStepRead(t, "creds", "test", []credentialTestFunc{listDynamoTablesTest, describeAzsTestUnauthorized}), 940 }, 941 Teardown: func() error { 942 return deleteTestUser(accessKey, userName) 943 }, 944 }) 945} 946 947func TestBackend_RoleDefaultSTSTTL(t *testing.T) { 948 t.Parallel() 949 roleName := generateUniqueName(t.Name()) 950 minAwsAssumeRoleDuration := 900 951 awsAccountID, err := getAccountID() 952 if err != nil { 953 t.Logf("Unable to retrive user via sts:GetCallerIdentity: %#v", err) 954 t.Skip("Could not determine AWS account ID from sts:GetCallerIdentity for acceptance tests, skipping") 955 } 956 roleData := map[string]interface{}{ 957 "role_arns": []string{fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, roleName)}, 958 "credential_type": assumedRoleCred, 959 "default_sts_ttl": minAwsAssumeRoleDuration, 960 "max_sts_ttl": minAwsAssumeRoleDuration, 961 } 962 logicaltest.Test(t, logicaltest.TestCase{ 963 AcceptanceTest: true, 964 PreCheck: func() { 965 testAccPreCheck(t) 966 createRole(t, roleName, awsAccountID, []string{ec2PolicyArn}) 967 log.Println("[WARN] Sleeping for 10 seconds waiting for AWS...") 968 time.Sleep(10 * time.Second) 969 }, 970 LogicalBackend: getBackend(t), 971 Steps: []logicaltest.TestStep{ 972 testAccStepConfig(t), 973 testAccStepWriteRole(t, "test", roleData), 974 testAccStepReadTTL("test", time.Duration(minAwsAssumeRoleDuration)*time.Second), // allow a little slack 975 }, 976 Teardown: func() error { 977 return deleteTestRole(roleName) 978 }, 979 }) 980} 981 982func TestBackend_policyArnCrud(t *testing.T) { 983 t.Parallel() 984 logicaltest.Test(t, logicaltest.TestCase{ 985 AcceptanceTest: true, 986 LogicalBackend: getBackend(t), 987 Steps: []logicaltest.TestStep{ 988 testAccStepConfig(t), 989 testAccStepWriteArnPolicyRef(t, "test", ec2PolicyArn), 990 testAccStepReadArnPolicy(t, "test", ec2PolicyArn), 991 testAccStepDeletePolicy(t, "test"), 992 testAccStepReadArnPolicy(t, "test", ""), 993 }, 994 }) 995} 996 997func testAccStepReadArnPolicy(t *testing.T, name string, value string) logicaltest.TestStep { 998 return logicaltest.TestStep{ 999 Operation: logical.ReadOperation, 1000 Path: "roles/" + name, 1001 Check: func(resp *logical.Response) error { 1002 if resp == nil { 1003 if value == "" { 1004 return nil 1005 } 1006 1007 return fmt.Errorf("bad: %#v", resp) 1008 } 1009 1010 expected := map[string]interface{}{ 1011 "policy_arns": []string{value}, 1012 "role_arns": []string(nil), 1013 "policy_document": "", 1014 "credential_type": iamUserCred, 1015 "default_sts_ttl": int64(0), 1016 "max_sts_ttl": int64(0), 1017 "user_path": "", 1018 "permissions_boundary_arn": "", 1019 } 1020 if !reflect.DeepEqual(resp.Data, expected) { 1021 return fmt.Errorf("bad: got: %#v\nexpected: %#v", resp.Data, expected) 1022 } 1023 1024 return nil 1025 }, 1026 } 1027} 1028 1029func testAccStepWriteArnRoleRef(t *testing.T, vaultRoleName, awsRoleName, awsAccountID string) logicaltest.TestStep { 1030 return logicaltest.TestStep{ 1031 Operation: logical.UpdateOperation, 1032 Path: "roles/" + vaultRoleName, 1033 Data: map[string]interface{}{ 1034 "arn": fmt.Sprintf("arn:aws:iam::%s:role/%s", awsAccountID, awsRoleName), 1035 }, 1036 } 1037} 1038 1039func generateUniqueName(prefix string) string { 1040 return testhelpers.RandomWithPrefix(prefix) 1041} 1042 1043type awsAccessKey struct { 1044 AccessKeyID string 1045 SecretAccessKey string 1046} 1047 1048type credentialTestFunc func(string, string, string) error 1049