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