diff --git a/USAGE.md b/USAGE.md index ecf24665a..1f6fc37e3 100644 --- a/USAGE.md +++ b/USAGE.md @@ -118,6 +118,7 @@ To configure the default flag values of `aws-vault` and its subcommands: To override the AWS config file (used in the `exec`, `login` and `rotate` subcommands): * `AWS_REGION`: The AWS region * `AWS_DEFAULT_REGION`: The AWS region, applied only if `AWS_REGION` isn't set +* `AWS_STS_REGIONAL_ENDPOINTS`: STS endpoint resolution logic, must be "regional" or "legacy" * `AWS_MFA_SERIAL`: The identification number of the MFA device to use * `AWS_ROLE_ARN`: Specifies the ARN of an IAM role in the active profile * `AWS_ROLE_SESSION_NAME`: Specifies the name to attach to the role session in the active profile diff --git a/cli/rotate.go b/cli/rotate.go index 0ee38b2a9..7eb59a7c3 100644 --- a/cli/rotate.go +++ b/cli/rotate.go @@ -98,7 +98,7 @@ func RotateCommand(input RotateCommandInput, f *vault.ConfigFile, keyring keyrin } } - sess, err := vault.NewSessionWithCreds(sessCreds, config.Region) + sess, err := vault.NewSessionWithCreds(sessCreds, config.Region, config.STSRegionalEndpoints) if err != nil { return err } diff --git a/vault/assumeroleprovider.go b/vault/assumeroleprovider.go index 31411382b..c57dbb39c 100644 --- a/vault/assumeroleprovider.go +++ b/vault/assumeroleprovider.go @@ -67,6 +67,8 @@ func (p *AssumeRoleProvider) assumeRole() (*sts.Credentials, error) { } } + log.Printf("Using STS endpoint %s", p.StsClient.Endpoint) + resp, err := p.StsClient.AssumeRole(input) if err != nil { return nil, err diff --git a/vault/config.go b/vault/config.go index 170d9f7b6..9a3750f9e 100644 --- a/vault/config.go +++ b/vault/config.go @@ -144,6 +144,7 @@ type ProfileSection struct { SSORoleName string `ini:"sso_role_name,omitempty"` WebIdentityTokenFile string `ini:"web_identity_token_file,omitempty"` WebIdentityTokenProcess string `ini:"web_identity_token_process,omitempty"` + STSRegionalEndpoints string `ini:"sts_regional_endpoints,omitempty"` } func (s ProfileSection) IsEmpty() bool { @@ -322,6 +323,9 @@ func (cl *ConfigLoader) populateFromConfigFile(config *Config, profileName strin if config.WebIdentityTokenProcess == "" { config.WebIdentityTokenProcess = psection.WebIdentityTokenProcess } + if config.STSRegionalEndpoints == "" { + config.STSRegionalEndpoints = psection.STSRegionalEndpoints + } if psection.ParentProfile != "" { fmt.Fprint(os.Stderr, "Warning: parent_profile is deprecated, please use include_profile instead in your AWS config\n") @@ -363,6 +367,11 @@ func (cl *ConfigLoader) populateFromEnv(profile *Config) { profile.Region = region } + if stsRegionalEndpoints := os.Getenv("AWS_STS_REGIONAL_ENDPOINTS"); stsRegionalEndpoints != "" && profile.STSRegionalEndpoints == "" { + log.Printf("Using %q from AWS_STS_REGIONAL_ENDPOINTS", stsRegionalEndpoints) + profile.STSRegionalEndpoints = stsRegionalEndpoints + } + if mfaSerial := os.Getenv("AWS_MFA_SERIAL"); mfaSerial != "" && profile.MfaSerial == "" { log.Printf("Using mfa_serial %q from AWS_MFA_SERIAL", mfaSerial) profile.MfaSerial = mfaSerial @@ -462,6 +471,9 @@ type Config struct { // Region is the AWS region Region string + // STSRegionalEndpoints sets STS endpoint resolution logic, must be "regional" or "legacy" + STSRegionalEndpoints string + // Mfa config MfaSerial string MfaToken string diff --git a/vault/config_test.go b/vault/config_test.go index eee325dce..5db86c1c1 100644 --- a/vault/config_test.go +++ b/vault/config_test.go @@ -32,6 +32,7 @@ Role_Arn=arn:aws:iam::4451234513441615400570:role/aws_admin mfa_Serial=arn:aws:iam::1234513441:mfa/blah Region=us-east-1 duration_seconds=1200 +sts_regional_endpoints=legacy [profile testincludeprofile1] region=us-east-1 @@ -103,7 +104,7 @@ func TestConfigParsingProfiles(t *testing.T) { }{ {vault.ProfileSection{Name: "user2", Region: "us-east-1"}, true}, {vault.ProfileSection{Name: "withsource", SourceProfile: "user2", Region: "us-east-1"}, true}, - {vault.ProfileSection{Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2"}, true}, + {vault.ProfileSection{Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2", STSRegionalEndpoints: "legacy"}, true}, {vault.ProfileSection{Name: "nopenotthere"}, false}, } @@ -157,7 +158,7 @@ func TestProfilesFromConfig(t *testing.T) { {Name: "default", Region: "us-west-2"}, {Name: "user2", Region: "us-east-1"}, {Name: "withsource", Region: "us-east-1", SourceProfile: "user2"}, - {Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2"}, + {Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2", STSRegionalEndpoints: "legacy"}, {Name: "testincludeprofile1", Region: "us-east-1"}, {Name: "testincludeprofile2", IncludeProfile: "testincludeprofile1"}, } @@ -191,7 +192,7 @@ func TestAddProfileToExistingConfig(t *testing.T) { {Name: "default", Region: "us-west-2"}, {Name: "user2", Region: "us-east-1"}, {Name: "withsource", Region: "us-east-1", SourceProfile: "user2"}, - {Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2"}, + {Name: "withMFA", MfaSerial: "arn:aws:iam::1234513441:mfa/blah", RoleARN: "arn:aws:iam::4451234513441615400570:role/aws_admin", Region: "us-east-1", DurationSeconds: 1200, SourceProfile: "user2", STSRegionalEndpoints: "legacy"}, {Name: "testincludeprofile1", Region: "us-east-1"}, {Name: "testincludeprofile2", IncludeProfile: "testincludeprofile1"}, {Name: "llamas", MfaSerial: "testserial", Region: "us-east-1", SourceProfile: "default"}, diff --git a/vault/sessiontokenprovider.go b/vault/sessiontokenprovider.go index d14018e7e..af727ea24 100644 --- a/vault/sessiontokenprovider.go +++ b/vault/sessiontokenprovider.go @@ -49,6 +49,8 @@ func (p *SessionTokenProvider) GetSessionToken() (*sts.Credentials, error) { } } + log.Printf("Using STS endpoint %s", p.StsClient.Endpoint) + resp, err := p.StsClient.GetSessionToken(input) if err != nil { return nil, err diff --git a/vault/vault.go b/vault/vault.go index 41563608c..7bcf6881a 100644 --- a/vault/vault.go +++ b/vault/vault.go @@ -11,6 +11,7 @@ import ( "github.com/99designs/keyring" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sso" "github.com/aws/aws-sdk-go/service/ssooidc" @@ -27,15 +28,23 @@ func init() { var UseSessionCache = true -func NewSession(region string) (*session.Session, error) { +func NewSession(region, stsRegionalEndpoints string) (*session.Session, error) { + endpointConfig, err := endpoints.GetSTSRegionalEndpoint(stsRegionalEndpoints) + if err != nil && stsRegionalEndpoints != "" { + return nil, err + } + return session.NewSessionWithOptions(session.Options{ - Config: aws.Config{Region: aws.String(region)}, + Config: aws.Config{ + Region: aws.String(region), + STSRegionalEndpoint: endpointConfig, + }, SharedConfigState: session.SharedConfigDisable, }) } -func NewSessionWithCreds(creds *credentials.Credentials, region string) (*session.Session, error) { - s, err := NewSession(region) +func NewSessionWithCreds(creds *credentials.Credentials, region, stsRegionalEndpoints string) (*session.Session, error) { + s, err := NewSession(region, stsRegionalEndpoints) if err != nil { return nil, err } @@ -80,7 +89,7 @@ func NewMasterCredentials(k *CredentialKeyring, credentialsName string) *credent } func NewSessionTokenProvider(creds *credentials.Credentials, k keyring.Keyring, config *Config) (credentials.Provider, error) { - sess, err := NewSessionWithCreds(creds, config.Region) + sess, err := NewSessionWithCreds(creds, config.Region, config.STSRegionalEndpoints) if err != nil { return nil, err } @@ -114,7 +123,7 @@ func NewSessionTokenProvider(creds *credentials.Credentials, k keyring.Keyring, // NewAssumeRoleProvider returns a provider that generates credentials using AssumeRole func NewAssumeRoleProvider(creds *credentials.Credentials, k keyring.Keyring, config *Config) (credentials.Provider, error) { - sess, err := NewSessionWithCreds(creds, config.Region) + sess, err := NewSessionWithCreds(creds, config.Region, config.STSRegionalEndpoints) if err != nil { return nil, err } @@ -152,7 +161,7 @@ func NewAssumeRoleProvider(creds *credentials.Credentials, k keyring.Keyring, co // NewAssumeRoleWithWebIdentityProvider returns a provider that generates // credentials using AssumeRoleWithWebIdentity func NewAssumeRoleWithWebIdentityProvider(k keyring.Keyring, config *Config) (credentials.Provider, error) { - sess, err := NewSession(config.Region) + sess, err := NewSession(config.Region, config.STSRegionalEndpoints) if err != nil { return nil, err } @@ -184,7 +193,7 @@ func NewAssumeRoleWithWebIdentityProvider(k keyring.Keyring, config *Config) (cr // NewSSORoleCredentialsProvider creates a provider for SSO credentials func NewSSORoleCredentialsProvider(k keyring.Keyring, config *Config) (credentials.Provider, error) { - sess, err := NewSession(config.SSORegion) + sess, err := NewSession(config.SSORegion, config.STSRegionalEndpoints) if err != nil { return nil, err } @@ -305,7 +314,8 @@ func NewFederationTokenCredentials(profileName string, k *CredentialKeyring, con return nil, err } - sess, err := NewSessionWithCreds(NewMasterCredentials(k, credentialsName), config.Region) + masterCreds := NewMasterCredentials(k, credentialsName) + sess, err := NewSessionWithCreds(masterCreds, config.Region, config.STSRegionalEndpoints) if err != nil { return nil, err }