Skip to content

Commit

Permalink
Adds controls for impersonation requests.
Browse files Browse the repository at this point in the history
Fixes #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 cert 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 committed Mar 19, 2021
1 parent d4b59fc commit 5085237
Show file tree
Hide file tree
Showing 40 changed files with 2,563 additions and 1,141 deletions.
652 changes: 347 additions & 305 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 @@ -44,6 +44,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 5085237

Please sign in to comment.