Skip to content

Commit

Permalink
Console refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
RowdyLemon committed Oct 27, 2017
1 parent d34b43f commit 08ccf83
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 85 deletions.
3 changes: 3 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ func parseConsoleArgs(args []string) (Command, error) {
vaultName := ""
assume, _ := flag.GetString("assume")
duration, _ := flag.GetDuration("duration")
if duration != 0 && (duration < ConsoleMinDuration || duration > ConsoleMaxDuration) {
return nil, ErrInvalidDuration
}

if flag.NArg() > 1 {
return nil, ErrTooManyArguments
Expand Down
7 changes: 7 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,13 @@ var (
Role: "arn:something:or:other",
},
},
{
Args: []string{"console", "--duration", "15m", "one"},
Command: &Console{
VaultName: "one",
Duration: ConsoleMinDuration,
},
},
{
Args: []string{"console", "--help"},
Command: &Help{Subcommand: "console"},
Expand Down
178 changes: 106 additions & 72 deletions console.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ var (
)

const (
CONSOLE_URL = "https://console.aws.amazon.com/console/home"
SIGNIN_URL = "https://signin.aws.amazon.com/federation"
ConsoleURL = "https://console.aws.amazon.com/console/home"
ConsoleFederationSigninURL = "https://signin.aws.amazon.com/federation"

MinConsoleDuration = 15 * time.Minute
MaxConsoleDuration = 12 * time.Hour
ConsoleMinDuration = 15 * time.Minute
ConsoleMaxDuration = 12 * time.Hour
ConsoleDefaultDuration = 1 * time.Hour
)

type Console struct {
Expand All @@ -37,103 +38,136 @@ func (c *Console) Run(store vaulted.Store) error {
return err
}

// console signin
signinUrl, _ := url.Parse(SIGNIN_URL)
loginQuery := make(url.Values)
loginQuery.Set("Action", "login")
loginQuery.Set("SigninToken", signinToken)
loginQuery.Set("Destination", CONSOLE_URL)
return openConsole(signinToken)
}

signinUrl.RawQuery = loginQuery.Encode()
err = browser.OpenURL(signinUrl.String())
func (c *Console) getSigninToken(store vaulted.Store) (string, error) {
vault, err := c.getVault(store)
if err != nil {
return err
return "", err
}

return nil
}
awsKey := c.getAWSKey(vault)

func (c *Console) getSigninToken(store vaulted.Store) (string, error) {
// Setup default values (may be overwritten by values from vault)
duration := 1 * time.Hour
var awsKey vaulted.AWSKey
awsCreds, err := c.getCredentials(vault, awsKey)
if err != nil {
return "", err
}

duration, err := c.getDuration(vault)
if err != nil {
return "", err
}

if c.Role != "" {
return c.getAssumeRoleToken(store, awsKey, awsCreds, duration)
} else {
return c.getFederationToken(awsCreds, duration)
}

}

// Override defaults with values from specified vault
func (c *Console) getVault(store vaulted.Store) (*vaulted.Vault, error) {
vault := &vaulted.Vault{}
var err error
if c.VaultName != "" {
v, _, err := store.OpenVault(c.VaultName)
vault, _, err = store.OpenVault(c.VaultName)
if err != nil {
return "", err
return nil, err
}
}
return vault, nil
}

duration = v.Duration

if v.AWSKey.Valid() {
awsKey = *v.AWSKey
if c.Role != "" {
awsKey.Role = c.Role
}
func (c *Console) getCredentials(vault *vaulted.Vault, awsKey *vaulted.AWSKey) (*vaulted.AWSCredentials, error) {
awsCreds, err := awsKey.AWSCredentials.WithLocalDefault()
if err != nil {
if err != credentials.ErrNoValidProvidersFoundInChain {
return nil, err
} else if err == credentials.ErrNoValidProvidersFoundInChain || !awsCreds.Valid() {
return nil, ErrNoCredentialsFound
}
} else if awsCreds.ValidSession() {
return nil, ErrInvalidTemporaryCredentials
}

// If duration was provided through the command line overwrite with that
if c.Duration != 0 && (c.Duration < 15*time.Minute || c.Duration > 12*time.Hour) {
return "", ErrInvalidDuration
} else if c.Duration > 0 {
duration = c.Duration
return awsCreds, nil
}

func (c *Console) getAWSKey(vault *vaulted.Vault) *vaulted.AWSKey {
var awsKey vaulted.AWSKey

if vault.AWSKey != nil && vault.AWSKey.Valid() {
awsKey = *vault.AWSKey
}
awsKey.Role = c.Role

return c.getSigninTokenFromCreds(store, awsKey, duration)
return &awsKey
}

func (c *Console) getSigninTokenFromCreds(store vaulted.Store, awsKey vaulted.AWSKey, duration time.Duration) (string, error) {
// Get creds from environment if no creds loaded from vault
creds, err := awsKey.AWSCredentials.WithLocalDefault()
if err != nil && err != credentials.ErrNoValidProvidersFoundInChain {
return "", err
} else if err == credentials.ErrNoValidProvidersFoundInChain || !creds.Valid() {
return "", ErrNoCredentialsFound
func (c *Console) getDuration(vault *vaulted.Vault) (time.Duration, error) {
var duration time.Duration
if c.Duration != 0 {
duration = c.Duration
} else if vault.Duration != 0 {
duration = vault.Duration
} else {
duration = ConsoleDefaultDuration
}

if creds.ValidSession() {
return "", ErrInvalidTemporaryCredentials
}
return capDuration(duration)
}

duration, err = capDuration(duration)
if err != nil {
return "", err
func capDuration(duration time.Duration) (time.Duration, error) {
if duration < ConsoleMinDuration {
return time.Duration(0), ErrInvalidDuration
}
if duration > ConsoleMaxDuration {
duration = ConsoleMaxDuration
fmt.Println("Your vault duration is greater than the max console duration.\nCurrent console session duration set to 12 hours.")
}
return duration, nil
}

// assume provided role or get a federation token
if awsKey.Role != "" {
if awsKey.MFA != "" {
tokenCode, tokenErr := store.Steward().GetMFAToken(c.VaultName)
if tokenErr != nil {
return "", tokenErr
}
creds, err = creds.AssumeRoleWithMFA(awsKey.MFA, tokenCode, awsKey.Role, 15*time.Minute)
} else {
creds, err = creds.AssumeRole(awsKey.Role, 15*time.Minute)
}
func (c *Console) getAssumeRoleToken(store vaulted.Store, awsKey *vaulted.AWSKey, awsCreds *vaulted.AWSCredentials, duration time.Duration) (string, error) {
var err error
if awsKey.MFA != "" {
tokenCode, err := store.Steward().GetMFAToken(c.VaultName)
if err != nil {
return "", err
}
return creds.GetSigninToken(&duration)
awsCreds, err = awsCreds.AssumeRoleWithMFA(awsKey.MFA, tokenCode, awsKey.Role, ConsoleMinDuration)
} else {
creds, err = creds.GetFederationToken(duration)
if err != nil {
return "", err
}
return creds.GetSigninToken(nil)
awsCreds, err = awsCreds.AssumeRole(awsKey.Role, ConsoleMinDuration)
}
if err != nil {
return "", err
}
return awsCreds.GetSigninToken(&duration)
}

func capDuration(duration time.Duration) (time.Duration, error) {
if duration < MinConsoleDuration {
return time.Duration(0), ErrInvalidDuration
func (c *Console) getFederationToken(awsCreds *vaulted.AWSCredentials, duration time.Duration) (string, error) {
awsCreds, err := awsCreds.GetFederationToken(duration)
if err != nil {
return "", err
}
if duration > MaxConsoleDuration {
duration = MaxConsoleDuration
fmt.Println("Your vault duration is greater than the max console duration.\nCurrent console session duration set to 12 hours.")
return awsCreds.GetSigninToken(nil)
}

func openConsole(signinToken string) error {
signinURL, _ := url.Parse(ConsoleFederationSigninURL)
signinURL.RawQuery = getLoginQuery(signinToken).Encode()
err := browser.OpenURL(signinURL.String())
if err != nil {
return err
}
return nil
}

func getLoginQuery(signinToken string) url.Values {
return url.Values{
"Action": []string{"login"},
"SigninToken": []string{signinToken},
"Destination": []string{ConsoleURL},
}
return duration, nil
}
4 changes: 4 additions & 0 deletions console_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func TestConsole(t *testing.T) {
VaultName: "one",
Duration: 10 * time.Minute,
}
store.Vaults["one"].AWSKey.AWSCredentials = vaulted.AWSCredentials{
ID: "id",
Secret: "secret",
}
err = c.Run(store)
if err != ErrInvalidDuration {
t.Error("Invalid duration provided, should have caused an ErrInvalidDuration")
Expand Down
20 changes: 14 additions & 6 deletions doc/man/vaulted-console.1
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ vaulted console \- Opens the AWS console in the default web browser
\fB\fCvaulted console\fR \fIname\fP [\fIOPTIONS\fP]
.br
\fB\fCvaulted console \-\-assume\fR \fIarn\fP [\fIOPTIONS\fP]
.br
\fB\fCvaulted console \-\-duration\fR \fIduration\fP [\fIOPTIONS\fP]
.SH DESCRIPTION
.PP
Opens the AWS console in the default web browser. Uses either the credentials in the current environment or the credentials in the specified vault. Console sessions either use the passed in duration, the provided vault's duration, or defaults to 1 hour.
Opens the AWS console in the default web browser. It will use the credentials
in the current environment unless a vault is specified, in which case it will
use the credentials in the vault.
.PP
The session length will either use the passed in duration, the provided vault's
duration, or defaults to 1 hour in that order of preference. Durations must be
at least 15 minutes and less than 12 hours.
.PP
Durations must be at least 15 minutes and less than 12 hours.
This requires that you have the sts:GetFederationToken or sts:AssumeRole
permissions enabled for your user.
.SH OPTIONS
.TP
\fB\fC\-\-assume\fR \fIarn\fP
Expand All @@ -27,10 +32,13 @@ When invoked this way, credentials are sourced from default locations (e.g.
environment, configuration files, instance profile, etc.).
.PP
\fB\fC\-\-duration\fR \fIduration\fP
Specifies the duration that the console session is valid. The duration must be within the range 15m\-12h.
Specifies the duration that the console session is valid. The duration must
be within the range 15m\-12h.
.SH ASSUMING A ROLE
.PP
A role to assume can be specified either in a vault's configuration (via
\fB\fCvaulted edit\fR) or specified via the \fB\fC\-\-assume\fR option.
.PP
Vaulted first opens the specified vault to retrieve the appropriate credentials. If a role is specified in the vault's configuration it will use that unless a role is explicitly passed in through the \fB\fC\-\-assume\fR option.
Vaulted first opens the specified vault to retrieve the appropriate credentials.
If a role is specified in the vault's configuration it will use that unless a
role is explicitly passed in through the \fB\fC\-\-assume\fR option.
21 changes: 15 additions & 6 deletions doc/vaulted-console.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ SYNOPSIS

`vaulted console`
`vaulted console` *name* [*OPTIONS*]
`vaulted console --assume` *arn* [*OPTIONS*]
`vaulted console --duration` *duration* [*OPTIONS*]
`vaulted console --assume` *arn* [*OPTIONS*]

DESCRIPTION
-----------

Opens the AWS console in the default web browser. Uses either the credentials in the current environment or the credentials in the specified vault. Console sessions either use the passed in duration, the provided vault's duration, or defaults to 1 hour.
Opens the AWS console in the default web browser. It will use the credentials
in the current environment unless a vault is specified, in which case it will
use the credentials in the vault.

Durations must be at least 15 minutes and less than 12 hours.
The session length will either use the passed in duration, the provided vault's
duration, or defaults to 1 hour in that order of preference. Durations must be
at least 15 minutes and less than 12 hours.

This requires that you have the sts:GetFederationToken or sts:AssumeRole
permissions enabled for your user.

OPTIONS
-------
Expand All @@ -33,12 +39,15 @@ OPTIONS
environment, configuration files, instance profile, etc.).

`--duration` *duration*
Specifies the duration that the console session is valid. The duration must be within the range 15m-12h.
Specifies the duration that the console session is valid. The duration must
be within the range 15m-12h.

ASSUMING A ROLE
---------------

A role to assume can be specified either in a vault's configuration (via
`vaulted edit`) or specified via the `--assume` option.

Vaulted first opens the specified vault to retrieve the appropriate credentials. If a role is specified in the vault's configuration it will use that unless a role is explicitly passed in through the `--assume` option.
Vaulted first opens the specified vault to retrieve the appropriate credentials.
If a role is specified in the vault's configuration it will use that unless a
role is explicitly passed in through the `--assume` option.
2 changes: 1 addition & 1 deletion man.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 08ccf83

Please sign in to comment.