Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework how we manage the AWS_DEFAULT_REGION #153

Merged
merged 1 commit into from
Nov 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ Suggested use (bash): `eval $(aws-sso eval <args>)`

Flags:

* `--region <region>`, `-r` -- Specify the `$AWS_DEFAULT_REGION` to use
* `--arn <arn>`, `-a` -- ARN of role to assume (`$AWS_SSO_ROLE_ARN`)
* `--account <account>`, `-A` -- AWS AccountID of role to assume (`$AWS_SSO_ACCOUNTID`)
* `--duration <minutes>`, `-d` -- AWS Session duration in minutes (default 60) (`$AWS_SSO_DURATION`)
* `--role <role>`, `-R` -- Name of AWS Role to assume (requires `--account`) (`$AWS_SSO_ROLE`)
* `--no-region` -- Do not set the AWS_DEFAULT_REGION from config

**Note:** The `eval` command will never honor the `--url-action=print`
option as this will intefere with bash/zsh/etc ability to evaluate
Expand All @@ -167,12 +167,12 @@ commands.

Flags:

* `--region <region>, `-r` -- Specify the `$AWS_DEFAULT_REGION` to use
* `--arn <arn>`, `-a` -- ARN of role to assume (`$AWS_SSO_ROLE_ARN`)
* `--account <account>`, `-A` -- AWS AccountID of role to assume (`$AWS_SSO_ACCOUNTID`)
* `--duration <minutes>`, `-d` -- AWS Session duration in minutes (default 60) (`$AWS_SSO_DURATION`)
* `--env`, `-e` -- Use existing ENV vars generated by AWS SSO to generate a URL
* `--role <role>`, `-R` -- Name of AWS Role to assume (requires `--account`) (`$AWS_SSO_ROLE`)
* `--no-region` -- Do not set the AWS_DEFAULT_REGION from config

Arguments: `[<command>] [<args> ...]`

Expand Down Expand Up @@ -276,6 +276,7 @@ SSOConfig:
<Name of AWS SSO>:
SSORegion: <AWS Region where AWS SSO is deployed>
StartUrl: <URL for AWS SSO Portal>
DefaultRegion: <AWS_DEFAULT_REGION>
Accounts: # optional block for specifying tags & overrides
<AccountId>:
Name: <Friendly Name of Account>
Expand Down Expand Up @@ -330,6 +331,24 @@ SSO provider (Okta/OneLogin/etc).

The `StartUrl` is required.

### SSORegion

Each AWS SSO instance is configured in a specific AWS region which needs to be set here.

The `SSORegion` is required.

### DefaultRegion

The `DefaultRegion` allows you to define a value for the `$AWS_DEFAULT_REGION` when switching to a role.
Note that, aws-sso will NEVER change an existing `$AWS_DEFAULT_REGION` set by the user.

`DefaultRegion` can be specified at the following levels and the first match is selected:

1. `SSOConfig -> <Name of the AWS SSO> -> Accounts -> <AccountId> -> Roles -> <RoleName>`
1. `SSOConfig -> <Name of the AWS SSO> -> Accounts -> <AccountId>`
1. `SSOConfig -> <Name of AWS SSO>`
1. Top level of the file

### Accounts

The `Accounts` block is completely optional! The only purpose of this block
Expand Down
31 changes: 18 additions & 13 deletions cmd/console_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
const AWS_FEDERATED_URL = "https://signin.aws.amazon.com/federation"

type ConsoleCmd struct {
// Console actually should honor the --region flag
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
UseEnv bool `kong:"short='e',help='Use existing ENV vars to generate URL'"`
Expand All @@ -59,41 +60,38 @@ func (cc *ConsoleCmd) Run(ctx *RunContext) error {
return err
}

region := ctx.Settings.GetDefaultRegion(accountid, role)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
return openConsole(ctx, awssso, accountid, role, region)
return openConsole(ctx, awssso, accountid, role)
} else if ctx.Cli.Console.AccountId != 0 || ctx.Cli.Console.Role != "" {
if ctx.Cli.Console.AccountId == 0 || ctx.Cli.Console.Role == "" {
return fmt.Errorf("Please specify both --account and --role")
}
awssso := doAuth(ctx)
region := ctx.Settings.GetDefaultRegion(ctx.Cli.Console.AccountId, ctx.Cli.Console.Role)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
return openConsole(ctx, awssso, ctx.Cli.Console.AccountId, ctx.Cli.Console.Role, region)
return openConsole(ctx, awssso, ctx.Cli.Console.AccountId, ctx.Cli.Console.Role)
} else if ctx.Cli.Console.UseEnv {
if ctx.Cli.Console.AccessKeyId == "" {
return fmt.Errorf("AWS_ACCESS_KEY_ID is not set")
}

if ctx.Cli.Console.SecretAccessKey == "" {
return fmt.Errorf("AWS_SECRET_ACCESS_KEY is not set")
}

if ctx.Cli.Console.SessionToken == "" {
return fmt.Errorf("AWS_SESSION_TOKEN is not set")
}

creds := storage.RoleCredentials{
AccessKeyId: ctx.Cli.Console.AccessKeyId,
SecretAccessKey: ctx.Cli.Console.SecretAccessKey,
SessionToken: ctx.Cli.Console.SessionToken,
}

accountid, err := strconv.ParseInt(os.Getenv("AWS_ACCOUNT_ID"), 10, 64)
if err != nil {
return fmt.Errorf("Unable to parse AWS_ACCOUNT_ID: %s", os.Getenv("AWS_ACCOUNT_ID"))
}
region := ctx.Settings.GetDefaultRegion(accountid, os.Getenv("AWS_ROLE_NAME"))

region := ctx.Settings.GetDefaultRegion(accountid, os.Getenv("AWS_ROLE_NAME"), false)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
Expand Down Expand Up @@ -124,7 +122,12 @@ func (cc *ConsoleCmd) Run(ctx *RunContext) error {
}

// opens the AWS console or just prints the URL
func openConsole(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, region string) error {
func openConsole(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role string) error {
region := ctx.Settings.GetDefaultRegion(accountid, role, false)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}

ctx.Settings.Cache.AddHistory(utils.MakeRoleARN(accountid, role), ctx.Settings.HistoryLimit)
if err := ctx.Settings.Cache.Save(false); err != nil {
log.WithError(err).Warnf("Unable to update cache")
Expand All @@ -149,19 +152,21 @@ func openConsoleAccessKey(ctx *RunContext, creds *storage.RoleCredentials, durat
if err != nil {
return fmt.Errorf("Unable to login to AWS: %s", err.Error())
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

loginResponse := LoginResponse{}
err = json.Unmarshal(body, &loginResponse)
if err != nil {
return fmt.Errorf("Error parsing Login response: %s", err.Error())
}

login := LoginUrlParams{
Issuer: "https://github.com/synfinatic/aws-sso-cli", // FIXME
Issuer: "https://github.com/synfinatic/aws-sso-cli",
Destination: fmt.Sprintf("https://console.aws.amazon.com/console/home?region=%s", region),
SigninToken: loginResponse.SigninToken,
}
Expand Down
22 changes: 13 additions & 9 deletions cmd/eval_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ import (

type EvalCmd struct {
// AWS Params
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn',xor='arn-1',xor='arn-2'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNTID',predictor='accountId',xor='arn-1'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_ROLE_NAME',predictor='role',xor='arn-2'"`
Clear bool `kong:"short='c',help='Generate \"unset XXXX\" commands to clear environment'"`
NoRegion bool `kong:"help='Do not set/clear AWS_DEFAULT_REGION from config'"`
}

func (cc *EvalCmd) Run(ctx *RunContext) error {
var err error

if ctx.Cli.Eval.Clear {
unsetEnvVars()
unsetEnvVars(ctx)
return nil
}

Expand All @@ -54,7 +54,7 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
// if CLI args are speecified, use that
role := ctx.Cli.Eval.Role
account := ctx.Cli.Eval.AccountId
region := ctx.Cli.Eval.Region
region := ctx.Settings.GetDefaultRegion(account, role, false)

if len(ctx.Cli.Eval.Arn) > 0 {
account, role, err = utils.ParseRoleARN(ctx.Cli.Eval.Arn)
Expand All @@ -78,10 +78,6 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
log.Infof("Refreshing current AWS Role credentials")
}

if len(region) == 0 {
region = ctx.Settings.GetDefaultRegion(account, role)
}

awssso := doAuth(ctx)
for k, v := range execShellEnvs(ctx, awssso, account, role, region) {
if strings.Contains(v, " ") {
Expand All @@ -93,7 +89,7 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
return nil
}

func unsetEnvVars() {
func unsetEnvVars(ctx *RunContext) {
envs := []string{
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
Expand All @@ -102,9 +98,17 @@ func unsetEnvVars() {
"AWS_ROLE_NAME",
"AWS_ROLE_ARN",
"AWS_SESSION_EXPIRATION",
"AWS_DEFAULT_REGION",
"AWS_SSO_PROFILE",
}

// clear the region if
// 1. User did not specify --no-region AND
// 2. The AWS_DEFAULT_REGION is managed by us (tracks AWS_SSO_DEFAULT_REGION)
if !ctx.Cli.Eval.NoRegion && os.Getenv("AWS_DEFAULT_REGION") == os.Getenv("AWS_SSO_DEFAULT_REGION") {
envs = append(envs, "AWS_DEFAULT_REGION")
envs = append(envs, "AWS_SSO_DEFAULT_REGION")
}

for _, e := range envs {
fmt.Printf("unset %s\n", e)
}
Expand Down
21 changes: 12 additions & 9 deletions cmd/exec_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import (

type ExecCmd struct {
// AWS Params
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn',xor='arn-1',xor='arn-2'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNTID',predictor='accountId',xor='arn-1'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_SSO_ROLE',predictor='role',xor='arn-2'"`
NoRegion bool `kong:"help='Do not set AWS_DEFAULT_REGION from config'"`

// Exec Params
Cmd string `kong:"arg,optional,name='command',help='Command to execute',env='SHELL'"`
Expand All @@ -62,16 +62,14 @@ func (cc *ExecCmd) Run(ctx *RunContext) error {
return err
}

region := ctx.Settings.GetDefaultRegion(accountid, role)
return execCmd(ctx, awssso, accountid, role, region)
return execCmd(ctx, awssso, accountid, role)
} else if ctx.Cli.Exec.AccountId != 0 || ctx.Cli.Exec.Role != "" {
if ctx.Cli.Exec.AccountId == 0 || ctx.Cli.Exec.Role == "" {
return fmt.Errorf("Please specify both --account and --role")
}
awssso := doAuth(ctx)

region := ctx.Settings.GetDefaultRegion(ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role)
return execCmd(ctx, awssso, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, region)
return execCmd(ctx, awssso, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role)
}

// Nope, auto-complete mode...
Expand Down Expand Up @@ -125,7 +123,9 @@ func accountIdToStr(id int64) string {
}

// Executes Cmd+Args in the context of the AWS Role creds
func execCmd(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, region string) error {
func execCmd(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role string) error {
region := ctx.Settings.GetDefaultRegion(ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, ctx.Cli.Exec.NoRegion)

ctx.Settings.Cache.AddHistory(utils.MakeRoleARN(accountid, role), ctx.Settings.HistoryLimit)
if err := ctx.Settings.Cache.Save(false); err != nil {
log.WithError(err).Warnf("Unable to update cache")
Expand Down Expand Up @@ -160,10 +160,13 @@ func execShellEnvs(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, r
"AWS_ROLE_NAME": creds.RoleName,
"AWS_SESSION_EXPIRATION": creds.ExpireString(),
}
if ctx.Cli.Exec.Region != "" {
shellVars["AWS_DEFAULT_REGION"] = ctx.Cli.Exec.Region
} else if region != "" {

if len(region) > 0 {
shellVars["AWS_DEFAULT_REGION"] = region
shellVars["AWS_SSO_DEFAULT_REGION"] = region
} else {
// we no longer manage the region
shellVars["AWS_SSO_DEFAULT_REGION"] = ""
}

var profileFormat string = AwsSsoProfileTemplate
Expand Down
5 changes: 2 additions & 3 deletions cmd/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"github.com/synfinatic/aws-sso-cli/utils"
)

type CompleterExec = func(*RunContext, *sso.AWSSSO, int64, string, string) error
type CompleterExec = func(*RunContext, *sso.AWSSSO, int64, string) error

type TagsCompleter struct {
ctx *RunContext
Expand Down Expand Up @@ -106,9 +106,8 @@ func (tc *TagsCompleter) Executor(args string) {
if err != nil {
log.Fatalf("Unable to parse %s: %s", roleArn, err.Error())
}
region := tc.ctx.Settings.GetDefaultRegion(aId, rName)
awsSSO := doAuth(tc.ctx)
err = tc.exec(tc.ctx, awsSSO, aId, rName, region)
err = tc.exec(tc.ctx, awsSSO, aId, rName)
if err != nil {
log.Fatalf("Unable to exec: %s", err.Error())
}
Expand Down
Loading