Skip to content
New issue

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

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

Already on GitHub? # to your account

GCP CLI support: RBAC changes #19786

Merged
merged 4 commits into from
Jan 11, 2023
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
4 changes: 4 additions & 0 deletions api/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ const (
// TraitAzureIdentities is the name of the role variable used to store
// allowed Azure identity names.
TraitAzureIdentities = "azure_identities"

// TraitGCPServiceAccounts is the name of the role variable used to store
// allowed GCP service accounts.
TraitGCPServiceAccounts = "gcp_service_accounts"
)

// Constants for AWS discovery
Expand Down
27 changes: 27 additions & 0 deletions api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ type Role interface {
// SetAzureIdentities sets a list of Azure identities this role is allowed to assume.
SetAzureIdentities(RoleConditionType, []string)

// GetGCPServiceAccounts returns a list of GCP service accounts this role is allowed to assume.
GetGCPServiceAccounts(RoleConditionType) []string
// SetGCPServiceAccounts sets a list of GCP service accounts this role is allowed to assume.
SetGCPServiceAccounts(RoleConditionType, []string)

// GetWindowsDesktopLabels gets the Windows desktop labels this role
// is allowed or denied access to.
GetWindowsDesktopLabels(RoleConditionType) Labels
Expand Down Expand Up @@ -629,6 +634,23 @@ func (r *RoleV6) SetAzureIdentities(rct RoleConditionType, identities []string)
}
}

// GetGCPServiceAccounts returns a list of GCP service accounts this role is allowed to assume.
func (r *RoleV6) GetGCPServiceAccounts(rct RoleConditionType) []string {
if rct == Allow {
return r.Spec.Allow.GCPServiceAccounts
}
return r.Spec.Deny.GCPServiceAccounts
}

// SetGCPServiceAccounts sets a list of GCP service accounts this role is allowed to assume.
func (r *RoleV6) SetGCPServiceAccounts(rct RoleConditionType, accounts []string) {
if rct == Allow {
r.Spec.Allow.GCPServiceAccounts = accounts
} else {
r.Spec.Deny.GCPServiceAccounts = accounts
}
}

// GetWindowsDesktopLabels gets the desktop labels this role is allowed or denied access to.
func (r *RoleV6) GetWindowsDesktopLabels(rct RoleConditionType) Labels {
if rct == Allow {
Expand Down Expand Up @@ -890,6 +912,11 @@ func (r *RoleV6) CheckAndSetDefaults() error {
return trace.BadParameter("wildcard matcher is not allowed in allow.azure_identities")
}
}
for _, identity := range r.Spec.Allow.GCPServiceAccounts {
if identity == Wildcard {
return trace.BadParameter("wildcard matcher is not allowed in allow.gcp_service_accounts")
}
}
checkWildcardSelector := func(labels Labels) error {
for key, val := range labels {
if key == Wildcard && !(len(val) == 1 && val[0] == Wildcard) {
Expand Down
22 changes: 18 additions & 4 deletions api/types/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type User interface {
GetAWSRoleARNs() []string
// GetAzureIdentities gets a list of Azure identities for the user
GetAzureIdentities() []string
// GetGCPServiceAccounts gets a list of GCP service accounts for the user
GetGCPServiceAccounts() []string
// String returns user
String() string
// GetStatus return user login status
Expand Down Expand Up @@ -89,14 +91,16 @@ type User interface {
// SetAWSRoleARNs sets a list of AWS role ARNs for user
SetAWSRoleARNs(awsRoleARNs []string)
// SetAzureIdentities sets a list of Azure identities for the user
SetAzureIdentities(AzureIdentities []string)
SetAzureIdentities(azureIdentities []string)
// SetGCPServiceAccounts sets a list of GCP service accounts for the user
SetGCPServiceAccounts(accounts []string)
// GetCreatedBy returns information about user
GetCreatedBy() CreatedBy
// SetCreatedBy sets created by information
SetCreatedBy(CreatedBy)
// GetTraits gets the trait map for this user used to populate role variables.
GetTraits() map[string][]string
// GetTraits sets the trait map for this user used to populate role variables.
// SetTraits sets the trait map for this user used to populate role variables.
SetTraits(map[string][]string)
}

Expand Down Expand Up @@ -283,8 +287,13 @@ func (u *UserV2) SetAWSRoleARNs(awsRoleARNs []string) {
}

// SetAzureIdentities sets a list of Azure identities for the user
func (u *UserV2) SetAzureIdentities(AzureIdentities []string) {
u.setTrait(constants.TraitAzureIdentities, AzureIdentities)
func (u *UserV2) SetAzureIdentities(identities []string) {
u.setTrait(constants.TraitAzureIdentities, identities)
}

// SetGCPServiceAccounts sets a list of GCP service accounts for the user
func (u *UserV2) SetGCPServiceAccounts(accounts []string) {
u.setTrait(constants.TraitGCPServiceAccounts, accounts)
}

// GetStatus returns login status of the user
Expand Down Expand Up @@ -379,6 +388,11 @@ func (u UserV2) GetAzureIdentities() []string {
return u.getTrait(constants.TraitAzureIdentities)
}

// GetGCPServiceAccounts gets a list of GCP service accounts for the user
func (u UserV2) GetGCPServiceAccounts() []string {
return u.getTrait(constants.TraitGCPServiceAccounts)
}

func (u *UserV2) String() string {
return fmt.Sprintf("User(name=%v, roles=%v, identities=%v)", u.Metadata.Name, u.Spec.Roles, u.Spec.OIDCIdentities)
}
Expand Down
4 changes: 4 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,10 @@ const (
// Azure identities for local accounts.
TraitInternalAzureIdentities = "{{internal.azure_identities}}"

// TraitInternalGCPServiceAccounts is the variable used to store allowed
// GCP service accounts for local accounts.
TraitInternalGCPServiceAccounts = "{{internal.gcp_service_accounts}}"

// TraitInternalJWTVariable is the variable used to store JWT token for
// app sessions.
TraitInternalJWTVariable = "{{internal.jwt}}"
Expand Down
3 changes: 3 additions & 0 deletions lib/services/access_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ type AccessChecker interface {
// CheckAzureIdentities returns a list of Azure identities the user is allowed to assume.
CheckAzureIdentities(ttl time.Duration, overrideTTL bool) ([]string, error)

// CheckGCPServiceAccounts returns a list of GCP service accounts the user is allowed to assume.
CheckGCPServiceAccounts(ttl time.Duration, overrideTTL bool) ([]string, error)

// AdjustSessionTTL will reduce the requested ttl to lowest max allowed TTL
// for this role set, otherwise it returns ttl unchanged
AdjustSessionTTL(ttl time.Duration) time.Duration
Expand Down
1 change: 1 addition & 0 deletions lib/services/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func NewPresetAccessRole() types.Role {
role.SetKubeGroups(types.Allow, []string{teleport.TraitInternalKubeGroupsVariable})
role.SetAWSRoleARNs(types.Allow, []string{teleport.TraitInternalAWSRoleARNs})
role.SetAzureIdentities(types.Allow, []string{teleport.TraitInternalAzureIdentities})
role.SetGCPServiceAccounts(types.Allow, []string{teleport.TraitInternalGCPServiceAccounts})
return role
}

Expand Down
69 changes: 68 additions & 1 deletion lib/services/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ func ApplyTraits(r types.Role, traits map[string][]string) types.Role {
warnInvalidAzureIdentities(outAzureIdentities)
r.SetAzureIdentities(condition, apiutils.Deduplicate(outAzureIdentities))

inGCPAccounts := r.GetGCPServiceAccounts(condition)
outGCPAccounts := applyValueTraitsSlice(inGCPAccounts, traits, "GCP service account")
r.SetGCPServiceAccounts(condition, apiutils.Deduplicate(outGCPAccounts))

// apply templates to kubernetes groups
inKubeGroups := r.GetKubeGroups(condition)
outKubeGroups := applyValueTraitsSlice(inKubeGroups, traits, "kube group")
Expand Down Expand Up @@ -498,7 +502,7 @@ func ApplyValueTraits(val string, traits map[string][]string) ([]string, error)
constants.TraitKubeGroups, constants.TraitKubeUsers,
constants.TraitDBNames, constants.TraitDBUsers,
constants.TraitAWSRoleARNs, constants.TraitAzureIdentities,
teleport.TraitJWT:
constants.TraitGCPServiceAccounts, teleport.TraitJWT:
default:
return nil, trace.BadParameter("unsupported variable %q", variable.Name())
}
Expand Down Expand Up @@ -1034,6 +1038,19 @@ func MatchAzureIdentity(selectors []string, identity string, matchWildcard bool)
return false, fmt.Sprintf("no match, role selectors %v, identity: %v", selectors, identity)
}

// MatchGCPServiceAccount returns true if provided GCP service account matches selectors.
func MatchGCPServiceAccount(selectors []string, account string, matchWildcard bool) (bool, string) {
for _, l := range selectors {
if l == account {
return true, "element matched"
}
if matchWildcard && l == types.Wildcard {
return true, "wildcard matched"
}
}
return false, fmt.Sprintf("no match, role selectors %v, identity: %v", selectors, account)
}

// MatchDatabaseName returns true if provided database name matches selectors.
func MatchDatabaseName(selectors []string, name string) (bool, string) {
for _, n := range selectors {
Expand Down Expand Up @@ -1447,6 +1464,38 @@ func (set RoleSet) CheckAzureIdentities(ttl time.Duration, overrideTTL bool) ([]
return out, nil
}

// CheckGCPServiceAccounts returns a list of GCP service accounts this role set is allowed to assume.
func (set RoleSet) CheckGCPServiceAccounts(ttl time.Duration, overrideTTL bool) ([]string, error) {
accounts := make(map[string]struct{})
var matchedTTL bool
for _, role := range set {
maxSessionTTL := role.GetOptions().MaxSessionTTL.Value()
if overrideTTL || (ttl <= maxSessionTTL && maxSessionTTL != 0) {
matchedTTL = true
for _, account := range role.GetGCPServiceAccounts(types.Allow) {
accounts[strings.ToLower(account)] = struct{}{}
}
}
}
for _, role := range set {
for _, account := range role.GetGCPServiceAccounts(types.Deny) {
// deny * removes all accounts
if account == types.Wildcard {
accounts = make(map[string]struct{})
}
// remove particular account
delete(accounts, strings.ToLower(account))
}
}
if !matchedTTL {
return nil, trace.AccessDenied("this user cannot request GCP API access for %v", ttl)
}
if len(accounts) == 0 {
return nil, trace.NotFound("this user cannot request GCP API access, has no assigned service accounts")
}
return utils.StringsSliceFromSet(accounts), nil
}

// CheckLoginDuration checks if role set can login up to given duration and
// returns a combined list of allowed logins.
func (set RoleSet) CheckLoginDuration(ttl time.Duration) ([]string, error) {
Expand Down Expand Up @@ -1604,6 +1653,24 @@ func (m *AzureIdentityMatcher) String() string {
return fmt.Sprintf("AzureIdentityMatcher(Identity=%v)", m.Identity)
}

// GCPServiceAccountMatcher matches a role against GCP service account.
type GCPServiceAccountMatcher struct {
// ServiceAccount is a GCP service account to match, e.g. teleport@example-123456.iam.gserviceaccount.com.
// It can also be a wildcard *, but that is only respected for Deny rules.
ServiceAccount string
}

// Match matches GCP ServiceAccount against provided role and condition.
func (m *GCPServiceAccountMatcher) Match(role types.Role, condition types.RoleConditionType) (bool, error) {
match, _ := MatchGCPServiceAccount(role.GetGCPServiceAccounts(condition), m.ServiceAccount, condition == types.Deny)
return match, nil
}

// String returns the matcher's string representation.
func (m *GCPServiceAccountMatcher) String() string {
return fmt.Sprintf("GCPServiceAccountMatcher(ServiceAccount=%v)", m.ServiceAccount)
}

// CanImpersonateSomeone returns true if this checker has any impersonation rules
func (set RoleSet) CanImpersonateSomeone() bool {
for _, role := range set {
Expand Down
Loading