diff --git a/USAGE.md b/USAGE.md index b936bb9c0..5dcd71076 100644 --- a/USAGE.md +++ b/USAGE.md @@ -304,6 +304,14 @@ You can use the `aws-vault login` command to open a browser window and login to $ aws-vault login work ``` +If you have temporary STS credentials already available in your environment, you can have aws-vault use these credentials to sign you in. +This is useful when you had to use something else than aws-vault to retrieve temporary credentials: + +```shell +# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN must be set in your environment prior to running the below +$ aws-vault login +``` + ### Removing stored sessions If you want to remove sessions managed by `aws-vault` before they expire, you can do this with `aws-vault clear` command. diff --git a/cli/login.go b/cli/login.go index 3d1b6e65f..550aca0c6 100644 --- a/cli/login.go +++ b/cli/login.go @@ -55,7 +55,6 @@ func ConfigureLoginCommand(app *kingpin.Application, a *AwsVault) { BoolVar(&input.UseStdout) cmd.Arg("profile", "Name of the profile"). - Required(). HintAction(a.MustGetProfileNames). StringVar(&input.ProfileName) @@ -94,22 +93,34 @@ func LoginCommand(input LoginCommandInput, f *vault.ConfigFile, keyring keyring. var credsProvider aws.CredentialsProvider - ckr := &vault.CredentialKeyring{Keyring: keyring} - // If AssumeRole or sso.GetRoleCredentials isn't used, GetFederationToken has to be used for IAM credentials - if config.HasRole() || config.HasSSOStartURL() { - credsProvider, err = vault.NewTempCredentialsProvider(config, ckr) + if input.ProfileName == "" { + // When no profile is specified, source credentials from the environment + credsProvider = vault.NewEnvironmentCredentialsProvider() } else { - credsProvider, err = vault.NewFederationTokenCredentialsProvider(input.ProfileName, ckr, config) - } - if err != nil { - return fmt.Errorf("profile %s: %w", input.ProfileName, err) + // Use a profile from the AWS config file + ckr := &vault.CredentialKeyring{Keyring: keyring} + if config.HasRole() || config.HasSSOStartURL() { + // If AssumeRole or sso.GetRoleCredentials isn't used, GetFederationToken has to be used for IAM credentials + credsProvider, err = vault.NewTempCredentialsProvider(config, ckr) + } else { + credsProvider, err = vault.NewFederationTokenCredentialsProvider(input.ProfileName, ckr, config) + } + if err != nil { + return fmt.Errorf("profile %s: %w", input.ProfileName, err) + } } creds, err := credsProvider.Retrieve(context.TODO()) if err != nil { - return fmt.Errorf("Failed to get credentials for %s: %w", config.ProfileName, err) + return fmt.Errorf("Failed to get credentials: %w", err) + } + if creds.SessionToken == "" { + // When sourcing credentials from the environment, it's possible a session token wasn't set + // Generating a sign-in link requires temporary credentials, so we return an error + // NOTE: We deliberately chose to have this logic here rather than in 'EnvironmentVariablesCredentialsProvider' + // to make it possible to reuse it for other commands than `aws-vault login` in the future + return fmt.Errorf("failed to retrieve a session token. Cannot generate a login URL without it") } - jsonBytes, err := json.Marshal(map[string]string{ "sessionId": creds.AccessKeyID, "sessionKey": creds.SecretAccessKey, diff --git a/go.mod b/go.mod index 4f8eac682..7a741d56c 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/99designs/keyring v1.2.0 github.com/alecthomas/kingpin v0.0.0-20200323085623-b6657d9477a6 github.com/aws/aws-sdk-go-v2 v1.14.0 + github.com/aws/aws-sdk-go-v2/config v1.14.0 github.com/aws/aws-sdk-go-v2/service/iam v1.17.0 github.com/aws/aws-sdk-go-v2/service/sso v1.10.0 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.11.0 @@ -22,8 +23,11 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.9.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.11.0 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.8.0 // indirect github.com/aws/smithy-go v1.11.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect diff --git a/go.sum b/go.sum index e135c08b9..432638248 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,18 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/aws/aws-sdk-go-v2 v1.14.0 h1:IzSYBJHu0ZdUi27kIW6xVrs0eSxI4AzwbenzfXhhVs4= github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU= +github.com/aws/aws-sdk-go-v2/config v1.14.0 h1:Yr8/7R6H8nqqfqgLATrcB83ax6FE2HcDXEB54XPhE98= +github.com/aws/aws-sdk-go-v2/config v1.14.0/go.mod h1:GKDRrvsq/PTaOYc9252u8Uah1hsIdtor4oIrFvUNPNM= +github.com/aws/aws-sdk-go-v2/credentials v1.9.0 h1:R3Q5s1uGLUg0aUzi+oRaUqRXhd17G/9+PiVnAwXp4sY= +github.com/aws/aws-sdk-go-v2/credentials v1.9.0/go.mod h1:PyHKqk/+tJuDY7T8R580S1j/AcSD+ODeUZ99CAUKLqQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.11.0 h1:CkM4d3lNeMXMZ0BDX3BtCktnKA1Ftud84Hb6d+Ix4Rk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.11.0/go.mod h1:rwdUKJV5rm+vHu1ncD1iGDqahBEL8O0tBjVqo9eO2N0= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5 h1:+phazLmKkjBYhFTsGYH9J7jgnA8+Aer2yE4QeS4zn6A= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5/go.mod h1:2hXc8ooJqF2nAznsbJQIn+7h851/bu8GVC80OVTTqf8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0 h1:PO+HNeJBeRK0yVD9CQZ+VUrYfd5sXqS7YdPYHHcDkR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.6 h1:c8s9EhIPVFMFS+R1+rtEghGrf7v83gSUWbcCYX/OPes= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.6/go.mod h1:o1ippSg3yJx5EuT4AOGXJCUcmt5vrcxla1cg6K1Q8Iw= github.com/aws/aws-sdk-go-v2/service/iam v1.17.0 h1:lG9Hx7BGOiVFTS0qJ0nM//yCxvGNxoqzfOSvo2dpsl0= github.com/aws/aws-sdk-go-v2/service/iam v1.17.0/go.mod h1:1o13iK9CH2OanAQZpbxKdhGj23ij4V8ZZ9DFnSve3fc= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.8.0 h1:JNMALY8/ZnFsfAzBHtC4gq8JeZPANmIoI2VaBgYzbf8= diff --git a/vault/environmentvariablescredentialsprovider.go b/vault/environmentvariablescredentialsprovider.go new file mode 100644 index 000000000..c4f351a5b --- /dev/null +++ b/vault/environmentvariablescredentialsprovider.go @@ -0,0 +1,21 @@ +package vault + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" +) + +// EnvironmentVariablesCredentialsProvider retrieves AWS credentials available in the OS environment variables +type EnvironmentVariablesCredentialsProvider struct { +} + +func (m *EnvironmentVariablesCredentialsProvider) Retrieve(context.Context) (aws.Credentials, error) { + configFromEnv, err := config.NewEnvConfig() + if err != nil { + err := fmt.Errorf("unable to authenticate to AWS through your environment variables: %w", err) + return aws.Credentials{}, err + } + return configFromEnv.Credentials, nil +} diff --git a/vault/vault.go b/vault/vault.go index 057771d9a..2a5244d9a 100644 --- a/vault/vault.go +++ b/vault/vault.go @@ -291,6 +291,10 @@ func NewFederationTokenCredentialsProvider(profileName string, k *CredentialKeyr }, nil } +func NewEnvironmentCredentialsProvider() aws.CredentialsProvider { + return &EnvironmentVariablesCredentialsProvider{} +} + func FindMasterCredentialsNameFor(profileName string, keyring *CredentialKeyring, config *Config) (string, error) { hasMasterCreds, err := keyring.Has(profileName) if err != nil {