iam

package
v1.19.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 1, 2021 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ActionUserKeys = &cli.Command{
		Name:  "keys",
		Usage: "view Access Keys associated with an IAM User",
		UsageText: `
Produces a table of Access Keys that are associated with an IAM User.

Interactive mode allows for you to Activate, Deactivate and Delete Access Keys.
`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "user",
				Aliases:  []string{"u"},
				Usage:    "user name to look for",
				Required: true,
			},
			&cli.BoolFlag{
				Name:    "interactive",
				Aliases: []string{"i"},
				Usage:   "interactive mode that allows status changes of keys",
			},
		},
		Action: func(c *cli.Context) error {
			un := c.String("user")
			user := &User{
				userName: &un,
			}
			err := user.GetAccessKeys()
			if err != nil {
				return cli.Exit(err, 2)
			}

			renderUserAccessKeys(user.AccessKeys())

			if c.Bool("interactive") {
				err = promptForAccessKeyAction(user)
				if err != nil {
					return cli.Exit(err, 2)
				}
			}

			return nil
		},
	}
	ActionUserPermissions = &cli.Command{
		Name:    "permissions",
		Aliases: []string{"p"},
		Usage:   "view permissions that are associated with an IAM User",
		UsageText: `
Produces a table of Groups and Policies that are attached to an IAM User.

Interactive mode allows for you to detach a permission from an IAM User.
`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:     "user",
				Aliases:  []string{"u"},
				Usage:    "user name to look for",
				Required: true,
			},
			&cli.BoolFlag{
				Name:    "interactive",
				Aliases: []string{"i"},
				Usage:   "interactive mode that allows for removal of permissions",
			},
		},
		Action: func(c *cli.Context) error {
			un := c.String("user")
			user := &User{
				userName: &un,
			}
			err := user.GetPermissions()
			if err != nil {
				return cli.Exit(err, 2)
			}

			renderPermissions(user.Permissions())

			if c.Bool("interactive") {
				err = promptForPermissionsAction(user)
				if err != nil {
					return cli.Exit(err, 2)
				}
			}
			return nil
		},
	}
	ActionDeprecatedUserReport = &cli.Command{
		Name:      "user-report",
		Usage:     "DEPRECATED: Please use the `iam user report` command",
		UsageText: "DEPRECATED: Please use the `iam user report` command",
		Action: func(c *cli.Context) error {
			return cli.Exit(fmt.Errorf("deprecated: please user the `iam report` command"), 2)
		},
		Hidden: true,
	}
	ActionUserReport = &cli.Command{
		Name:  "report",
		Usage: "generates report of IAM Users and Access Key Usage",
		UsageText: `
This action will generate a report for all Users within an AWS account with the details
specific user authentication methods.

Interactive mode will allow you to search for an IAM User and take actions once an IAM User is
selected.

USER [string]:
  - The user name

STATUS [enum]:
  - PASS: When a does NOT have Console Access and has NO Access Keys or only INACTIVE Access Keys
  - FAIL: When an IAM User has Console Access
  - WARN: When an IAM User does NOT have Console Access, but does have at least 1 ACTIVE Access Key
  - UNKNOWN: Catch all for cases not handled.

AGE [duration]:
  - Time since User was created

CONSOLE [bool]:
  - Does the User have Console Access? YES/NO

LAST LOGIN [duration]:
  - Time since User was created
  - NONE if the User does not have Console Access or if the User has NEVER logged in.

PERMISSIONS [struct]:
  - G: n -> Groups that the User belongs to
  - P: n -> Policies that are attached to the User
  - I: n -> Inline Policies that are attached to the User

ACCESS KEY DETAILS [sub table]:
  - Primary header row is the number of Access Keys associated with the User

  KEY ID [string]:
    - The AWS_ACCESS_KEY_ID

  STATUS [enum]:
    - Active/Inactive

  LAST USED [duration]:
    - Time since the Access Key was last used.

  SERVICE [string]:
    - The last AWS Service that the Access Key was used to access at the "LAST USED" time.
`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:  "show-only",
				Usage: "filter results to show only pass, warn or fail",
			},
			&cli.BoolFlag{
				Name:    "interactive",
				Aliases: []string{"i"},
				Usage:   "after generating the report, prompt for digging into a user",
			},
		},
		Action: func(c *cli.Context) error {
			kl := kiam.Extend("ActionUserReport")
			showOnly := ""
			if c.String("show-only") != "" {
				showOnly = strings.ToLower(c.String("show-only"))
			}
			allowedFilters := []string{"", "pass", "warn", "fail"}
			if !funk.ContainsString(allowedFilters, showOnly) {
				return cli.Exit(fmt.Sprintf("Invalid value for show-only. Must be one of: %v", allowedFilters), 3)
			}

			users, err := getAllUsers(&buildUserDataOptions{
				checkConsoleAccess: true,
				getPermissions:     true,
				getAccessKeys:      true,
			})
			if err != nil {
				return cli.Exit(err, 2)
			}

			err = renderUsersReport(users, showOnly)
			if err != nil {
				return cli.Exit(err, 2)
			}

			if c.Bool("interactive") {
				// prompt for user selection
				var options []string
				for _, user := range users {
					options = append(options, user.UserName())
				}

				sort.Strings(options)

				var passedUser string
				prompt := &survey.Select{
					Message: "Pick an IAM User:",
					Options: options,
				}
				err := survey.AskOne(prompt, &passedUser)
				if err != nil {
					return cli.Exit(err, 2)
				}
				kl.Log(passedUser)

				var user *User
				for _, u := range users {
					if u.UserName() == passedUser {
						user = u
					}
				}
				kl.Log(user)

				if user == nil {
					return fmt.Errorf("user not found: %s", passedUser)
				}

				viewUserDetails(user)

				err = modifyUser(user)
				if err != nil {
					return cli.Exit(err, 2)
				}
			}
			return nil
		},
	}
	ActionUserModify = &cli.Command{
		Name:  "modify",
		Usage: "modify an IAM User within AWS",
		UsageText: `
This action allows you to take actions to modify a user's Permissions (Groups and Policies)
and the state of their Access Keys (Active, Inactive, Delete).
`,
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "user",
				Aliases: []string{"u"},
				Usage:   "user name to look for",
			},
			&cli.StringFlag{
				Name:  "show-only",
				Usage: "filter results to show only pass, warn or fail",
			},
		},
		Action: func(c *cli.Context) error {
			kl := kiam.Extend("ActionUserModify")
			showOnly := ""
			if c.String("show-only") != "" {
				showOnly = strings.ToLower(c.String("show-only"))
			}
			allowedFilters := []string{"", "pass", "warn", "fail"}
			if !funk.ContainsString(allowedFilters, showOnly) {
				return cli.Exit(fmt.Sprintf("Invalid value for show-only. Must be one of: %v", allowedFilters), 3)
			}

			var err error
			var user *User
			passedUser := c.String("user")
			if passedUser == "" {
				users, err := getAllUsers(&buildUserDataOptions{})
				if err != nil {
					return cli.Exit(err, 2)
				}

				// prompt for user selection
				var options []string
				for _, user := range users {
					options = append(options, user.UserName())
				}

				sort.Strings(options)

				prompt := &survey.Select{
					Message: "Pick an IAM User:",
					Options: options,
				}
				err = survey.AskOne(prompt, &passedUser)
				if err != nil {
					return cli.Exit(err, 2)
				}
				kl.Log(passedUser)
			}

			user, err = getUser(passedUser, &buildUserDataOptions{
				checkConsoleAccess: true,
				getAccessKeys:      true,
				getPermissions:     true,
			})
			if err != nil {
				return cli.Exit(err, 2)
			}

			kl.Log(user)
			if user == nil {
				return fmt.Errorf("user not found: %s", passedUser)
			}

			viewUserDetails(user)

			err = modifyUser(user)
			if err != nil {
				return cli.Exit(err, 2)
			}

			return nil
		},
	}
	ActionKeysDeactivate = &cli.Command{
		Name:  "deactivate",
		Usage: "bulk deactivate Access Keys",
		UsageText: `
This action will check ALL Access Keys to determine if they meet the criteria
to be marked as INACTIVE within IAM.

Current rules are:

- If a keys HAS been used, the last usage was not within the last n(threshold) days
- If a key has NEVER been used, that the key was created at least n(threshold) days ago
`,
		Flags: []cli.Flag{
			&cli.Int64Flag{
				Name:  "threshold",
				Usage: "number of days to pass as check for qualification",
				Value: 180,
			},
		},
		Action: func(c *cli.Context) error {
			users, err := getAllUsers(&buildUserDataOptions{
				checkConsoleAccess: false,
				getPermissions:     false,
				getAccessKeys:      true,
			})
			if err != nil {
				return cli.Exit(err, 2)
			}

			sort.Slice(users, func(i, j int) bool {
				return strings.ToLower(users[i].UserName()) < strings.ToLower(users[j].UserName())
			})

			var toAction []*AccessKey
			thresholdCheck := c.Int64("threshold") * 24
			for _, user := range users {
				for _, key := range user.accessKeys {
					if markToDeactivate(key, thresholdCheck) {
						toAction = append(toAction, key)
					}
				}
			}

			if len(toAction) == 0 {
				fmt.Println(emoji.Sprint(":check_mark_button: No Access Keys qualify."))
				return nil
			}

			fmt.Println(emoji.Sprintf(":warning: Found %d Access Keys that qualify for deactivation :warning:", len(toAction)))

			takeAction := false
			prompt := &survey.Confirm{
				Message: "View keys that qualify?",
			}
			err = survey.AskOne(prompt, &takeAction)
			if err != nil {
				return err
			}

			if takeAction {
				renderUserAccessKeys(toAction)
			}

			takeAction = false
			p := &survey.Confirm{
				Message: "Deactivate Keys?",
			}
			err = survey.AskOne(p, &takeAction)
			if err != nil {
				return err
			}

			if takeAction {
				err = actionOnUserAccessKey(toAction, "DEACTIVATE")
				if err != nil {
					return err
				}
			}

			return nil
		},
	}
	ActionKeysRecent = &cli.Command{
		Name:      "recent",
		Usage:     "list Access Keys that have been recently used",
		UsageText: "This action will check ALL Access Keys to determine if they have been used within the threshold time.",
		Flags: []cli.Flag{
			&cli.Int64Flag{
				Name:    "threshold",
				Aliases: []string{"t"},
				Usage:   "number of Units to check for qualification",
				Value:   7,
			},
			&cli.StringFlag{
				Name:    "units",
				Aliases: []string{"u"},
				Usage:   "hours, days, weeks, months",
				Value:   "days",
			},
		},
		Action: func(c *cli.Context) error {
			units := strings.ToLower(c.String("units"))
			allowedFilters := []string{"hours", "days", "weeks", "months"}
			if !funk.ContainsString(allowedFilters, units) {
				return cli.Exit(fmt.Sprintf("Invalid value for units. Must be one of: %v", allowedFilters), 3)
			}

			threshold := c.Int64("threshold")
			var check int64
			switch units {
			case "hours":
				check = threshold
			case "days":
				check = threshold * 24
			case "weeks":
				check = threshold * 7 * 24
			case "months":
				check = threshold * 30 * 24
			}

			users, err := getAllUsers(&buildUserDataOptions{
				checkConsoleAccess: false,
				getPermissions:     false,
				getAccessKeys:      true,
			})
			if err != nil {
				return cli.Exit(err, 2)
			}

			var toAction []*AccessKey
			for _, user := range users {
				for _, key := range user.accessKeys {
					if markAsRecentlyUsed(key, check) {
						toAction = append(toAction, key)
					}
				}
			}

			if len(toAction) == 0 {
				fmt.Println(emoji.Sprint(":check_mark_button: No Access Keys qualify."))
				return nil
			}
			fmt.Println(emoji.Sprintf(":peacock: Found %d Access Keys used in the last %d %s :whale:", len(toAction), threshold, units))

			sort.Slice(toAction, func(i, j int) bool {
				return toAction[i].LastUsedDate().After(toAction[j].LastUsedDate())
			})

			renderUserAccessKeys(toAction)

			return nil
		},
	}
	ActionKeysUnused = &cli.Command{
		Name:  "unused",
		Usage: "list Access Keys that have NEVER been used",
		Action: func(c *cli.Context) error {
			users, err := getAllUsers(&buildUserDataOptions{
				checkConsoleAccess: false,
				getPermissions:     false,
				getAccessKeys:      true,
			})
			if err != nil {
				return cli.Exit(err, 2)
			}

			var toAction []*AccessKey
			for _, user := range users {
				for _, key := range user.accessKeys {
					if markAsNeverUsed(key) {
						toAction = append(toAction, key)
					}
				}
			}

			if len(toAction) == 0 {
				fmt.Println(emoji.Sprint(":check_mark_button: No Access Keys qualify."))
				return nil
			}
			fmt.Println(emoji.Sprintf(":doughnut: Found %d Access Keys that have NEVER been used :coffee:", len(toAction)))

			sort.Slice(toAction, func(i, j int) bool {
				return toAction[i].CreatedDate().Before(toAction[j].CreatedDate())
			})

			renderUserAccessKeys(toAction)

			return nil
		},
	}
)

Functions

This section is empty.

Types

type AccessKey added in v1.17.0

type AccessKey struct {
	// contains filtered or unexported fields
}

func (*AccessKey) Activate added in v1.17.0

func (ak *AccessKey) Activate() error

func (*AccessKey) CreatedDate added in v1.19.0

func (ak *AccessKey) CreatedDate() time.Time

func (*AccessKey) Deactivate added in v1.17.0

func (ak *AccessKey) Deactivate() error

func (*AccessKey) Delete added in v1.17.0

func (ak *AccessKey) Delete() error

func (*AccessKey) ID added in v1.19.0

func (ak *AccessKey) ID() string

func (*AccessKey) LastUsedDate added in v1.19.0

func (ak *AccessKey) LastUsedDate() time.Time

func (*AccessKey) LastUsedService added in v1.19.0

func (ak *AccessKey) LastUsedService() string

func (*AccessKey) Status added in v1.19.0

func (ak *AccessKey) Status() string

func (*AccessKey) UserName added in v1.19.0

func (ak *AccessKey) UserName() string

type Permission added in v1.17.0

type Permission struct {
	ARN  string
	Name string
	Type string
}

type User added in v1.16.0

type User struct {
	// contains filtered or unexported fields
}

func (User) ARN added in v1.16.0

func (u User) ARN() string

func (User) AccessKeys added in v1.17.0

func (u User) AccessKeys() []*AccessKey

func (User) AccessKeysCount added in v1.16.0

func (u User) AccessKeysCount() int

func (User) CheckStatus added in v1.16.0

func (u User) CheckStatus() string

TODO: Make the Status more robust

func (User) CreatedDate added in v1.16.0

func (u User) CreatedDate() time.Time

func (User) CreatedDateDuration added in v1.16.0

func (u User) CreatedDateDuration() string

func (User) FormattedCheckStatus added in v1.16.0

func (u User) FormattedCheckStatus() string

func (User) FormattedLastLoginDateDuration added in v1.16.0

func (u User) FormattedLastLoginDateDuration() string

func (*User) GetAccessKeys added in v1.17.0

func (u *User) GetAccessKeys() error

func (*User) GetPermissions added in v1.17.0

func (u *User) GetPermissions() error

func (User) Groups added in v1.16.0

func (u User) Groups() []*Permission

func (User) HasAccessKeys added in v1.16.0

func (u User) HasAccessKeys() bool

func (User) HasConsoleAccess added in v1.16.0

func (u User) HasConsoleAccess() bool

func (User) HasPermissions added in v1.16.0

func (u User) HasPermissions() bool

func (User) ID added in v1.16.0

func (u User) ID() string

func (User) InlinePolicies added in v1.19.3

func (u User) InlinePolicies() []*Permission

func (User) LastLogin added in v1.16.0

func (u User) LastLogin() time.Time

func (User) LastLoginDuration added in v1.16.0

func (u User) LastLoginDuration() string

func (User) Permissions added in v1.16.0

func (u User) Permissions() []*Permission

func (User) PermissionsCount added in v1.16.0

func (u User) PermissionsCount() int

func (User) Policies added in v1.16.0

func (u User) Policies() []*Permission

func (User) UserName added in v1.16.0

func (u User) UserName() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL