Skip to content

Commit

Permalink
Merge pull request #997 from synfinatic/login-only-needed
Browse files Browse the repository at this point in the history
Continue work on login command
  • Loading branch information
synfinatic authored Jul 16, 2024
2 parents aa355dc + 84dfcf5 commit 198ac91
Show file tree
Hide file tree
Showing 18 changed files with 135 additions and 186 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
* No longer show help for sub-commands by default
* Warnings about invalid accounts/roles in config.yaml are now Debug messages #980
* Default ProfileFormat is now the `Friendly` format #992
* `config`, `config-profiles` and `completions` are now sub-commands of `setup` #975
* Refactor commands under `setup`: #975
* `config` is now `setup wizard` and `ConfigProfilesUrlAction` config option is no longer used
* `config-profiles` is now `setup profiles`
* `completions` is now `setup completions`
* Make `--url-action` and `--sts-refresh` command specific options
* Refactor `ecs ssl` commands to be just flags.
* Remove `--open` option from `process` command #291
* Only the and `cache` command will auto-update the contents of `~/.aws/config` #974
* `tags` command no longer supports the `--force-update` option
* Change default log level from `warn` to `info`
Expand Down
3 changes: 1 addition & 2 deletions cmd/aws-sso/cache_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ func (cc *CacheCmd) Run(ctx *RunContext) error {
// should we update our config??
if !ctx.Cli.Cache.NoConfigCheck && ctx.Settings.AutoConfigCheck {
if ctx.Settings.ConfigProfilesUrlAction != url.ConfigProfilesUndef {
action, _ := url.NewAction(string(ctx.Settings.ConfigProfilesUrlAction))
err := awsconfig.UpdateAwsConfig(ctx.Settings, action, "", true, false)
err := awsconfig.UpdateAwsConfig(ctx.Settings, "", true, false)
if err != nil {
log.Errorf("Unable to auto-update aws config file: %s", err.Error())
}
Expand Down
21 changes: 16 additions & 5 deletions cmd/aws-sso/console_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/sts"

// "github.com/davecgh/go-spew/spew"
"github.com/synfinatic/aws-sso-cli/internal/storage"
"github.com/synfinatic/aws-sso-cli/internal/url"
Expand All @@ -42,9 +43,11 @@ import (

type ConsoleCmd struct {
// Console actually should honor the --region flag
Duration int32 `kong:"short='d',help='AWS Session duration in minutes (default 60)'"` // default stored in DEFAULT_CONFIG
Prompt bool `kong:"short='P',help='Force interactive prompt to select role'"`
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int32 `kong:"short='d',help='AWS Session duration in minutes (default 60)'"` // default stored in DEFAULT_CONFIG
Prompt bool `kong:"short='P',help='Force interactive prompt to select role'"`
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
STSRefresh bool `kong:"help='Force refresh of STS Token Credentials'"`
UrlAction string `kong:"short='u',help='How to handle URLs [clip|exec|open|print|printurl|granted-containers|open-url-in-container] (default: open)'"`

Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNT_ID',predictor='accountId'"`
Expand Down Expand Up @@ -235,7 +238,7 @@ func openConsole(ctx *RunContext, accountid int64, role string) error {
log.WithError(err).Warnf("Unable to update cache")
}

creds := GetRoleCredentials(ctx, AwsSSO, accountid, role)
creds := GetRoleCredentials(ctx, AwsSSO, ctx.Cli.Console.STSRefresh, accountid, role)
return openConsoleAccessKey(ctx, creds, duration, region, accountid, role)
}

Expand Down Expand Up @@ -287,7 +290,15 @@ func openConsoleAccessKey(ctx *RunContext, creds *storage.RoleCredentials,
SigninToken: loginResponse.SigninToken,
}

urlOpener := url.NewHandleUrl(ctx.Settings.UrlAction, login.GetUrl(),
action, err := url.NewAction(ctx.Cli.Console.UrlAction)
if err != nil {
log.Fatalf("Invalid --url-action %s", ctx.Cli.Console.UrlAction)
}
if action == "" {
action = ctx.Settings.UrlAction
}

urlOpener := url.NewHandleUrl(action, login.GetUrl(),
ctx.Settings.Browser, ctx.Settings.UrlExecCommand)

urlOpener.ContainerSettings(containerParams(ctx, accountId, role))
Expand Down
2 changes: 1 addition & 1 deletion cmd/aws-sso/credentials_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (cc *CredentialsCmd) Run(ctx *RunContext) error {
return err
}

pCreds := GetRoleCredentials(ctx, AwsSSO, roleFlat.AccountId, roleFlat.RoleName)
pCreds := GetRoleCredentials(ctx, AwsSSO, ctx.Cli.Console.STSRefresh, roleFlat.AccountId, roleFlat.RoleName)

creds = append(creds, awsconfig.ProfileCredentials{
Profile: profile,
Expand Down
11 changes: 6 additions & 5 deletions cmd/aws-sso/ecs_client_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ import (

type EcsLoadCmd struct {
// AWS Params
Arn string `kong:"short='a',help='ARN of role to load',env='AWS_SSO_ROLE_ARN',predictor='arn'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to load',env='AWS_SSO_ACCOUNT_ID',predictor='accountId',xor='account'"`
Role string `kong:"short='R',help='Name of AWS Role to load',env='AWS_SSO_ROLE_NAME',predictor='role',xor='role'"`
Profile string `kong:"short='p',help='Name of AWS Profile to load',predictor='profile',xor='account,role'"`
Arn string `kong:"short='a',help='ARN of role to load',env='AWS_SSO_ROLE_ARN',predictor='arn'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to load',env='AWS_SSO_ACCOUNT_ID',predictor='accountId',xor='account'"`
Role string `kong:"short='R',help='Name of AWS Role to load',env='AWS_SSO_ROLE_NAME',predictor='role',xor='role'"`
Profile string `kong:"short='p',help='Name of AWS Profile to load',predictor='profile',xor='account,role'"`
STSRefresh bool `kong:"help='Force refresh of STS Token Credentials'"`

// Other params
Server string `kong:"help='Endpoint of aws-sso ECS Server',env='AWS_SSO_ECS_SERVER',default='localhost:4144'"`
Expand Down Expand Up @@ -82,7 +83,7 @@ func (cc *EcsProfileCmd) Run(ctx *RunContext) error {
func ecsLoadCmd(ctx *RunContext, accountId int64, role string) error {
c := newClient(ctx.Cli.Ecs.Load.Server, ctx)

creds := GetRoleCredentials(ctx, AwsSSO, accountId, role)
creds := GetRoleCredentials(ctx, AwsSSO, ctx.Cli.Ecs.Load.STSRefresh, accountId, role)

cache := ctx.Settings.Cache.GetSSO()
rFlat, err := cache.Roles.GetRole(accountId, role)
Expand Down
52 changes: 21 additions & 31 deletions cmd/aws-sso/ecs_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,54 +62,44 @@ func (cc *EcsAuthCmd) Run(ctx *RunContext) error {
}

type EcsSSLCmd struct {
Delete EcsSSLDeleteCmd `kong:"cmd,help='Delete the current SSL certificate/private key'"`
Print EcsSSLPrintCmd `kong:"cmd,help='Print the current SSL certificate'"`
Save EcsSSLSaveCmd `kong:"cmd,help='Save a new SSL certificate/private key'"`
}

type EcsSSLSaveCmd struct {
Certificate string `kong:"short=c,type='existingfile',help='Path to certificate chain PEM file',predictor='allFiles',required"`
PrivateKey string `kong:"short=p,type='existingfile',help='Path to private key file PEM file',predictor='allFiles'"`
Delete bool `kong:"short=d,help='Disable SSL and delete the current SSL cert/key',xor='flag,cert,key'"`
Print bool `kong:"short=p,help='Print the current SSL certificate',xor='flag,cert,key'"`
Certificate string `kong:"short=c,type='existingfile',help='Path to certificate chain PEM file',predictor='allFiles',group='add-ssl',xor='cert'"`
PrivateKey string `kong:"short=k,type='existingfile',help='Path to private key file PEM file',predictor='allFiles',group='add-ssl',xor='key'"`
Force bool `kong:"hidden,help='Force loading the certificate'"`
}

type EcsSSLDeleteCmd struct{}

func (cc *EcsSSLDeleteCmd) Run(ctx *RunContext) error {
return ctx.Store.DeleteEcsSslKeyPair()
}

type EcsSSLPrintCmd struct{}

func (cc *EcsSSLPrintCmd) Run(ctx *RunContext) error {
cert, err := ctx.Store.GetEcsSslCert()
if err != nil {
return err
}
if cert == "" {
return fmt.Errorf("no certificate found")
func (cc *EcsSSLCmd) Run(ctx *RunContext) error {
if ctx.Cli.Ecs.SSL.Delete {
return ctx.Store.DeleteEcsSslKeyPair()
} else if ctx.Cli.Ecs.SSL.Print {
cert, err := ctx.Store.GetEcsSslCert()
if err != nil {
return err
}
if cert == "" {
return fmt.Errorf("no certificate found")
}
fmt.Println(cert)
return nil
}
fmt.Println(cert)
return nil
}

func (cc *EcsSSLSaveCmd) Run(ctx *RunContext) error {
var privateKey, certChain []byte
var err error

if !ctx.Cli.Ecs.SSL.Save.Force {
if !ctx.Cli.Ecs.SSL.Force {
log.Warn("This feature is experimental and may not work as expected.")
log.Warn("Please read https://github.com/synfinatic/aws-sso-cli/issues/936 before contiuing.")
log.Fatal("Use `--force` to continue anyways.")
}

certChain, err = os.ReadFile(ctx.Cli.Ecs.SSL.Save.Certificate)
certChain, err = os.ReadFile(ctx.Cli.Ecs.SSL.Certificate)
if err != nil {
return fmt.Errorf("failed to read certificate chain file: %w", err)
}

if ctx.Cli.Ecs.SSL.Save.PrivateKey != "" {
privateKey, err = os.ReadFile(ctx.Cli.Ecs.SSL.Save.PrivateKey)
if ctx.Cli.Ecs.SSL.PrivateKey != "" {
privateKey, err = os.ReadFile(ctx.Cli.Ecs.SSL.PrivateKey)
if err != nil {
return fmt.Errorf("failed to read private key file: %w", err)
}
Expand Down
13 changes: 7 additions & 6 deletions cmd/aws-sso/exec_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ import (

type ExecCmd struct {
// AWS Params
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNT_ID',predictor='accountId'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_SSO_ROLE_NAME',predictor='role'"`
Profile string `kong:"short='p',help='Name of AWS Profile to assume',predictor='profile'"`
NoRegion bool `kong:"short='n',help='Do not set AWS_DEFAULT_REGION from config.yaml'"`
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNT_ID',predictor='accountId'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_SSO_ROLE_NAME',predictor='role'"`
Profile string `kong:"short='p',help='Name of AWS Profile to assume',predictor='profile'"`
NoRegion bool `kong:"short='n',help='Do not set AWS_DEFAULT_REGION from config.yaml'"`
STSRefresh bool `kong:"help='Force refresh of STS Token Credentials'"`

// Exec Params
Cmd string `kong:"arg,optional,name='command',help='Command to execute',env='SHELL'"`
Expand Down Expand Up @@ -93,7 +94,7 @@ func execCmd(ctx *RunContext, accountid int64, role string) error {

func execShellEnvs(ctx *RunContext, accountid int64, role, region string) map[string]string {
var err error
credsPtr := GetRoleCredentials(ctx, AwsSSO, accountid, role)
credsPtr := GetRoleCredentials(ctx, AwsSSO, ctx.Cli.Exec.STSRefresh, accountid, role)
creds := *credsPtr

ssoName, _ := ctx.Settings.GetSelectedSSOName(ctx.Cli.SSO)
Expand Down
13 changes: 11 additions & 2 deletions cmd/aws-sso/login_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ package main

import (
"github.com/synfinatic/aws-sso-cli/internal/sso"
"github.com/synfinatic/aws-sso-cli/internal/url"
)

type LoginCmd struct {
Threads int `kong:"help='Override number of threads for talking to AWS',default=${DEFAULT_THREADS}"`
UrlAction string `kong:"short='u',help='How to handle URLs [clip|exec|open|print|printurl|granted-containers|open-url-in-container] (default: open)'"`
Threads int `kong:"help='Override number of threads for talking to AWS',default=${DEFAULT_THREADS}"`
}

func (cc *LoginCmd) Run(ctx *RunContext) error {
Expand Down Expand Up @@ -55,7 +57,14 @@ func doAuth(ctx *RunContext) {
return
}

err := AwsSSO.Authenticate(ctx.Settings.UrlAction, ctx.Settings.Browser)
action, err := url.NewAction(ctx.Cli.Login.UrlAction)
if err != nil {
log.Fatalf("Invalid --url-action %s", ctx.Cli.Login.UrlAction)
}
if action == "" {
action = ctx.Settings.UrlAction
}
err = AwsSSO.Authenticate(action, ctx.Settings.Browser)
if err != nil {
log.WithError(err).Fatalf("Unable to authenticate")
}
Expand Down
46 changes: 26 additions & 20 deletions cmd/aws-sso/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,27 +107,27 @@ type CLI struct {
ConfigFile string `kong:"name='config',default='${CONFIG_FILE}',help='Config file',env='AWS_SSO_CONFIG',predict='allFiles'"`
LogLevel string `kong:"short='L',name='level',help='Logging level [error|warn|info|debug|trace] (default: info)'"`
Lines bool `kong:"help='Print line number in logs'"`
UrlAction string `kong:"short='u',help='How to handle URLs [clip|exec|open|print|printurl|granted-containers|open-url-in-container] (default: open)'"`
SSO string `kong:"short='S',help='Override default AWS SSO Instance',env='AWS_SSO',predictor='sso'"`
STSRefresh bool `kong:"help='Force refresh of STS Token Credentials'"`

// Commands
Cache CacheCmd `kong:"cmd,help='Force reload of cached AWS SSO role info and config.yaml'"`
Setup SetupCmd `kong:"cmd,help='Setup Wizard, Completions, etc'"`
Console ConsoleCmd `kong:"cmd,help='Open AWS Console using specificed AWS role/profile'"`
Credentials CredentialsCmd `kong:"cmd,help='Generate static AWS credentials for use with AWS CLI'"`
Default DefaultCmd `kong:"cmd,hidden,default='1'"` // list command without args
Ecs EcsCmd `kong:"cmd,help='ECS server/client commands'"`
Eval EvalCmd `kong:"cmd,help='Print AWS environment vars for use with eval $(aws-sso eval ...)'"`
Exec ExecCmd `kong:"cmd,help='Execute command using specified IAM role in a new shell'"`
List ListCmd `kong:"cmd,help='List all accounts / roles (default command)'"`
Login LoginCmd `kong:"cmd,help='Login to an AWS Identity Center instance'"`
Logout LogoutCmd `kong:"cmd,help='Logout from an AWS Identity Center instance and invalidate all credentials'"`
ListSSORoles ListSSORolesCmd `kong:"cmd,hidden,help='List AWS SSO Roles (debugging)'"`
Process ProcessCmd `kong:"cmd,help='Generate JSON for credential_process in ~/.aws/config'"`
Setup SetupCmd `kong:"cmd,help='Setup Wizard, Completions, Profiles, etc'"`
Tags TagsCmd `kong:"cmd,help='List tags'"`
Time TimeCmd `kong:"cmd,help='Print how much time before current STS Token expires'"`
Version VersionCmd `kong:"cmd,help='Print version and exit'"`

// Login Commands
Cache CacheCmd `kong:"cmd,help='Force reload of cached AWS SSO role info and config.yaml',group='login-required'"`
Console ConsoleCmd `kong:"cmd,help='Open AWS Console using specificed AWS role/profile',group='login-required'"`
Credentials CredentialsCmd `kong:"cmd,help='Generate static AWS credentials for use with AWS CLI',group='login-required'"`
Eval EvalCmd `kong:"cmd,help='Print AWS environment vars for use with eval $(aws-sso eval ...)',group='login-required'"`
Exec ExecCmd `kong:"cmd,help='Execute command using specified IAM role in a new shell',group='login-required'"`
Logout LogoutCmd `kong:"cmd,help='Logout from an AWS Identity Center instance and invalidate all credentials',group='login-required'"`
Process ProcessCmd `kong:"cmd,help='Generate JSON for AWS SDK credential_process command',group='login-required'"`
}

func main() {
Expand Down Expand Up @@ -162,7 +162,7 @@ func main() {
log.Fatalf("%s", err.Error())
}
return
case "ecs server":
case "ecs server", "ecs list", "ecs unload", "ecs profile":
// side-step the rest of the setup...
if err = ctx.Run(&runCtx); err != nil {
log.Fatalf("%s", err.Error())
Expand Down Expand Up @@ -192,7 +192,7 @@ func main() {
}

switch ctx.Command() {
case "list", "login", "ecs list", "ecs unload", "ecs profile":
case "list", "login", "tags":
// Initialize our AwsSSO variable & SecureStore
c := &runCtx
s, err := c.Settings.GetSelectedSSO(c.Cli.SSO)
Expand Down Expand Up @@ -259,12 +259,24 @@ func parseArgs(cli *CLI) (*kong.Context, sso.OverrideSettings) {
NoExpandSubcommands: true,
}

groups := []kong.Group{
{
Title: "Commands requiring login:",
Key: "login-required",
},
{
Title: "Add SSL Certificate/Key:",
Key: "add-ssl",
},
}

parser := kong.Must(
cli,
kong.Name("aws-sso"),
kong.Description("Securely manage temporary AWS API Credentials issued via AWS SSO"),
kong.ConfigureHelp(help),
vars,
kong.ExplicitGroups(groups),
)

p := predictor.NewPredictor(config.InsecureCacheFile(true), config.ConfigFile(true))
Expand All @@ -287,11 +299,6 @@ func parseArgs(cli *CLI) (*kong.Context, sso.OverrideSettings) {
ctx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)

action, err := url.NewAction(cli.UrlAction)
if err != nil {
log.Fatalf("Invalid --url-action %s", cli.UrlAction)
}

threads := 0
if cli.Cache.Threads != DEFAULT_THREADS {
threads = cli.Cache.Threads
Expand All @@ -305,7 +312,6 @@ func parseArgs(cli *CLI) (*kong.Context, sso.OverrideSettings) {
LogLevel: cli.LogLevel,
LogLines: cli.Lines,
Threads: threads, // must be > 0 to override config
UrlAction: action,
}

log.SetFormatter(&logrus.TextFormatter{
Expand All @@ -331,13 +337,13 @@ func (cc *VersionCmd) Run(ctx *RunContext) error {
}

// Get our RoleCredentials from the secure store or from AWS SSO
func GetRoleCredentials(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role string) *storage.RoleCredentials {
func GetRoleCredentials(ctx *RunContext, awssso *sso.AWSSSO, refreshSTS bool, accountid int64, role string) *storage.RoleCredentials {
creds := storage.RoleCredentials{}

// First look for our creds in the secure store, if we're not forcing a refresh
arn := utils.MakeRoleARN(accountid, role)
log.Debugf("Getting role credentials for %s", arn)
if !ctx.Cli.STSRefresh {
if !refreshSTS {
if roleFlat, err := ctx.Settings.Cache.GetRole(arn); err == nil {
if !roleFlat.IsExpired() {
if err := ctx.Store.GetRoleCredentials(arn, &creds); err == nil {
Expand Down
Loading

0 comments on commit 198ac91

Please sign in to comment.