From 74b40d0ceca844467e9f1b0be12105c4ee67f530 Mon Sep 17 00:00:00 2001 From: Roman Tkachenko Date: Wed, 20 Apr 2022 13:50:07 -0700 Subject: [PATCH] Allow setting additional traits in tctl users add command (#12102) --- constants.go | 8 ++++++++ lib/services/presets.go | 1 + lib/services/role.go | 15 +++------------ lib/services/role_test.go | 15 +++++++++------ tool/tctl/common/user_command.go | 20 ++++++++++++++++---- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/constants.go b/constants.go index 559dbfa4b1fe9..dff78d33681d8 100644 --- a/constants.go +++ b/constants.go @@ -527,6 +527,10 @@ const ( // allowed database users. TraitDBUsers = "db_users" + // TraitAWSRoleARNs is the name of the role variable used to store + // allowed AWS role ARNs. + TraitAWSRoleARNs = "aws_role_arns" + // TraitTeams is the name of the role variable use to store team // membership information TraitTeams = "github_teams" @@ -554,6 +558,10 @@ const ( // TraitInternalDBUsersVariable is the variable used to store allowed // database users for local accounts. TraitInternalDBUsersVariable = "{{internal.db_users}}" + + // TraitInternalAWSRoleARNs is the variable used to store allowed AWS + // role ARNs for local accounts. + TraitInternalAWSRoleARNs = "{{internal.aws_role_arns}}" ) // SCP is Secure Copy. diff --git a/lib/services/presets.go b/lib/services/presets.go index d2608ac06e959..aee00809ff66e 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -116,6 +116,7 @@ func NewPresetAccessRole() types.Role { role.SetWindowsLogins(types.Allow, []string{teleport.TraitInternalWindowsLoginsVariable}) role.SetKubeUsers(types.Allow, []string{teleport.TraitInternalKubeUsersVariable}) role.SetKubeGroups(types.Allow, []string{teleport.TraitInternalKubeGroupsVariable}) + role.SetAWSRoleARNs(types.Allow, []string{teleport.TraitInternalAWSRoleARNs}) return role } diff --git a/lib/services/role.go b/lib/services/role.go index 8cef7c276fb3a..c70325e525d40 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -292,17 +292,7 @@ func ApplyTraits(r types.Role, traits map[string][]string) types.Role { r.SetWindowsLogins(condition, apiutils.Deduplicate(outWindowsLogins)) inRoleARNs := r.GetAWSRoleARNs(condition) - var outRoleARNs []string - for _, arn := range inRoleARNs { - variableValues, err := ApplyValueTraits(arn, traits) - if err != nil { - if !trace.IsNotFound(err) { - log.Debugf("Skipping AWS role ARN %v: %v.", arn, err) - } - continue - } - outRoleARNs = append(outRoleARNs, variableValues...) - } + outRoleARNs := applyValueTraitsSlice(inRoleARNs, traits, "AWS role ARN") r.SetAWSRoleARNs(condition, apiutils.Deduplicate(outRoleARNs)) // apply templates to kubernetes groups @@ -454,7 +444,8 @@ func ApplyValueTraits(val string, traits map[string][]string) ([]string, error) switch variable.Name() { case teleport.TraitLogins, teleport.TraitWindowsLogins, teleport.TraitKubeGroups, teleport.TraitKubeUsers, - teleport.TraitDBNames, teleport.TraitDBUsers: + teleport.TraitDBNames, teleport.TraitDBUsers, + teleport.TraitAWSRoleARNs: default: return nil, trace.BadParameter("unsupported variable %q", variable.Name()) } diff --git a/lib/services/role_test.go b/lib/services/role_test.go index 93d4aef07960d..4bbefcec653cf 100644 --- a/lib/services/role_test.go +++ b/lib/services/role_test.go @@ -24,6 +24,7 @@ import ( "testing" "time" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" @@ -1897,21 +1898,23 @@ func TestApplyTraits(t *testing.T) { { comment: "AWS role ARN substitute in allow rule", inTraits: map[string][]string{ - "foo": {"bar"}, + "foo": {"bar"}, + teleport.TraitAWSRoleARNs: {"baz"}, }, allow: rule{ - inRoleARNs: []string{"{{external.foo}}"}, - outRoleARNs: []string{"bar"}, + inRoleARNs: []string{"{{external.foo}}", teleport.TraitInternalAWSRoleARNs}, + outRoleARNs: []string{"bar", "baz"}, }, }, { comment: "AWS role ARN substitute in deny rule", inTraits: map[string][]string{ - "foo": {"bar"}, + "foo": {"bar"}, + teleport.TraitAWSRoleARNs: {"baz"}, }, deny: rule{ - inRoleARNs: []string{"{{external.foo}}"}, - outRoleARNs: []string{"bar"}, + inRoleARNs: []string{"{{external.foo}}", teleport.TraitInternalAWSRoleARNs}, + outRoleARNs: []string{"bar", "baz"}, }, }, { diff --git a/tool/tctl/common/user_command.go b/tool/tctl/common/user_command.go index c6ad02bf7a08f..024022da6aa50 100644 --- a/tool/tctl/common/user_command.go +++ b/tool/tctl/common/user_command.go @@ -41,9 +41,12 @@ type UserCommand struct { login string allowedLogins []string allowedWindowsLogins []string + allowedKubeUsers []string + allowedKubeGroups []string + allowedDatabaseUsers []string + allowedDatabaseNames []string + allowedAWSRoleARNs []string createRoles []string - kubeUsers string - kubeGroups string ttl time.Duration @@ -72,6 +75,12 @@ func (u *UserCommand) Initialize(app *kingpin.Application, config *service.Confi u.userAdd.Flag("logins", "List of allowed SSH logins for the new user").StringsVar(&u.allowedLogins) u.userAdd.Flag("windows-logins", "List of allowed Windows logins for the new user").StringsVar(&u.allowedWindowsLogins) + u.userAdd.Flag("kubernetes-users", "List of allowed Kubernetes users for the new user").StringsVar(&u.allowedKubeUsers) + u.userAdd.Flag("kubernetes-groups", "List of allowed Kubernetes groups for the new user").StringsVar(&u.allowedKubeGroups) + u.userAdd.Flag("db-users", "List of allowed database users for the new user").StringsVar(&u.allowedDatabaseUsers) + u.userAdd.Flag("db-names", "List of allowed database names for the new user").StringsVar(&u.allowedDatabaseNames) + u.userAdd.Flag("aws-role-arns", "List of allowed AWS role ARNs for the new user").StringsVar(&u.allowedAWSRoleARNs) + u.userAdd.Flag("roles", "List of roles for the new user to assume").Required().StringsVar(&u.createRoles) u.userAdd.Flag("ttl", fmt.Sprintf("Set expiration time for token, default is %v, maximum is %v", @@ -200,8 +209,11 @@ func (u *UserCommand) Add(client auth.ClientI) error { traits := map[string][]string{ teleport.TraitLogins: u.allowedLogins, teleport.TraitWindowsLogins: u.allowedWindowsLogins, - teleport.TraitKubeUsers: flattenSlice([]string{u.kubeUsers}), - teleport.TraitKubeGroups: flattenSlice([]string{u.kubeGroups}), + teleport.TraitKubeUsers: flattenSlice(u.allowedKubeUsers), + teleport.TraitKubeGroups: flattenSlice(u.allowedKubeGroups), + teleport.TraitDBUsers: flattenSlice(u.allowedDatabaseUsers), + teleport.TraitDBNames: flattenSlice(u.allowedDatabaseNames), + teleport.TraitAWSRoleARNs: flattenSlice(u.allowedAWSRoleARNs), } user, err := types.NewUser(u.login)