Skip to content

Commit

Permalink
Adds controls for impersonation requests. (gravitational#6009) (gravi…
Browse files Browse the repository at this point in the history
…tational#6073)

Fixes gravitational#5352

```yaml
allow:
  impersonate:
    users: ['alice', 'bob']
    roles: ['*']
    where: 'contains(user.spec.traits["groups"], impersonate_role.traits)'
```

Adds "impersonator" to all X.509 and SSH client certs
issued using impersonation and does best effort to track
requests by impersonators in audit events.

Limits certs TTL to the impersonator's max session TTL.

Prevents impersonating users to recursively impersonate
other users.

Allows impersonating users to renew their own certificate,
for example to set route to cluster.

Adds missing token permission for editor role.
  • Loading branch information
klizhentas authored Mar 19, 2021
1 parent 854d5fc commit f17625c
Show file tree
Hide file tree
Showing 39 changed files with 2,456 additions and 1,018 deletions.
561 changes: 304 additions & 257 deletions api/types/events/events.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions api/types/events/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ message UserMetadata {

// Login is OS login
string Login = 2 [ (gogoproto.jsontag) = "login,omitempty" ];

// Impersonator is a user acting on behalf of another user
string Impersonator = 3 [ (gogoproto.jsontag) = "impersonator,omitempty" ];
}

// Server is a server metadata
Expand Down
74 changes: 73 additions & 1 deletion api/types/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ type Role interface {
GetDatabaseUsers(RoleConditionType) []string
// SetDatabaseUsers sets a list of database users this role is allowed or denied access to.
SetDatabaseUsers(RoleConditionType, []string)

// GetImpersonateConditions returns conditions this role is allowed or denied to impersonate.
GetImpersonateConditions(rct RoleConditionType) ImpersonateConditions
// SetImpersonateConditions returns conditions this role is allowed or denied to impersonate.
SetImpersonateConditions(rct RoleConditionType, cond ImpersonateConditions)
}

// NewRole constructs new standard role
Expand Down Expand Up @@ -179,6 +184,9 @@ func (r *RoleV3) Equals(other Role) bool {
if !r.GetKubernetesLabels(condition).Equals(other.GetKubernetesLabels(condition)) {
return false
}
if !r.GetImpersonateConditions(condition).Equals(other.GetImpersonateConditions(condition)) {
return false
}
}

return true
Expand Down Expand Up @@ -472,6 +480,27 @@ func (r *RoleV3) SetDatabaseUsers(rct RoleConditionType, values []string) {
}
}

// GetImpersonateConditions returns conditions this role is allowed or denied to impersonate.
func (r *RoleV3) GetImpersonateConditions(rct RoleConditionType) ImpersonateConditions {
cond := r.Spec.Deny.Impersonate
if rct == Allow {
cond = r.Spec.Allow.Impersonate
}
if cond == nil {
return ImpersonateConditions{}
}
return *cond
}

// SetImpersonateConditions returns conditions this role is allowed or denied to impersonate.
func (r *RoleV3) SetImpersonateConditions(rct RoleConditionType, cond ImpersonateConditions) {
if rct == Allow {
r.Spec.Allow.Impersonate = &cond
} else {
r.Spec.Deny.Impersonate = &cond
}
}

// GetRules gets all allow or deny rules.
func (r *RoleV3) GetRules(rct RoleConditionType) []Rule {
if rct == Allow {
Expand Down Expand Up @@ -592,7 +621,19 @@ func (r *RoleV3) CheckAndSetDefaults() error {
return trace.BadParameter("failed to process 'deny' rule %v: %v", i, err)
}
}

if r.Spec.Allow.Impersonate != nil {
if err := r.Spec.Allow.Impersonate.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
}
if r.Spec.Deny.Impersonate != nil {
if r.Spec.Deny.Impersonate.Where != "" {
return trace.BadParameter("'where' is not supported in deny.impersonate conditions")
}
if err := r.Spec.Deny.Impersonate.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
}
return nil
}

Expand Down Expand Up @@ -651,6 +692,37 @@ func (r *RoleConditions) Equals(o RoleConditions) bool {
return true
}

// IsEmpty returns true if conditions are unspecified
func (i ImpersonateConditions) IsEmpty() bool {
return len(i.Users) == 0 || len(i.Roles) == 0
}

// Equals returns true if the impersonate conditions (logins, roles
// and rules) are equal and false if they are not.
func (i ImpersonateConditions) Equals(o ImpersonateConditions) bool {
if !utils.StringSlicesEqual(i.Users, o.Users) {
return false
}
if !utils.StringSlicesEqual(i.Roles, o.Roles) {
return false
}
if i.Where != o.Where {
return false
}
return true
}

// CheckAndSetDefaults checks and sets default values
func (i ImpersonateConditions) CheckAndSetDefaults() error {
if len(i.Users) != 0 && len(i.Roles) == 0 {
return trace.BadParameter("please set both impersonate.users and impersonate.roles")
}
if len(i.Users) == 0 && len(i.Roles) != 0 {
return trace.BadParameter("please set both impersonate.users and impersonate.roles")
}
return nil
}

// NewRule creates a rule based on a resource name and a list of verbs
func NewRule(resource string, verbs []string) Rule {
return Rule{
Expand Down
Loading

0 comments on commit f17625c

Please sign in to comment.