github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/auth_helpers_test.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "testing" 13 "time" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/awserr" 17 awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" 18 "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" 19 "github.com/aws/aws-sdk-go/aws/session" 20 "github.com/aws/aws-sdk-go/service/iam" 21 "github.com/aws/aws-sdk-go/service/sts" 22 ) 23 24 func TestAWSGetAccountInfo_shouldBeValid_fromEC2Role(t *testing.T) { 25 resetEnv := unsetEnv(t) 26 defer resetEnv() 27 // capture the test server's close method, to call after the test returns 28 awsTs := awsEnv(t) 29 defer awsTs() 30 31 iamEndpoints := []*iamEndpoint{} 32 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 33 defer ts() 34 35 part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName) 36 if err != nil { 37 t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) 38 } 39 40 expectedPart := "aws" 41 if part != expectedPart { 42 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 43 } 44 45 expectedAccountId := "123456789013" 46 if id != expectedAccountId { 47 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 48 } 49 } 50 51 func TestAWSGetAccountInfo_shouldBeValid_EC2RoleHasPriority(t *testing.T) { 52 resetEnv := unsetEnv(t) 53 defer resetEnv() 54 // capture the test server's close method, to call after the test returns 55 awsTs := awsEnv(t) 56 defer awsTs() 57 58 iamEndpoints := []*iamEndpoint{ 59 { 60 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 61 Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, 62 }, 63 } 64 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 65 defer ts() 66 67 part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName) 68 if err != nil { 69 t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) 70 } 71 72 expectedPart := "aws" 73 if part != expectedPart { 74 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 75 } 76 77 expectedAccountId := "123456789013" 78 if id != expectedAccountId { 79 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 80 } 81 } 82 83 func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) { 84 iamEndpoints := []*iamEndpoint{ 85 { 86 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 87 Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, 88 }, 89 } 90 91 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 92 defer ts() 93 94 part, id, err := GetAccountInfo(iamConn, stsConn, "") 95 if err != nil { 96 t.Fatalf("Getting account ID via GetUser failed: %s", err) 97 } 98 99 expectedPart := "aws" 100 if part != expectedPart { 101 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 102 } 103 104 expectedAccountId := "123456789012" 105 if id != expectedAccountId { 106 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 107 } 108 } 109 110 func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) { 111 iamEndpoints := []*iamEndpoint{ 112 { 113 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 114 Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, 115 }, 116 { 117 Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, 118 Response: &iamResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, 119 }, 120 } 121 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 122 defer ts() 123 124 part, id, err := GetAccountInfo(iamConn, stsConn, "") 125 if err != nil { 126 t.Fatalf("Getting account ID via GetUser failed: %s", err) 127 } 128 129 expectedPart := "aws" 130 if part != expectedPart { 131 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 132 } 133 134 expectedAccountId := "123456789012" 135 if id != expectedAccountId { 136 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 137 } 138 } 139 140 func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) { 141 iamEndpoints := []*iamEndpoint{ 142 { 143 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 144 Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, 145 }, 146 { 147 Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, 148 Response: &iamResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"}, 149 }, 150 { 151 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 152 Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, 153 }, 154 } 155 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 156 defer ts() 157 158 part, id, err := GetAccountInfo(iamConn, stsConn, "") 159 if err != nil { 160 t.Fatalf("Getting account ID via ListRoles failed: %s", err) 161 } 162 163 expectedPart := "aws" 164 if part != expectedPart { 165 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 166 } 167 168 expectedAccountId := "123456789012" 169 if id != expectedAccountId { 170 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 171 } 172 } 173 174 func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) { 175 iamEndpoints := []*iamEndpoint{ 176 { 177 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 178 Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, 179 }, 180 { 181 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 182 Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, 183 }, 184 } 185 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 186 defer ts() 187 188 part, id, err := GetAccountInfo(iamConn, stsConn, "") 189 if err != nil { 190 t.Fatalf("Getting account ID via ListRoles failed: %s", err) 191 } 192 193 expectedPart := "aws" 194 if part != expectedPart { 195 t.Fatalf("Expected partition: %s, given: %s", expectedPart, part) 196 } 197 198 expectedAccountId := "123456789012" 199 if id != expectedAccountId { 200 t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) 201 } 202 } 203 204 func TestAWSGetAccountInfo_shouldError_unauthorizedFromIam(t *testing.T) { 205 iamEndpoints := []*iamEndpoint{ 206 { 207 Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, 208 Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, 209 }, 210 { 211 Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, 212 Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, 213 }, 214 } 215 ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) 216 defer ts() 217 218 part, id, err := GetAccountInfo(iamConn, stsConn, "") 219 if err == nil { 220 t.Fatal("Expected error when getting account ID") 221 } 222 223 if part != "" { 224 t.Fatalf("Expected no partition, given: %s", part) 225 } 226 227 if id != "" { 228 t.Fatalf("Expected no account ID, given: %s", id) 229 } 230 } 231 232 func TestAWSParseAccountInfoFromArn(t *testing.T) { 233 validArn := "arn:aws:iam::101636750127:instance-profile/aws-elasticbeanstalk-ec2-role" 234 expectedPart := "aws" 235 expectedId := "101636750127" 236 part, id, err := parseAccountInfoFromArn(validArn) 237 if err != nil { 238 t.Fatalf("Expected no error when parsing valid ARN: %s", err) 239 } 240 if part != expectedPart { 241 t.Fatalf("Parsed part doesn't match with expected (%q != %q)", part, expectedPart) 242 } 243 if id != expectedId { 244 t.Fatalf("Parsed id doesn't match with expected (%q != %q)", id, expectedId) 245 } 246 247 invalidArn := "blablah" 248 part, id, err = parseAccountInfoFromArn(invalidArn) 249 if err == nil { 250 t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn) 251 } 252 } 253 254 func TestAWSGetCredentials_shouldError(t *testing.T) { 255 resetEnv := unsetEnv(t) 256 defer resetEnv() 257 cfg := Config{} 258 259 c, err := GetCredentials(&cfg) 260 if awsErr, ok := err.(awserr.Error); ok { 261 if awsErr.Code() != "NoCredentialProviders" { 262 t.Fatal("Expected NoCredentialProviders error") 263 } 264 } 265 _, err = c.Get() 266 if awsErr, ok := err.(awserr.Error); ok { 267 if awsErr.Code() != "NoCredentialProviders" { 268 t.Fatal("Expected NoCredentialProviders error") 269 } 270 } 271 if err == nil { 272 t.Fatal("Expected an error with empty env, keys, and IAM in AWS Config") 273 } 274 } 275 276 func TestAWSGetCredentials_shouldBeStatic(t *testing.T) { 277 simple := []struct { 278 Key, Secret, Token string 279 }{ 280 { 281 Key: "test", 282 Secret: "secret", 283 }, { 284 Key: "test", 285 Secret: "test", 286 Token: "test", 287 }, 288 } 289 290 for _, c := range simple { 291 cfg := Config{ 292 AccessKey: c.Key, 293 SecretKey: c.Secret, 294 Token: c.Token, 295 } 296 297 creds, err := GetCredentials(&cfg) 298 if err != nil { 299 t.Fatalf("Error gettings creds: %s", err) 300 } 301 if creds == nil { 302 t.Fatal("Expected a static creds provider to be returned") 303 } 304 305 v, err := creds.Get() 306 if err != nil { 307 t.Fatalf("Error gettings creds: %s", err) 308 } 309 310 if v.AccessKeyID != c.Key { 311 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 312 } 313 if v.SecretAccessKey != c.Secret { 314 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 315 } 316 if v.SessionToken != c.Token { 317 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 318 } 319 } 320 } 321 322 // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform 323 // from an EC2 instance, without environment variables or manually supplied 324 // credentials. 325 func TestAWSGetCredentials_shouldIAM(t *testing.T) { 326 // clear AWS_* environment variables 327 resetEnv := unsetEnv(t) 328 defer resetEnv() 329 330 // capture the test server's close method, to call after the test returns 331 ts := awsEnv(t) 332 defer ts() 333 334 // An empty config, no key supplied 335 cfg := Config{} 336 337 creds, err := GetCredentials(&cfg) 338 if err != nil { 339 t.Fatalf("Error gettings creds: %s", err) 340 } 341 if creds == nil { 342 t.Fatal("Expected a static creds provider to be returned") 343 } 344 345 v, err := creds.Get() 346 if err != nil { 347 t.Fatalf("Error gettings creds: %s", err) 348 } 349 if v.AccessKeyID != "somekey" { 350 t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) 351 } 352 if v.SecretAccessKey != "somesecret" { 353 t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) 354 } 355 if v.SessionToken != "sometoken" { 356 t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) 357 } 358 } 359 360 // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform 361 // from an EC2 instance, without environment variables or manually supplied 362 // credentials. 363 func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) { 364 resetEnv := unsetEnv(t) 365 defer resetEnv() 366 // capture the test server's close method, to call after the test returns 367 ts := awsEnv(t) 368 defer ts() 369 simple := []struct { 370 Key, Secret, Token string 371 }{ 372 { 373 Key: "test", 374 Secret: "secret", 375 }, { 376 Key: "test", 377 Secret: "test", 378 Token: "test", 379 }, 380 } 381 382 for _, c := range simple { 383 cfg := Config{ 384 AccessKey: c.Key, 385 SecretKey: c.Secret, 386 Token: c.Token, 387 } 388 389 creds, err := GetCredentials(&cfg) 390 if err != nil { 391 t.Fatalf("Error gettings creds: %s", err) 392 } 393 if creds == nil { 394 t.Fatal("Expected a static creds provider to be returned") 395 } 396 397 v, err := creds.Get() 398 if err != nil { 399 t.Fatalf("Error gettings creds: %s", err) 400 } 401 if v.AccessKeyID != c.Key { 402 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) 403 } 404 if v.SecretAccessKey != c.Secret { 405 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) 406 } 407 if v.SessionToken != c.Token { 408 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) 409 } 410 } 411 } 412 413 func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) { 414 resetEnv := unsetEnv(t) 415 defer resetEnv() 416 // capture the test server's close method, to call after the test returns 417 ts := invalidAwsEnv(t) 418 defer ts() 419 420 creds, err := GetCredentials(&Config{}) 421 if err != nil { 422 t.Fatalf("Error gettings creds: %s", err) 423 } 424 if creds == nil { 425 t.Fatal("Expected a static creds provider to be returned") 426 } 427 428 v, err := creds.Get() 429 if err == nil { 430 t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint") 431 } 432 433 if v.ProviderName != "" { 434 t.Fatalf("Expected provider name to be empty, %q given", v.ProviderName) 435 } 436 } 437 438 func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) { 439 resetEnv := unsetEnv(t) 440 defer resetEnv() 441 // capture the test server's close method, to call after the test returns 442 ts := invalidAwsEnv(t) 443 defer ts() 444 445 creds, err := GetCredentials(&Config{AccessKey: "accessKey", SecretKey: "secretKey"}) 446 if err != nil { 447 t.Fatalf("Error gettings creds: %s", err) 448 } 449 v, err := creds.Get() 450 if err != nil { 451 t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err) 452 } 453 if creds == nil { 454 t.Fatal("Expected a static creds provider to be returned") 455 } 456 457 if v.ProviderName != "StaticProvider" { 458 t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName) 459 } 460 461 if v.AccessKeyID != "accessKey" { 462 t.Fatalf("Static Access Key %q doesn't match: %s", "accessKey", v.AccessKeyID) 463 } 464 465 if v.SecretAccessKey != "secretKey" { 466 t.Fatalf("Static Secret Key %q doesn't match: %s", "secretKey", v.SecretAccessKey) 467 } 468 } 469 470 func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) { 471 resetEnv := unsetEnv(t) 472 defer resetEnv() 473 // capture the test server's close method, to call after the test returns 474 ts := awsEnv(t) 475 defer ts() 476 477 creds, err := GetCredentials(&Config{}) 478 if err != nil { 479 t.Fatalf("Error gettings creds: %s", err) 480 } 481 if creds == nil { 482 t.Fatal("Expected an EC2Role creds provider to be returned") 483 } 484 485 v, err := creds.Get() 486 if err != nil { 487 t.Fatalf("Expected no error when getting creds: %s", err) 488 } 489 expectedProvider := "EC2RoleProvider" 490 if v.ProviderName != expectedProvider { 491 t.Fatalf("Expected provider name to be %q, %q given", 492 expectedProvider, v.ProviderName) 493 } 494 } 495 496 var credentialsFileContents = `[myprofile] 497 aws_access_key_id = accesskey 498 aws_secret_access_key = secretkey 499 ` 500 501 func TestAWSGetCredentials_shouldBeShared(t *testing.T) { 502 file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred") 503 if err != nil { 504 t.Fatalf("Error writing temporary credentials file: %s", err) 505 } 506 _, err = file.WriteString(credentialsFileContents) 507 if err != nil { 508 t.Fatalf("Error writing temporary credentials to file: %s", err) 509 } 510 err = file.Close() 511 if err != nil { 512 t.Fatalf("Error closing temporary credentials file: %s", err) 513 } 514 515 defer os.Remove(file.Name()) 516 517 resetEnv := unsetEnv(t) 518 defer resetEnv() 519 520 if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil { 521 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 522 } 523 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil { 524 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 525 } 526 527 creds, err := GetCredentials(&Config{Profile: "myprofile", CredsFilename: file.Name()}) 528 if err != nil { 529 t.Fatalf("Error gettings creds: %s", err) 530 } 531 if creds == nil { 532 t.Fatal("Expected a provider chain to be returned") 533 } 534 535 v, err := creds.Get() 536 if err != nil { 537 t.Fatalf("Error gettings creds: %s", err) 538 } 539 540 if v.AccessKeyID != "accesskey" { 541 t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 542 } 543 544 if v.SecretAccessKey != "secretkey" { 545 t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) 546 } 547 } 548 549 func TestAWSGetCredentials_shouldBeENV(t *testing.T) { 550 // need to set the environment variables to a dummy string, as we don't know 551 // what they may be at runtime without hardcoding here 552 s := "some_env" 553 resetEnv := setEnv(s, t) 554 555 defer resetEnv() 556 557 cfg := Config{} 558 creds, err := GetCredentials(&cfg) 559 if err != nil { 560 t.Fatalf("Error gettings creds: %s", err) 561 } 562 if creds == nil { 563 t.Fatalf("Expected a static creds provider to be returned") 564 } 565 566 v, err := creds.Get() 567 if err != nil { 568 t.Fatalf("Error gettings creds: %s", err) 569 } 570 if v.AccessKeyID != s { 571 t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID) 572 } 573 if v.SecretAccessKey != s { 574 t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey) 575 } 576 if v.SessionToken != s { 577 t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken) 578 } 579 } 580 581 // unsetEnv unsets environment variables for testing a "clean slate" with no 582 // credentials in the environment 583 func unsetEnv(t *testing.T) func() { 584 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 585 // we need to have them and restore them after 586 e := getEnv() 587 if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil { 588 t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err) 589 } 590 if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil { 591 t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err) 592 } 593 if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil { 594 t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err) 595 } 596 if err := os.Unsetenv("AWS_PROFILE"); err != nil { 597 t.Fatalf("Error unsetting env var AWS_PROFILE: %s", err) 598 } 599 if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil { 600 t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 601 } 602 603 return func() { 604 // re-set all the envs we unset above 605 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 606 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 607 } 608 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 609 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 610 } 611 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 612 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 613 } 614 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 615 t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) 616 } 617 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil { 618 t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) 619 } 620 } 621 } 622 623 func setEnv(s string, t *testing.T) func() { 624 e := getEnv() 625 // Set all the envs to a dummy value 626 if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil { 627 t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err) 628 } 629 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil { 630 t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err) 631 } 632 if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil { 633 t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err) 634 } 635 if err := os.Setenv("AWS_PROFILE", s); err != nil { 636 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 637 } 638 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 639 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 640 } 641 642 return func() { 643 // re-set all the envs we unset above 644 if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { 645 t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) 646 } 647 if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { 648 t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) 649 } 650 if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { 651 t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) 652 } 653 if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { 654 t.Fatalf("Error setting env var AWS_PROFILE: %s", err) 655 } 656 if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { 657 t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) 658 } 659 } 660 } 661 662 // awsEnv establishes a httptest server to mock out the internal AWS Metadata 663 // service. IAM Credentials are retrieved by the EC2RoleProvider, which makes 664 // API calls to this internal URL. By replacing the server with a test server, 665 // we can simulate an AWS environment 666 func awsEnv(t *testing.T) func() { 667 routes := routes{} 668 if err := json.Unmarshal([]byte(metadataApiRoutes), &routes); err != nil { 669 t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) 670 } 671 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 672 w.Header().Set("Content-Type", "text/plain") 673 w.Header().Add("Server", "MockEC2") 674 log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI) 675 for _, e := range routes.Endpoints { 676 if r.RequestURI == e.Uri { 677 fmt.Fprintln(w, e.Body) 678 w.WriteHeader(200) 679 return 680 } 681 } 682 w.WriteHeader(400) 683 })) 684 685 os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") 686 return ts.Close 687 } 688 689 // invalidAwsEnv establishes a httptest server to simulate behaviour 690 // when endpoint doesn't respond as expected 691 func invalidAwsEnv(t *testing.T) func() { 692 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 693 w.WriteHeader(400) 694 })) 695 696 os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") 697 return ts.Close 698 } 699 700 // getMockedAwsIamStsApi establishes a httptest server to simulate behaviour 701 // of a real AWS' IAM & STS server 702 func getMockedAwsIamStsApi(endpoints []*iamEndpoint) (func(), *iam.IAM, *sts.STS) { 703 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 704 buf := new(bytes.Buffer) 705 buf.ReadFrom(r.Body) 706 requestBody := buf.String() 707 708 log.Printf("[DEBUG] Received API %q request to %q: %s", 709 r.Method, r.RequestURI, requestBody) 710 711 for _, e := range endpoints { 712 if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body { 713 log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body) 714 715 w.WriteHeader(e.Response.StatusCode) 716 w.Header().Set("Content-Type", e.Response.ContentType) 717 w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a") 718 w.Header().Set("Date", time.Now().Format(time.RFC1123)) 719 720 fmt.Fprintln(w, e.Response.Body) 721 return 722 } 723 } 724 725 w.WriteHeader(400) 726 return 727 })) 728 729 sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "") 730 731 sess, err := session.NewSession(&aws.Config{ 732 Credentials: sc, 733 Region: aws.String("us-east-1"), 734 Endpoint: aws.String(ts.URL), 735 CredentialsChainVerboseErrors: aws.Bool(true), 736 }) 737 if err != nil { 738 panic(fmt.Sprintf("Error creating AWS Session: %s", err)) 739 } 740 iamConn := iam.New(sess) 741 stsConn := sts.New(sess) 742 return ts.Close, iamConn, stsConn 743 } 744 745 func getEnv() *currentEnv { 746 // Grab any existing AWS keys and preserve. In some tests we'll unset these, so 747 // we need to have them and restore them after 748 return ¤tEnv{ 749 Key: os.Getenv("AWS_ACCESS_KEY_ID"), 750 Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"), 751 Token: os.Getenv("AWS_SESSION_TOKEN"), 752 Profile: os.Getenv("AWS_PROFILE"), 753 CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), 754 } 755 } 756 757 // struct to preserve the current environment 758 type currentEnv struct { 759 Key, Secret, Token, Profile, CredsFilename string 760 } 761 762 type routes struct { 763 Endpoints []*endpoint `json:"endpoints"` 764 } 765 type endpoint struct { 766 Uri string `json:"uri"` 767 Body string `json:"body"` 768 } 769 770 const metadataApiRoutes = ` 771 { 772 "endpoints": [ 773 { 774 "uri": "/latest/meta-data/instance-id", 775 "body": "mock-instance-id" 776 }, 777 { 778 "uri": "/latest/meta-data/iam/info", 779 "body": "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::123456789013:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}" 780 }, 781 { 782 "uri": "/latest/meta-data/iam/security-credentials", 783 "body": "test_role" 784 }, 785 { 786 "uri": "/latest/meta-data/iam/security-credentials/test_role", 787 "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}" 788 } 789 ] 790 } 791 ` 792 793 type iamEndpoint struct { 794 Request *iamRequest 795 Response *iamResponse 796 } 797 798 type iamRequest struct { 799 Method string 800 Uri string 801 Body string 802 } 803 804 type iamResponse struct { 805 StatusCode int 806 Body string 807 ContentType string 808 } 809 810 const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 811 <GetUserResult> 812 <User> 813 <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId> 814 <Path>/division_abc/subdivision_xyz/</Path> 815 <UserName>Bob</UserName> 816 <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob</Arn> 817 <CreateDate>2013-10-02T17:01:44Z</CreateDate> 818 <PasswordLastUsed>2014-10-10T14:37:51Z</PasswordLastUsed> 819 </User> 820 </GetUserResult> 821 <ResponseMetadata> 822 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 823 </ResponseMetadata> 824 </GetUserResponse>` 825 826 const iamResponse_GetUser_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 827 <Error> 828 <Type>Sender</Type> 829 <Code>AccessDenied</Code> 830 <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob</Message> 831 </Error> 832 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 833 </ErrorResponse>` 834 835 const stsResponse_GetCallerIdentity_valid = `<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/"> 836 <GetCallerIdentityResult> 837 <Arn>arn:aws:iam::123456789012:user/Alice</Arn> 838 <UserId>AKIAI44QH8DHBEXAMPLE</UserId> 839 <Account>123456789012</Account> 840 </GetCallerIdentityResult> 841 <ResponseMetadata> 842 <RequestId>01234567-89ab-cdef-0123-456789abcdef</RequestId> 843 </ResponseMetadata> 844 </GetCallerIdentityResponse>` 845 846 const stsResponse_GetCallerIdentity_unauthorized = `<ErrorResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/"> 847 <Error> 848 <Type>Sender</Type> 849 <Code>AccessDenied</Code> 850 <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: sts:GetCallerIdentity</Message> 851 </Error> 852 <RequestId>01234567-89ab-cdef-0123-456789abcdef</RequestId> 853 </ErrorResponse>` 854 855 const iamResponse_GetUser_federatedFailure = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 856 <Error> 857 <Type>Sender</Type> 858 <Code>ValidationError</Code> 859 <Message>Must specify userName when calling with non-User credentials</Message> 860 </Error> 861 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 862 </ErrorResponse>` 863 864 const iamResponse_ListRoles_valid = `<ListRolesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 865 <ListRolesResult> 866 <IsTruncated>true</IsTruncated> 867 <Marker>AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE</Marker> 868 <Roles> 869 <member> 870 <Path>/</Path> 871 <AssumeRolePolicyDocument>%7B%22Version%22%3A%222008-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D</AssumeRolePolicyDocument> 872 <RoleId>AROACKCEVSQ6C2EXAMPLE</RoleId> 873 <RoleName>elasticbeanstalk-role</RoleName> 874 <Arn>arn:aws:iam::123456789012:role/elasticbeanstalk-role</Arn> 875 <CreateDate>2013-10-02T17:01:44Z</CreateDate> 876 </member> 877 </Roles> 878 </ListRolesResult> 879 <ResponseMetadata> 880 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 881 </ResponseMetadata> 882 </ListRolesResponse>` 883 884 const iamResponse_ListRoles_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> 885 <Error> 886 <Type>Sender</Type> 887 <Code>AccessDenied</Code> 888 <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/</Message> 889 </Error> 890 <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId> 891 </ErrorResponse>`