Skip to content

Commit

Permalink
Start work on ECS Server
Browse files Browse the repository at this point in the history
- Add ECS Server doc
- create basic CLI utility boilerplate
- refactor exec to make it easier to use for ecs

Refs: #398
  • Loading branch information
synfinatic committed Aug 13, 2023
1 parent 54934b9 commit 4823afd
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 93 deletions.
132 changes: 132 additions & 0 deletions cmd/aws-sso/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package main

/*
* AWS SSO CLI
* Copyright (c) 2021-2022 Aaron Turner <synfinatic at gmail dot com>
*
* This program is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or with the authors permission any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import (
"fmt"

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

type SelectCliArgs struct {
Arn string
AccountId int64
RoleName string
Profile string
}

func NewSelectCliArgs(arn string, accountId int64, role, profile string) *SelectCliArgs {
return &SelectCliArgs{
Arn: arn,
AccountId: accountId,
RoleName: role,
Profile: profile,
}
}

func (a *SelectCliArgs) Update(ctx *RunContext) (*sso.AWSSSO, error) {
if a.AccountId != 0 && a.RoleName != "" {
return doAuth(ctx), nil
} else if a.Profile != "" {
awssso := doAuth(ctx)
cache := ctx.Settings.Cache.GetSSO()
rFlat, err := cache.Roles.GetRoleByProfile(a.Profile, ctx.Settings)
if err != nil {
return awssso, err
}

a.AccountId = rFlat.AccountId
a.RoleName = rFlat.RoleName

return awssso, nil
} else if a.Arn != "" {
awssso := doAuth(ctx)
accountId, role, err := utils.ParseRoleARN(a.Arn)
if err != nil {
return awssso, err
}
a.AccountId = accountId
a.RoleName = role

return awssso, nil
}
return &sso.AWSSSO{}, fmt.Errorf("Please specify both --account and --role")
}

// Creates a singleton AWSSO object post authentication
func doAuth(ctx *RunContext) *sso.AWSSSO {
if AwsSSO != nil {
return AwsSSO
}
s, err := ctx.Settings.GetSelectedSSO(ctx.Cli.SSO)
if err != nil {
log.Fatalf("%s", err.Error())
}
AwsSSO = sso.NewAWSSSO(s, &ctx.Store)
err = AwsSSO.Authenticate(ctx.Settings.UrlAction, ctx.Settings.Browser)
if err != nil {
log.WithError(err).Fatalf("Unable to authenticate")
}
if err = ctx.Settings.Cache.Expired(s); err != nil {
ssoName, err := ctx.Settings.GetSelectedSSOName(ctx.Cli.SSO)
log.Infof("Refreshing AWS SSO role cache for %s, please wait...", ssoName)
if err != nil {
log.Fatalf(err.Error())
}
if err = ctx.Settings.Cache.Refresh(AwsSSO, s, ssoName); err != nil {
log.WithError(err).Fatalf("Unable to refresh cache")
}
if err = ctx.Settings.Cache.Save(true); err != nil {
log.WithError(err).Errorf("Unable to save cache")
}

// should we update our config??
if !ctx.Cli.NoConfigCheck && ctx.Settings.AutoConfigCheck {
if ctx.Settings.ConfigProfilesUrlAction != url.ConfigProfilesUndef {
cfgFile := utils.GetHomePath("~/.aws/config")

action, _ := url.NewAction(string(ctx.Settings.ConfigProfilesUrlAction))
profiles, err := ctx.Settings.GetAllProfiles(action)
if err != nil {
log.Warnf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}

if err = profiles.UniqueCheck(ctx.Settings); err != nil {
log.Errorf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}

f, err := utils.NewFileEdit(CONFIG_TEMPLATE, profiles)
if err != nil {
log.Errorf("%s", err)
return AwsSSO
}

if err = f.UpdateConfig(true, false, cfgFile); err != nil {
log.Errorf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}
}
}
}
return AwsSSO
}
95 changes: 95 additions & 0 deletions cmd/aws-sso/ecs_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

/*
* AWS SSO CLI
* Copyright (c) 2021-2022 Aaron Turner <synfinatic at gmail dot com>
*
* This program is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or with the authors permission any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import (
"fmt"

"github.com/c-bata/go-prompt"
"github.com/synfinatic/aws-sso-cli/sso"
)

type EcsCmd struct {
Run EcsRunCmd `kong:"cmd,help='Run the ECS Server'"`
Load EcsLoadCmd `kong:"cmd,help='Load new IAM Role credentials into the ECS Server'"`
}

type EcsRunCmd struct {
Port uint16 `kong:"help='TCP port to listen on',env='AWS_SSO_ECS_PORT',required"`
}

type EcsLoadCmd 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'"`

// Other params
Port uint16 `kong:"help='TCP port of aws-sso ECS Server',env='AWS_SSO_ECS_PORT',required"`
}

func (cc *EcsRunCmd) Run(ctx *RunContext) error {
return nil
}

func (cc *EcsLoadCmd) Run(ctx *RunContext) error {
sci := NewSelectCliArgs(ctx.Cli.Exec.Arn, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, ctx.Cli.Exec.Profile)
if awssso, err := sci.Update(ctx); err == nil {
// successful lookup?
return ecsLoadCmd(ctx, awssso, sci.AccountId, sci.RoleName)
}

// nope, auto-complete mode
sso, err := ctx.Settings.GetSelectedSSO(ctx.Cli.SSO)
if err != nil {
return err
}
if err = ctx.Settings.Cache.Expired(sso); err != nil {
log.Infof(err.Error())
c := &CacheCmd{}
if err = c.Run(ctx); err != nil {
return err
}
}

sso.Refresh(ctx.Settings)
fmt.Printf("Please use `exit` or `Ctrl-D` to quit.\n")

c := NewTagsCompleter(ctx, sso, ecsLoadCmd)
opts := ctx.Settings.DefaultOptions(c.ExitChecker)
opts = append(opts, ctx.Settings.GetColorOptions()...)

p := prompt.New(
c.Executor,
c.Complete,
opts...,
)
p.Run()
return nil
}

// Loads our AWS API creds into the ECS Server
func ecsLoadCmd(ctx *RunContext, awssso *sso.AWSSSO, accountId int64, role string) error {
_ = GetRoleCredentials(ctx, awssso, accountId, role)
// creds := *credsPtr

// do something
return nil
}
30 changes: 4 additions & 26 deletions cmd/aws-sso/exec_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,10 @@ func (cc *ExecCmd) Run(ctx *RunContext) error {
ctx.Cli.Exec.Cmd = "cmd.exe"
}

// Did user specify the ARN or account/role?
if ctx.Cli.Exec.Profile != "" {
awssso := doAuth(ctx)
cache := ctx.Settings.Cache.GetSSO()
rFlat, err := cache.Roles.GetRoleByProfile(ctx.Cli.Exec.Profile, ctx.Settings)
if err != nil {
return err
}

return execCmd(ctx, awssso, rFlat.AccountId, rFlat.RoleName)
} else if ctx.Cli.Exec.Arn != "" {
awssso := doAuth(ctx)

accountid, role, err := utils.ParseRoleARN(ctx.Cli.Exec.Arn)
if err != nil {
return err
}

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)

return execCmd(ctx, awssso, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role)
sci := NewSelectCliArgs(ctx.Cli.Exec.Arn, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, ctx.Cli.Exec.Profile)
if awssso, err := sci.Update(ctx); err == nil {
// successful lookup?
return execCmd(ctx, awssso, sci.AccountId, sci.RoleName)
}

// Nope, auto-complete mode...
Expand Down
67 changes: 4 additions & 63 deletions cmd/aws-sso/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ var CommitID = "unknown"
var Delta = ""
var VALID_LOG_LEVELS = []string{"error", "warn", "info", "debug", "trace"}

var AwsSSO *sso.AWSSSO // global

type RunContext struct {
Kctx *kong.Context
Cli *CLI
Expand Down Expand Up @@ -125,8 +127,9 @@ type CLI struct {
Completions CompleteCmd `kong:"cmd,help='Manage shell completions'"`
ConfigProfiles ConfigProfilesCmd `kong:"cmd,help='Update ~/.aws/config with AWS SSO profiles from the cache'"`
Config ConfigCmd `kong:"cmd,help='Run the configuration wizard'"`
Version VersionCmd `kong:"cmd,help='Print version and exit'"`
Ecs EcsCmd `kong:"cmd,help='ECS Server commands'"`
Setup SetupCmd `kong:"cmd,hidden"` // need this so variables are visisble.
Version VersionCmd `kong:"cmd,help='Print version and exit'"`
}

func main() {
Expand Down Expand Up @@ -325,68 +328,6 @@ func GetRoleCredentials(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, ro
return &creds
}

var AwsSSO *sso.AWSSSO // global

// Creates a singleton AWSSO object post authentication
func doAuth(ctx *RunContext) *sso.AWSSSO {
if AwsSSO != nil {
return AwsSSO
}
s, err := ctx.Settings.GetSelectedSSO(ctx.Cli.SSO)
if err != nil {
log.Fatalf("%s", err.Error())
}
AwsSSO = sso.NewAWSSSO(s, &ctx.Store)
err = AwsSSO.Authenticate(ctx.Settings.UrlAction, ctx.Settings.Browser)
if err != nil {
log.WithError(err).Fatalf("Unable to authenticate")
}
if err = ctx.Settings.Cache.Expired(s); err != nil {
ssoName, err := ctx.Settings.GetSelectedSSOName(ctx.Cli.SSO)
log.Infof("Refreshing AWS SSO role cache for %s, please wait...", ssoName)
if err != nil {
log.Fatalf(err.Error())
}
if err = ctx.Settings.Cache.Refresh(AwsSSO, s, ssoName); err != nil {
log.WithError(err).Fatalf("Unable to refresh cache")
}
if err = ctx.Settings.Cache.Save(true); err != nil {
log.WithError(err).Errorf("Unable to save cache")
}

// should we update our config??
if !ctx.Cli.NoConfigCheck && ctx.Settings.AutoConfigCheck {
if ctx.Settings.ConfigProfilesUrlAction != url.ConfigProfilesUndef {
cfgFile := utils.GetHomePath("~/.aws/config")

action, _ := url.NewAction(string(ctx.Settings.ConfigProfilesUrlAction))
profiles, err := ctx.Settings.GetAllProfiles(action)
if err != nil {
log.Warnf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}

if err = profiles.UniqueCheck(ctx.Settings); err != nil {
log.Errorf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}

f, err := utils.NewFileEdit(CONFIG_TEMPLATE, profiles)
if err != nil {
log.Errorf("%s", err)
return AwsSSO
}

if err = f.UpdateConfig(true, false, cfgFile); err != nil {
log.Errorf("Unable to update %s: %s", cfgFile, err.Error())
return AwsSSO
}
}
}
}
return AwsSSO
}

func logLevelValidate(level string) error {
if utils.StrListContains(level, VALID_LOG_LEVELS) || level == "" {
return nil
Expand Down
8 changes: 4 additions & 4 deletions docs/ecs-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ AWS clients and `aws-sso` should use:

`AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:8444/get-credentials`

**Note:** It is important to _not_ set `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` as that
takes precidence for `AWS_CONTAINER_CREDENTIALS_FULL_URI`.
**Note:** It is important to _not_ set `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`
as that takes precidence for `AWS_CONTAINER_CREDENTIALS_FULL_URI`.

## Selecting a role via ECS Server

Before you can assume a role, you must select an IAM role for the aws-sso ecs server to
present to clients.
Before you can assume a role, you must select an IAM role for the aws-sso ecs
server to present to clients.

```bash
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:8444/get-credentials aws-sso ecs load ...
Expand Down

0 comments on commit 4823afd

Please sign in to comment.