Skip to content

Commit

Permalink
Enable getting profile credentials from EC2 instance metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
jbergknoff-rival authored and xibz committed Oct 11, 2018
1 parent a441fee commit a7379c6
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 21 deletions.
11 changes: 8 additions & 3 deletions aws/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,14 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg share
)
} else if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.RoleARN) > 0 && sharedCfg.AssumeRoleSource != nil {
cfgCp := *cfg
cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
sharedCfg.AssumeRoleSource.Creds,
)
if sharedCfg.AssumeRole.CredentialSource == "Ec2InstanceMetadata" {
p := defaults.RemoteCredProvider(cfgCp, handlers)
cfgCp.Credentials = credentials.NewCredentials(p)
} else if len(sharedCfg.AssumeRole.SourceProfile) > 0 {
cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
sharedCfg.AssumeRoleSource.Creds,
)
}
if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil {
// AssumeRole Token provider is required if doing Assume Role
// with MFA.
Expand Down
63 changes: 63 additions & 0 deletions aws/session/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,69 @@ func TestSessionAssumeRole_InvalidSourceProfile(t *testing.T) {
assert.Nil(t, s)
}

func TestSessionAssumeRole_EC2CredentialSource(t *testing.T) {
oldEnv := initSessionTestEnv()
defer awstesting.PopEnv(oldEnv)

os.Setenv("AWS_REGION", "us-east-1")
os.Setenv("AWS_SDK_LOAD_CONFIG", "1")
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", testConfigFilename)
os.Setenv("AWS_PROFILE", "assume_role_ec2_instance_metadata")

const ec2MetadataResponse = `{
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId" : "accessKey",
"SecretAccessKey" : "secret",
"Token" : "token",
"Expiration" : "2100-01-01T00:00:00Z",
"LastUpdated" : "2009-11-23T0:00:00Z"
}`

ec2MetadataCalled := false
ec2MetadataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/meta-data/iam/security-credentials/RoleName" {
ec2MetadataCalled = true
w.Write([]byte(ec2MetadataResponse))
} else if r.URL.Path == "/meta-data/iam/security-credentials" {
w.Write([]byte("RoleName"))
} else {
w.Write([]byte(""))
}
}))

defer ec2MetadataServer.Close()

stsServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(fmt.Sprintf(assumeRoleRespMsg, time.Now().Add(15*time.Minute).Format("2006-01-02T15:04:05Z"))))
}))

defer stsServer.Close()

s, err := NewSession(&aws.Config{
EndpointResolver: endpoints.ResolverFunc(
func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
if service == "ec2metadata" {
return endpoints.ResolvedEndpoint{
URL: ec2MetadataServer.URL,
}, nil
}

return endpoints.ResolvedEndpoint{
URL: stsServer.URL,
}, nil
},
),
})

creds, err := s.Config.Credentials.Get()
assert.NoError(t, err)
assert.Equal(t, ec2MetadataCalled, true)
assert.Equal(t, "AKID", creds.AccessKeyID)
assert.Equal(t, "SECRET", creds.SecretAccessKey)
assert.Equal(t, "SESSION_TOKEN", creds.SessionToken)
}

func initSessionTestEnv() (oldEnv []string) {
oldEnv = awstesting.StashEnv()
os.Setenv("AWS_CONFIG_FILE", "file_not_exists")
Expand Down
44 changes: 27 additions & 17 deletions aws/session/shared_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ const (
sessionTokenKey = `aws_session_token` // optional

// Assume Role Credentials group
roleArnKey = `role_arn` // group required
sourceProfileKey = `source_profile` // group required
externalIDKey = `external_id` // optional
mfaSerialKey = `mfa_serial` // optional
roleSessionNameKey = `role_session_name` // optional
roleArnKey = `role_arn` // group required
sourceProfileKey = `source_profile` // group required (or credential_source)
credentialSourceKey = `credential_source` // group required (or source_profile)
externalIDKey = `external_id` // optional
mfaSerialKey = `mfa_serial` // optional
roleSessionNameKey = `role_session_name` // optional

// Additional Config fields
regionKey = `region`
Expand All @@ -32,11 +33,12 @@ const (
)

type assumeRoleConfig struct {
RoleARN string
SourceProfile string
ExternalID string
MFASerial string
RoleSessionName string
RoleARN string
SourceProfile string
CredentialSource string
ExternalID string
MFASerial string
RoleSessionName string
}

// sharedConfig represents the configuration fields of the SDK config files.
Expand Down Expand Up @@ -92,7 +94,7 @@ func loadSharedConfig(profile string, filenames []string) (sharedConfig, error)
return sharedConfig{}, err
}

if len(cfg.AssumeRole.SourceProfile) > 0 {
if len(cfg.AssumeRole.SourceProfile) > 0 || len(cfg.AssumeRole.CredentialSource) > 0 {
if err := cfg.setAssumeRoleSource(profile, files); err != nil {
return sharedConfig{}, err
}
Expand Down Expand Up @@ -127,6 +129,11 @@ func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
var assumeRoleSrc sharedConfig

if len(cfg.AssumeRole.CredentialSource) > 0 {
cfg.AssumeRoleSource = &sharedConfig{}
return nil
}

// Multiple level assume role chains are not support
if cfg.AssumeRole.SourceProfile == origProfile {
assumeRoleSrc = *cfg
Expand Down Expand Up @@ -195,13 +202,16 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) e
// Assume Role
roleArn := section.Key(roleArnKey).String()
srcProfile := section.Key(sourceProfileKey).String()
if len(roleArn) > 0 && len(srcProfile) > 0 {
credentialSource := section.Key(credentialSourceKey).String()
hasSource := len(srcProfile) > 0 || len(credentialSource) > 0
if len(roleArn) > 0 && hasSource {
cfg.AssumeRole = assumeRoleConfig{
RoleARN: roleArn,
SourceProfile: srcProfile,
ExternalID: section.Key(externalIDKey).String(),
MFASerial: section.Key(mfaSerialKey).String(),
RoleSessionName: section.Key(roleSessionNameKey).String(),
RoleARN: roleArn,
SourceProfile: srcProfile,
CredentialSource: credentialSource,
ExternalID: section.Key(externalIDKey).String(),
MFASerial: section.Key(mfaSerialKey).String(),
RoleSessionName: section.Key(roleSessionNameKey).String(),
}
}

Expand Down
11 changes: 11 additions & 0 deletions aws/session/shared_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ func TestLoadSharedConfig(t *testing.T) {
},
},
},
{
Filenames: []string{testConfigOtherFilename, testConfigFilename},
Profile: "assume_role_ec2_instance_metadata",
Expected: sharedConfig{
AssumeRole: assumeRoleConfig{
RoleARN: "assume_role_ec2_instance_metadata_role_arn",
CredentialSource: "Ec2InstanceMetadata",
},
AssumeRoleSource: &sharedConfig{},
},
},
{
Filenames: []string{testConfigOtherFilename, testConfigFilename},
Profile: "assume_role_wo_creds",
Expand Down
6 changes: 5 additions & 1 deletion aws/session/testdata/shared_config
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[default]
s3 =
s3 =
unsupported_key=123
other_unsupported=abc

Expand Down Expand Up @@ -63,3 +63,7 @@ aws_secret_access_key = assume_role_w_creds_secret
[assume_role_wo_creds]
role_arn = assume_role_wo_creds_role_arn
source_profile = assume_role_wo_creds

[assume_role_ec2_instance_metadata]
role_arn = assume_role_ec2_instance_metadata_role_arn
credential_source = Ec2InstanceMetadata

0 comments on commit a7379c6

Please sign in to comment.