diff --git a/awsauth.go b/awsauth.go index daab3f8e..d559f0c9 100644 --- a/awsauth.go +++ b/awsauth.go @@ -79,7 +79,7 @@ func GetAccountIDAndPartitionFromEC2Metadata() (string, string, error) { setOptionalEndpoint(cfg) sess, err := session.NewSession(cfg) if err != nil { - return "", "", fmt.Errorf("error creating EC2 Metadata session: %s", err) + return "", "", fmt.Errorf("error creating EC2 Metadata session: %w", err) } metadataClient := ec2metadata.New(sess) @@ -88,7 +88,7 @@ func GetAccountIDAndPartitionFromEC2Metadata() (string, string, error) { // We can end up here if there's an issue with the instance metadata service // or if we're getting credentials from AdRoll's Hologram (in which case IAMInfo will // error out). - err = fmt.Errorf("failed getting account information via EC2 Metadata IAM information: %s", err) + err = fmt.Errorf("failed getting account information via EC2 Metadata IAM information: %w", err) log.Printf("[DEBUG] %s", err) return "", "", err } @@ -111,7 +111,7 @@ func GetAccountIDAndPartitionFromIAMGetUser(iamconn *iam.IAM) (string, string, e return "", "", nil } } - err = fmt.Errorf("failed getting account information via iam:GetUser: %s", err) + err = fmt.Errorf("failed getting account information via iam:GetUser: %w", err) log.Printf("[DEBUG] %s", err) return "", "", err } @@ -134,7 +134,7 @@ func GetAccountIDAndPartitionFromIAMListRoles(iamconn *iam.IAM) (string, string, MaxItems: aws.Int64(int64(1)), }) if err != nil { - err = fmt.Errorf("failed getting account information via iam:ListRoles: %s", err) + err = fmt.Errorf("failed getting account information via iam:ListRoles: %w", err) log.Printf("[DEBUG] %s", err) return "", "", err } @@ -155,7 +155,7 @@ func GetAccountIDAndPartitionFromSTSGetCallerIdentity(stsconn *sts.STS) (string, output, err := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{}) if err != nil { - return "", "", fmt.Errorf("error calling sts:GetCallerIdentity: %s", err) + return "", "", fmt.Errorf("error calling sts:GetCallerIdentity: %w", err) } if output == nil || output.Arn == nil { @@ -184,16 +184,17 @@ func GetCredentialsFromSession(c *Config) (*awsCredentials.Credentials, error) { var sess *session.Session var err error if c.Profile == "" { - sess, err = session.NewSession() + sess, err = session.NewSession(&aws.Config{EndpointResolver: c.EndpointResolver()}) if err != nil { return nil, ErrNoValidCredentialSources } } else { options := &session.Options{ Config: aws.Config{ - HTTPClient: cleanhttp.DefaultClient(), - MaxRetries: aws.Int(0), - Region: aws.String(c.Region), + EndpointResolver: c.EndpointResolver(), + HTTPClient: cleanhttp.DefaultClient(), + MaxRetries: aws.Int(0), + Region: aws.String(c.Region), }, } options.Profile = c.Profile @@ -204,7 +205,7 @@ func GetCredentialsFromSession(c *Config) (*awsCredentials.Credentials, error) { if IsAWSErr(err, "NoCredentialProviders", "") { return nil, ErrNoValidCredentialSources } - return nil, fmt.Errorf("Error creating AWS session: %s", err) + return nil, fmt.Errorf("Error creating AWS session: %w", err) } } @@ -276,7 +277,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { ec2Session, err := session.NewSession(cfg) if err != nil { - return nil, fmt.Errorf("error creating EC2 Metadata session: %s", err) + return nil, fmt.Errorf("error creating EC2 Metadata session: %w", err) } metadataClient := ec2metadata.New(ec2Session) @@ -305,7 +306,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { return nil, err } } else { - return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) + return nil, fmt.Errorf("Error loading credentials for AWS Provider: %w", err) } } else { log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) @@ -322,16 +323,17 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { c.AssumeRoleARN, c.AssumeRoleSessionName, c.AssumeRoleExternalID, c.AssumeRolePolicy) awsConfig := &aws.Config{ - Credentials: creds, - Region: aws.String(c.Region), - MaxRetries: aws.Int(c.MaxRetries), - HTTPClient: cleanhttp.DefaultClient(), + Credentials: creds, + EndpointResolver: c.EndpointResolver(), + Region: aws.String(c.Region), + MaxRetries: aws.Int(c.MaxRetries), + HTTPClient: cleanhttp.DefaultClient(), } assumeRoleSession, err := session.NewSession(awsConfig) if err != nil { - return nil, fmt.Errorf("error creating assume role session: %s", err) + return nil, fmt.Errorf("error creating assume role session: %w", err) } stsclient := sts.New(assumeRoleSession) @@ -354,7 +356,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { assumeRoleCreds := awsCredentials.NewChainCredentials(providers) _, err = assumeRoleCreds.Get() if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { + if IsAWSErr(err, "NoCredentialProviders", "") { return nil, fmt.Errorf("The role %q cannot be assumed.\n\n"+ " There are a number of possible causes of this - the most common are:\n"+ " * The credentials used in order to assume the role are invalid\n"+ @@ -363,7 +365,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) { c.AssumeRoleARN) } - return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) + return nil, fmt.Errorf("Error loading credentials for AWS Provider: %w", err) } return assumeRoleCreds, nil diff --git a/awsauth_test.go b/awsauth_test.go index aa2c65aa..86d63720 100644 --- a/awsauth_test.go +++ b/awsauth_test.go @@ -518,14 +518,14 @@ func TestAWSGetCredentials_shouldIAM(t *testing.T) { if err != nil { t.Fatalf("Error gettings creds: %s", err) } - if v.AccessKeyID != "somekey" { - t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) + if expected, actual := "Ec2MetadataAccessKey", v.AccessKeyID; expected != actual { + t.Fatalf("expected access key (%s), got: %s", expected, actual) } - if v.SecretAccessKey != "somesecret" { - t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) + if expected, actual := "Ec2MetadataSecretKey", v.SecretAccessKey; expected != actual { + t.Fatalf("expected secret key (%s), got: %s", expected, actual) } - if v.SessionToken != "sometoken" { - t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) + if expected, actual := "Ec2MetadataSessionToken", v.SessionToken; expected != actual { + t.Fatalf("expected session token (%s), got: %s", expected, actual) } } diff --git a/awserr.go b/awserr.go index 55fcc06f..ce657679 100644 --- a/awserr.go +++ b/awserr.go @@ -1,6 +1,7 @@ package awsbase import ( + "errors" "strings" "github.com/aws/aws-sdk-go/aws/awserr" @@ -11,17 +12,13 @@ import ( // * Error.Code() matches code // * Error.Message() contains message func IsAWSErr(err error, code string, message string) bool { - awsErr, ok := err.(awserr.Error) + var awsErr awserr.Error - if !ok { - return false + if errors.As(err, &awsErr) { + return awsErr.Code() == code && strings.Contains(awsErr.Message(), message) } - if awsErr.Code() != code { - return false - } - - return strings.Contains(awsErr.Message(), message) + return false } // IsAWSErrExtended returns true if the error matches all these conditions: diff --git a/awserr_test.go b/awserr_test.go index 94a1b424..c896a73f 100644 --- a/awserr_test.go +++ b/awserr_test.go @@ -2,6 +2,7 @@ package awsbase import ( "errors" + "fmt" "testing" "github.com/aws/aws-sdk-go/aws/awserr" @@ -100,6 +101,71 @@ func TestIsAwsErr(t *testing.T) { Err: awserr.New("TestCode", "TestMessage", nil), Message: "TestMessage", }, + { + Name: "wrapped other error", + Err: fmt.Errorf("test: %w", errors.New("test")), + }, + { + Name: "wrapped other error code", + Err: fmt.Errorf("test: %w", errors.New("test")), + Code: "test", + }, + { + Name: "wrapped other error message", + Err: fmt.Errorf("test: %w", errors.New("test")), + Message: "test", + }, + { + Name: "wrapped other error code and message", + Err: fmt.Errorf("test: %w", errors.New("test")), + Code: "test", + Message: "test", + }, + { + Name: "wrapped awserr error matching code and no message", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Code: "TestCode", + Expected: true, + }, + { + Name: "wrapped awserr error matching code and matching message exact", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Code: "TestCode", + Message: "TestMessage", + Expected: true, + }, + { + Name: "wrapped awserr error matching code and matching message contains", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Code: "TestCode", + Message: "Message", + Expected: true, + }, + { + Name: "wrapped awserr error matching code and non-matching message", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Code: "TestCode", + Message: "NotMatching", + }, + { + Name: "wrapped awserr error no code", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + }, + { + Name: "wrapped awserr error no code and matching message exact", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Message: "TestMessage", + }, + { + Name: "wrapped awserr error non-matching code", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Code: "NotMatching", + }, + { + Name: "wrapped awserr error non-matching code and message exact", + Err: fmt.Errorf("test: %w", awserr.New("TestCode", "TestMessage", nil)), + Message: "TestMessage", + }, } for _, testCase := range testCases { diff --git a/endpoints.go b/endpoints.go new file mode 100644 index 00000000..c3f64d1c --- /dev/null +++ b/endpoints.go @@ -0,0 +1,46 @@ +package awsbase + +import ( + "log" + "os" + + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go/service/sts" +) + +func (c *Config) EndpointResolver() endpoints.Resolver { + resolver := func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) { + // Ensure we pass all existing information (e.g. SigningRegion) and + // only override the URL, otherwise a MissingRegion error can occur + // when aws.Config.Region is not defined. + resolvedEndpoint, err := endpoints.DefaultResolver().EndpointFor(service, region, optFns...) + + if err != nil { + return resolvedEndpoint, err + } + + switch service { + case ec2metadata.ServiceName: + if endpoint := os.Getenv("AWS_METADATA_URL"); endpoint != "" { + log.Printf("[INFO] Setting custom EC2 metadata endpoint: %s", endpoint) + resolvedEndpoint.URL = endpoint + } + case iam.ServiceName: + if endpoint := c.IamEndpoint; endpoint != "" { + log.Printf("[INFO] Setting custom IAM endpoint: %s", endpoint) + resolvedEndpoint.URL = endpoint + } + case sts.ServiceName: + if endpoint := c.StsEndpoint; endpoint != "" { + log.Printf("[INFO] Setting custom STS endpoint: %s", endpoint) + resolvedEndpoint.URL = endpoint + } + } + + return resolvedEndpoint, nil + } + + return endpoints.ResolverFunc(resolver) +} diff --git a/mock.go b/mock.go index 7584353d..e085af88 100644 --- a/mock.go +++ b/mock.go @@ -2,6 +2,7 @@ package awsbase import ( "bytes" + "encoding/json" "fmt" "log" "net/http" @@ -73,7 +74,7 @@ func awsMetadataApiMock(responses []*MetadataResponse) func() { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.Header().Add("Server", "MockEC2") - log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI) + log.Printf("[DEBUG] Mock EC2 metadata server received request: %s", r.RequestURI) for _, e := range responses { if r.RequestURI == e.Uri { fmt.Fprintln(w, e.Body) @@ -87,6 +88,29 @@ func awsMetadataApiMock(responses []*MetadataResponse) func() { return ts.Close } +// ecsCredentialsApiMock establishes a httptest server to mock out the ECS credentials API. +func ecsCredentialsApiMock() func() { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Add("Server", "MockECS") + log.Printf("[DEBUG] Mock ECS credentials server received request: %s", r.RequestURI) + if r.RequestURI == "/creds" { + _ = json.NewEncoder(w).Encode(map[string]string{ + "AccessKeyId": "EcsCredentialsAccessKey", + "Expiration": time.Now().UTC().Format(time.RFC3339), + "RoleArn": "arn:aws:iam::000000000000:role/EcsCredentials", + "SecretAccessKey": "EcsCredentialsSecretKey", + "Token": "EcsCredentialsSessionToken", + }) + return + } + w.WriteHeader(http.StatusBadRequest) + })) + + os.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", ts.URL+"/creds") + return ts.Close +} + // MockEndpoint represents a basic request and response that can be used for creating simple httptest server routes. type MockEndpoint struct { Request *MockRequest @@ -125,7 +149,7 @@ var ec2metadata_securityCredentialsEndpoints = []*MetadataResponse{ }, { Uri: "/latest/meta-data/iam/security-credentials/test_role", - Body: "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}", + Body: "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"Ec2MetadataAccessKey\",\"SecretAccessKey\":\"Ec2MetadataSecretKey\",\"Token\":\"Ec2MetadataSessionToken\"}", }, } @@ -165,6 +189,33 @@ const iamResponse_GetUser_unauthorized = ` + + + arn:aws:sts::555555555555:assumed-role/role/AssumeRoleSessionName + ARO123EXAMPLE123:AssumeRoleSessionName + + + AssumeRoleAccessKey + AssumeRoleSecretKey + AssumeRoleSessionToken + %s + + + + 01234567-89ab-cdef-0123-456789abcdef + +`, time.Now().UTC().Format(time.RFC3339)) + +const stsResponse_AssumeRole_InvalidClientTokenId = ` + + Sender + InvalidClientTokenId + The security token included in the request is invalid. + +4d0cf5ec-892a-4d3f-84e4-30e9987d9bdd +` + const stsResponse_GetCallerIdentity_valid = ` arn:aws:iam::222222222222:user/Alice diff --git a/session.go b/session.go index 881d0ad1..adbc6a01 100644 --- a/session.go +++ b/session.go @@ -30,9 +30,10 @@ const ( func GetSessionOptions(c *Config) (*session.Options, error) { options := &session.Options{ Config: aws.Config{ - HTTPClient: cleanhttp.DefaultClient(), - MaxRetries: aws.Int(0), - Region: aws.String(c.Region), + EndpointResolver: c.EndpointResolver(), + HTTPClient: cleanhttp.DefaultClient(), + MaxRetries: aws.Int(0), + Region: aws.String(c.Region), }, } @@ -73,7 +74,7 @@ func GetSession(c *Config) (*session.Session, error) { if IsAWSErr(err, "NoCredentialProviders", "") { return nil, ErrNoValidCredentialSources } - return nil, fmt.Errorf("Error creating AWS session: %s", err) + return nil, fmt.Errorf("Error creating AWS session: %w", err) } if c.MaxRetries > 0 { @@ -109,9 +110,8 @@ func GetSession(c *Config) (*session.Session, error) { }) if !c.SkipCredsValidation { - stsClient := sts.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.StsEndpoint)})) - if _, _, err := GetAccountIDAndPartitionFromSTSGetCallerIdentity(stsClient); err != nil { - return nil, fmt.Errorf("error using credentials to get account ID: %s", err) + if _, _, err := GetAccountIDAndPartitionFromSTSGetCallerIdentity(sts.New(sess)); err != nil { + return nil, fmt.Errorf("error validating provider credentials: %w", err) } } @@ -132,14 +132,14 @@ func GetSessionWithAccountIDAndPartition(c *Config) (*session.Session, string, s return sess, accountID, partition, nil } - iamClient := iam.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)})) - stsClient := sts.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.StsEndpoint)})) + iamClient := iam.New(sess) + stsClient := sts.New(sess) if !c.SkipCredsValidation { accountID, partition, err := GetAccountIDAndPartitionFromSTSGetCallerIdentity(stsClient) if err != nil { - return nil, "", "", fmt.Errorf("error validating provider credentials: %s", err) + return nil, "", "", fmt.Errorf("error validating provider credentials: %w", err) } return sess, accountID, partition, nil @@ -161,7 +161,7 @@ func GetSessionWithAccountIDAndPartition(c *Config) (*session.Session, string, s return nil, "", "", fmt.Errorf( "AWS account ID not previously found and failed retrieving via all available methods. "+ "See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for workaround and implications. "+ - "Errors: %s", err) + "Errors: %w", err) } var partition string diff --git a/session_test.go b/session_test.go index 2c5df7ec..e06c1842 100644 --- a/session_test.go +++ b/session_test.go @@ -1,9 +1,18 @@ package awsbase import ( + "errors" + "io/ioutil" "os" + "reflect" "strings" "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" ) func TestGetSessionOptions(t *testing.T) { @@ -50,34 +59,989 @@ func TestGetSessionOptions(t *testing.T) { } } -func TestGetSessionWithBlankConfig(t *testing.T) { - oldEnv := initSessionTestEnv() - defer PopEnv(oldEnv) +// End-to-end testing for GetSession +func TestGetSession(t *testing.T) { + testCases := []struct { + Config *Config + Description string + EnableEc2MetadataServer bool + EnableEcsCredentialsServer bool + EnvironmentVariables map[string]string + ExpectedCredentialsValue credentials.Value + ExpectedRegion string + ExpectedError func(err error) bool + MockStsEndpoints []*MockEndpoint + SharedConfigurationFile string + SharedCredentialsFile string + }{ + { + Config: &Config{}, + Description: "no configuration or credentials", + ExpectedError: func(err error) bool { + return errors.Is(err, ErrNoValidCredentialSources) + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey config AssumeRoleARN access key", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Profile: "SharedCredentialsProfile", + Region: "us-east-1", + }, + Description: "config Profile shared credentials profile aws_access_key_id", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "ProfileSharedCredentialsAccessKey", + ProviderName: credentials.SharedCredsProviderName, + SecretAccessKey: "ProfileSharedCredentialsSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey - _, err := GetSession(&Config{}) - if err == nil { - t.Fatal("GetSession(&Config{}) with an empty config should result in an error") - } -} +[SharedCredentialsProfile] +aws_access_key_id = ProfileSharedCredentialsAccessKey +aws_secret_access_key = ProfileSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Profile: "SharedConfigurationProfile", + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "config Profile shared configuration credential_source Ec2InstanceMetadata", + EnvironmentVariables: map[string]string{ + "AWS_SDK_LOAD_CONFIG": "1", + }, + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +credential_source = Ec2InstanceMetadata +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +`, + }, + { + Config: &Config{ + Profile: "SharedConfigurationProfile", + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "config Profile shared configuration credential_source EcsContainer", + EnvironmentVariables: map[string]string{ + "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI": "/creds", + "AWS_SDK_LOAD_CONFIG": "1", + }, + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ // TODO: Expected credentials should be assume role credentials + AccessKeyID: "EcsCredentialsAccessKey", + ProviderName: endpointcreds.ProviderName, + SecretAccessKey: "EcsCredentialsSecretKey", + SessionToken: "EcsCredentialsSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +credential_source = EcsContainer +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +`, + }, + { + Config: &Config{ + Profile: "SharedConfigurationProfile", + Region: "us-east-1", + }, + Description: "config Profile shared configuration source_profile", + EnvironmentVariables: map[string]string{ + "AWS_SDK_LOAD_CONFIG": "1", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +source_profile = SharedConfigurationSourceProfile -func TestGetSessionWithCreds(t *testing.T) { - oldEnv := initSessionTestEnv() - defer PopEnv(oldEnv) +[profile SharedConfigurationSourceProfile] +aws_access_key_id = SharedConfigurationSourceAccessKey +aws_secret_access_key = SharedConfigurationSourceSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_ACCESS_KEY_ID", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EnvAccessKey", + ProviderName: credentials.EnvProviderName, + SecretAccessKey: "EnvSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + Region: "us-east-1", + }, + Description: "environment AWS_ACCESS_KEY_ID config AssumeRoleARN access key", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_PROFILE shared credentials profile aws_access_key_id", + EnvironmentVariables: map[string]string{ + "AWS_PROFILE": "SharedCredentialsProfile", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "ProfileSharedCredentialsAccessKey", + ProviderName: credentials.SharedCredsProviderName, + SecretAccessKey: "ProfileSharedCredentialsSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey - sess, err := GetSession(&Config{ - AccessKey: "MockAccessKey", - SecretKey: "MockSecretKey", - SkipCredsValidation: true, - SkipMetadataApiCheck: true, - MaxRetries: 1, - UserAgentProducts: []*UserAgentProduct{{}}, - }) - if err != nil { - t.Fatalf("GetSession(&Config{...}) should return a valid session, but got the error %s", err) +[SharedCredentialsProfile] +aws_access_key_id = ProfileSharedCredentialsAccessKey +aws_secret_access_key = ProfileSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "environment AWS_PROFILE shared configuration credential_source Ec2InstanceMetadata", + EnableEc2MetadataServer: true, + EnvironmentVariables: map[string]string{ + "AWS_PROFILE": "SharedConfigurationProfile", + "AWS_SDK_LOAD_CONFIG": "1", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +credential_source = Ec2InstanceMetadata +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +`, + }, + { + Config: &Config{ + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "environment AWS_PROFILE shared configuration credential_source EcsContainer", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + EnvironmentVariables: map[string]string{ + "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI": "/creds", + "AWS_PROFILE": "SharedConfigurationProfile", + "AWS_SDK_LOAD_CONFIG": "1", + }, + ExpectedCredentialsValue: credentials.Value{ // TODO: Expected credentials should be assume role credentials + AccessKeyID: "EcsCredentialsAccessKey", + ProviderName: endpointcreds.ProviderName, + SecretAccessKey: "EcsCredentialsSecretKey", + SessionToken: "EcsCredentialsSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +credential_source = EcsContainer +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_PROFILE shared configuration source_profile", + EnvironmentVariables: map[string]string{ + "AWS_PROFILE": "SharedConfigurationProfile", + "AWS_SDK_LOAD_CONFIG": "1", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +role_arn = arn:aws:iam::555555555555:role/AssumeRole +role_session_name = AssumeRoleSessionName +source_profile = SharedConfigurationSourceProfile + +[profile SharedConfigurationSourceProfile] +aws_access_key_id = SharedConfigurationSourceAccessKey +aws_secret_access_key = SharedConfigurationSourceSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_SESSION_TOKEN", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + "AWS_SESSION_TOKEN": "EnvSessionToken", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EnvAccessKey", + ProviderName: credentials.EnvProviderName, + SecretAccessKey: "EnvSecretKey", + SessionToken: "EnvSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "shared credentials default aws_access_key_id", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "DefaultSharedCredentialsAccessKey", + ProviderName: credentials.SharedCredsProviderName, + SecretAccessKey: "DefaultSharedCredentialsSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + Region: "us-east-1", + }, + Description: "shared credentials default aws_access_key_id config AssumeRoleARN access key", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "EC2 metadata access key", + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "Ec2MetadataAccessKey", + ProviderName: ec2rolecreds.ProviderName, + SecretAccessKey: "Ec2MetadataSecretKey", + SessionToken: "Ec2MetadataSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + Region: "us-east-1", + }, + Description: "EC2 metadata access key config AssumeRoleARN access key", + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "ECS credentials access key", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EcsCredentialsAccessKey", + ProviderName: endpointcreds.ProviderName, + SecretAccessKey: "EcsCredentialsSecretKey", + SessionToken: "EcsCredentialsSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + Region: "us-east-1", + SkipMetadataApiCheck: true, // TODO: Should not be necessary + }, + Description: "ECS credentials access key config AssumeRoleARN access key", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "AssumeRoleAccessKey", + ProviderName: stscreds.ProviderName, + SecretAccessKey: "AssumeRoleSecretKey", + SessionToken: "AssumeRoleSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_AssumeRole_valid, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey over environment AWS_ACCESS_KEY_ID", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey over shared credentials default aws_access_key_id", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey over EC2 metadata access key", + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "config AccessKey over ECS credentials access key", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_ACCESS_KEY_ID over shared credentials default aws_access_key_id", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EnvAccessKey", + ProviderName: credentials.EnvProviderName, + SecretAccessKey: "EnvSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_ACCESS_KEY_ID over EC2 metadata access key", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EnvAccessKey", + ProviderName: credentials.EnvProviderName, + SecretAccessKey: "EnvSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "environment AWS_ACCESS_KEY_ID over ECS credentials access key", + EnvironmentVariables: map[string]string{ + "AWS_ACCESS_KEY_ID": "EnvAccessKey", + "AWS_SECRET_ACCESS_KEY": "EnvSecretKey", + }, + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "EnvAccessKey", + ProviderName: credentials.EnvProviderName, + SecretAccessKey: "EnvSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "shared credentials default aws_access_key_id over EC2 metadata access key", + EnableEc2MetadataServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "DefaultSharedCredentialsAccessKey", + ProviderName: credentials.SharedCredsProviderName, + SecretAccessKey: "DefaultSharedCredentialsSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "shared credentials default aws_access_key_id over ECS credentials access key", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "DefaultSharedCredentialsAccessKey", + ProviderName: credentials.SharedCredsProviderName, + SecretAccessKey: "DefaultSharedCredentialsSecretKey", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + SharedCredentialsFile: ` +[default] +aws_access_key_id = DefaultSharedCredentialsAccessKey +aws_secret_access_key = DefaultSharedCredentialsSecretKey +`, + }, + { + Config: &Config{ + Region: "us-east-1", + }, + Description: "EC2 metadata access key over ECS credentials access key", + EnableEc2MetadataServer: true, + EnableEcsCredentialsServer: true, + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "Ec2MetadataAccessKey", + ProviderName: ec2rolecreds.ProviderName, + SecretAccessKey: "Ec2MetadataSecretKey", + SessionToken: "Ec2MetadataSessionToken", + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + AssumeRoleARN: "arn:aws:iam::555555555555:role/AssumeRole", + AssumeRoleSessionName: "AssumeRoleSessionName", + DebugLogging: true, + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "assume role error", + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "The role \"arn:aws:iam::555555555555:role/AssumeRole\" cannot be assumed.") + }, + ExpectedRegion: "us-east-1", + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=AssumeRole&DurationSeconds=900&RoleArn=arn%3Aaws%3Aiam%3A%3A555555555555%3Arole%2FAssumeRole&RoleSessionName=AssumeRoleSessionName&Version=2011-06-15"}, + Response: &MockResponse{403, stsResponse_AssumeRole_InvalidClientTokenId, "text/xml"}, + }, + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + }, + Description: "credential validation error", + ExpectedError: func(err error) bool { + return IsAWSErr(err, "AccessDenied", "") + }, + MockStsEndpoints: []*MockEndpoint{ + { + Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, + Response: &MockResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"}, + }, + }, + }, + { + Config: &Config{ + Profile: "SharedConfigurationProfile", + Region: "us-east-1", + }, + Description: "session creation error", + EnvironmentVariables: map[string]string{ + "AWS_SDK_LOAD_CONFIG": "1", + }, + ExpectedError: func(err error) bool { + return IsAWSErr(err, "CredentialRequiresARNError", "") + }, + SharedConfigurationFile: ` +[profile SharedConfigurationProfile] +source_profile = SourceSharedCredentials +`, + }, + { + Config: &Config{ + AccessKey: "StaticAccessKey", + Region: "us-east-1", + SecretKey: "StaticSecretKey", + SkipCredsValidation: true, + }, + Description: "skip credentials validation", + ExpectedCredentialsValue: credentials.Value{ + AccessKeyID: "StaticAccessKey", + ProviderName: credentials.StaticProviderName, + SecretAccessKey: "StaticSecretKey", + }, + ExpectedRegion: "us-east-1", + }, } - if sess == nil { - t.Error("GetSession(...) resulted in a nil session") + for _, testCase := range testCases { + testCase := testCase + + t.Run(testCase.Description, func(t *testing.T) { + oldEnv := initSessionTestEnv() + defer PopEnv(oldEnv) + + if testCase.EnableEc2MetadataServer { + closeEc2Metadata := awsMetadataApiMock(append(ec2metadata_securityCredentialsEndpoints, ec2metadata_instanceIdEndpoint, ec2metadata_iamInfoEndpoint)) + defer closeEc2Metadata() + } + + if testCase.EnableEcsCredentialsServer { + closeEcsCredentials := ecsCredentialsApiMock() + defer closeEcsCredentials() + } + + closeSts, mockStsSession, err := GetMockedAwsApiSession("STS", testCase.MockStsEndpoints) + defer closeSts() + + if err != nil { + t.Fatalf("unexpected error creating mock STS server: %s", err) + } + + if mockStsSession != nil && mockStsSession.Config != nil { + testCase.Config.StsEndpoint = aws.StringValue(mockStsSession.Config.Endpoint) + } + + if testCase.SharedConfigurationFile != "" { + file, err := ioutil.TempFile("", "aws-sdk-go-base-shared-configuration-file") + + if err != nil { + t.Fatalf("unexpected error creating temporary shared configuration file: %s", err) + } + + defer os.Remove(file.Name()) + + err = ioutil.WriteFile(file.Name(), []byte(testCase.SharedConfigurationFile), 0600) + + if err != nil { + t.Fatalf("unexpected error writing shared configuration file: %s", err) + } + + // Config does not provide a passthrough for session.Options.SharedConfigFiles + os.Setenv("AWS_CONFIG_FILE", file.Name()) + } + + if testCase.SharedCredentialsFile != "" { + file, err := ioutil.TempFile("", "aws-sdk-go-base-shared-credentials-file") + + if err != nil { + t.Fatalf("unexpected error creating temporary shared credentials file: %s", err) + } + + defer os.Remove(file.Name()) + + err = ioutil.WriteFile(file.Name(), []byte(testCase.SharedCredentialsFile), 0600) + + if err != nil { + t.Fatalf("unexpected error writing shared credentials file: %s", err) + } + + // Config does not provide a passthrough for session.Options.SharedConfigFiles + testCase.Config.CredsFilename = file.Name() + } + + for k, v := range testCase.EnvironmentVariables { + os.Setenv(k, v) + } + + actualSession, err := GetSession(testCase.Config) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected GetSession() error: %s", err) + } + + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + + credentialsValue, err := actualSession.Config.Credentials.Get() + + if err != nil { + t.Fatalf("unexpected credentials Get() error: %s", err) + } + + if !reflect.DeepEqual(credentialsValue, testCase.ExpectedCredentialsValue) { + t.Fatalf("unexpected credentials: %#v", credentialsValue) + } + + if expected, actual := testCase.ExpectedRegion, aws.StringValue(actualSession.Config.Region); expected != actual { + t.Fatalf("expected region (%s), got: %s", expected, actual) + } + }) } }