mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 16:53:57 +00:00
refactor internal rule representation
This commit is contained in:
parent
20a859aa28
commit
0c77c5c0e9
|
@ -164,9 +164,9 @@ func createUserAndRoleWithoutRoles(clt clt, username string, allowedLogins []str
|
|||
}
|
||||
|
||||
role := services.RoleForUser(user)
|
||||
rules := role.GetRules(services.Allow)
|
||||
delete(rules, services.KindRole)
|
||||
role.SetRules(services.Allow, rules)
|
||||
set := services.MakeRuleSet(role.GetRules(services.Allow))
|
||||
delete(set, services.KindRole)
|
||||
role.SetRules(services.Allow, set.Slice())
|
||||
role.SetLogins(services.Allow, []string{user.GetName()})
|
||||
err = clt.UpsertRole(role, backend.Forever)
|
||||
if err != nil {
|
||||
|
|
|
@ -152,8 +152,8 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.KindAuthServer: services.RW(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindAuthServer, services.RW()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -165,17 +165,17 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.KindNode: services.RW(),
|
||||
services.KindSession: services.RW(),
|
||||
services.KindEvent: services.RW(),
|
||||
services.KindProxy: services.RO(),
|
||||
services.KindCertAuthority: services.RO(),
|
||||
services.KindUser: services.RO(),
|
||||
services.KindNamespace: services.RO(),
|
||||
services.KindRole: services.RO(),
|
||||
services.KindAuthServer: services.RO(),
|
||||
services.KindReverseTunnel: services.RO(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindNode, services.RW()),
|
||||
services.NewRule(services.KindSession, services.RW()),
|
||||
services.NewRule(services.KindEvent, services.RW()),
|
||||
services.NewRule(services.KindProxy, services.RO()),
|
||||
services.NewRule(services.KindCertAuthority, services.RO()),
|
||||
services.NewRule(services.KindUser, services.RO()),
|
||||
services.NewRule(services.KindNamespace, services.RO()),
|
||||
services.NewRule(services.KindRole, services.RO()),
|
||||
services.NewRule(services.KindAuthServer, services.RO()),
|
||||
services.NewRule(services.KindReverseTunnel, services.RO()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -185,24 +185,24 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.KindProxy: services.RW(),
|
||||
services.KindOIDCRequest: services.RW(),
|
||||
services.KindSession: services.RW(),
|
||||
services.KindEvent: services.RW(),
|
||||
services.KindSAMLRequest: services.RW(),
|
||||
services.KindOIDC: services.RO(),
|
||||
services.KindSAML: services.RO(),
|
||||
services.KindNamespace: services.RO(),
|
||||
services.KindNode: services.RO(),
|
||||
services.KindAuthServer: services.RO(),
|
||||
services.KindReverseTunnel: services.RO(),
|
||||
services.KindCertAuthority: services.RO(),
|
||||
services.KindUser: services.RO(),
|
||||
services.KindRole: services.RO(),
|
||||
services.KindClusterAuthPreference: services.RO(),
|
||||
services.KindClusterName: services.RO(),
|
||||
services.KindStaticTokens: services.RO(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindProxy, services.RW()),
|
||||
services.NewRule(services.KindOIDCRequest, services.RW()),
|
||||
services.NewRule(services.KindSession, services.RW()),
|
||||
services.NewRule(services.KindEvent, services.RW()),
|
||||
services.NewRule(services.KindSAMLRequest, services.RW()),
|
||||
services.NewRule(services.KindOIDC, services.RO()),
|
||||
services.NewRule(services.KindSAML, services.RO()),
|
||||
services.NewRule(services.KindNamespace, services.RO()),
|
||||
services.NewRule(services.KindNode, services.RO()),
|
||||
services.NewRule(services.KindAuthServer, services.RO()),
|
||||
services.NewRule(services.KindReverseTunnel, services.RO()),
|
||||
services.NewRule(services.KindCertAuthority, services.RO()),
|
||||
services.NewRule(services.KindUser, services.RO()),
|
||||
services.NewRule(services.KindRole, services.RO()),
|
||||
services.NewRule(services.KindClusterAuthPreference, services.RO()),
|
||||
services.NewRule(services.KindClusterName, services.RO()),
|
||||
services.NewRule(services.KindStaticTokens, services.RO()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -212,14 +212,14 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.KindWebSession: services.RW(),
|
||||
services.KindSession: services.RW(),
|
||||
services.KindAuthServer: services.RO(),
|
||||
services.KindUser: services.RO(),
|
||||
services.KindRole: services.RO(),
|
||||
services.KindNamespace: services.RO(),
|
||||
services.KindTrustedCluster: services.RO(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindWebSession, services.RW()),
|
||||
services.NewRule(services.KindSession, services.RW()),
|
||||
services.NewRule(services.KindAuthServer, services.RO()),
|
||||
services.NewRule(services.KindUser, services.RO()),
|
||||
services.NewRule(services.KindRole, services.RO()),
|
||||
services.NewRule(services.KindNamespace, services.RO()),
|
||||
services.NewRule(services.KindTrustedCluster, services.RO()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -229,9 +229,9 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.KindAuthServer: services.RO(),
|
||||
services.KindClusterAuthPreference: services.RO(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindAuthServer, services.RO()),
|
||||
services.NewRule(services.KindClusterAuthPreference, services.RO()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -246,8 +246,8 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
Namespaces: []string{services.Wildcard},
|
||||
Logins: []string{},
|
||||
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
|
||||
Rules: map[string][]string{
|
||||
services.Wildcard: services.RW(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.Wildcard, services.RW()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -257,7 +257,7 @@ func GetCheckerForBuiltinRole(role teleport.Role) (services.AccessChecker, error
|
|||
services.RoleSpecV3{
|
||||
Allow: services.RoleConditions{
|
||||
Namespaces: []string{},
|
||||
Rules: map[string][]string{},
|
||||
Rules: []services.Rule{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ func (s *TunSuite) TestUnixServerClient(c *C) {
|
|||
|
||||
user, role := createUserAndRole(s.a, userName, []string{userName})
|
||||
rules := role.GetRules(services.Allow)
|
||||
rules[services.KindNode] = services.RW()
|
||||
rules = append(rules, services.NewRule(services.KindNode, services.RW()))
|
||||
role.SetRules(services.Allow, rules)
|
||||
err = s.a.UpsertRole(role, backend.Forever)
|
||||
c.Assert(err, IsNil)
|
||||
|
|
|
@ -45,6 +45,7 @@ import (
|
|||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/session"
|
||||
"github.com/gravitational/teleport/lib/shell"
|
||||
"github.com/gravitational/teleport/lib/state"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
|
@ -1364,7 +1365,7 @@ func runLocalCommand(command []string) error {
|
|||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
shell, err := utils.GetLoginShell(user.Username)
|
||||
shell, err := shell.GetLoginShell(user.Username)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -280,8 +280,8 @@ func (s *MigrationsSuite) TestMigrateRoles(c *C) {
|
|||
Logins: []string{"foo"},
|
||||
NodeLabels: map[string]string{"a": "b"},
|
||||
Namespaces: []string{"system", "default"},
|
||||
Rules: map[string][]string{
|
||||
KindRole: RW(),
|
||||
Rules: []Rule{
|
||||
NewRule(KindRole, RW()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -36,31 +36,31 @@ import (
|
|||
|
||||
// DefaultUserRules provides access to the default set of rules assigned to
|
||||
// all users.
|
||||
var DefaultUserRules = map[string][]string{
|
||||
KindRole: RO(),
|
||||
KindOIDC: RO(),
|
||||
KindSAML: RO(),
|
||||
KindSession: RO(),
|
||||
KindTrustedCluster: RW(),
|
||||
var DefaultUserRules = []Rule{
|
||||
NewRule(KindRole, RO()),
|
||||
NewRule(KindOIDC, RO()),
|
||||
NewRule(KindSAML, RO()),
|
||||
NewRule(KindSession, RO()),
|
||||
NewRule(KindTrustedCluster, RW()),
|
||||
}
|
||||
|
||||
// DefaultImplicitRules provides access to the default set of implicit rules
|
||||
// assigned to all roles.
|
||||
var DefaultImplicitRules = map[string][]string{
|
||||
KindNode: RO(),
|
||||
KindAuthServer: RO(),
|
||||
KindReverseTunnel: RO(),
|
||||
KindCertAuthority: RO(),
|
||||
var DefaultImplicitRules = []Rule{
|
||||
NewRule(KindNode, RO()),
|
||||
NewRule(KindAuthServer, RO()),
|
||||
NewRule(KindReverseTunnel, RO()),
|
||||
NewRule(KindCertAuthority, RO()),
|
||||
}
|
||||
|
||||
// DefaultCertAuthorityRules provides access the minimal set of resources
|
||||
// needed for a certificate authority to function.
|
||||
var DefaultCertAuthorityRules = map[string][]string{
|
||||
KindSession: RO(),
|
||||
KindNode: RO(),
|
||||
KindAuthServer: RO(),
|
||||
KindReverseTunnel: RO(),
|
||||
KindCertAuthority: RO(),
|
||||
var DefaultCertAuthorityRules = []Rule{
|
||||
NewRule(KindSession, RO()),
|
||||
NewRule(KindNode, RO()),
|
||||
NewRule(KindAuthServer, RO()),
|
||||
NewRule(KindReverseTunnel, RO()),
|
||||
NewRule(KindCertAuthority, RO()),
|
||||
}
|
||||
|
||||
// RoleNameForUser returns role name associated with a user.
|
||||
|
@ -92,7 +92,7 @@ func NewDefaultRole() Role {
|
|||
Namespaces: []string{defaults.Namespace},
|
||||
Logins: []string{teleport.TraitInternalRoleVariable},
|
||||
NodeLabels: map[string]string{Wildcard: Wildcard},
|
||||
Rules: utils.CopyStringMapSlices(DefaultUserRules),
|
||||
Rules: CopyRulesSlice(DefaultUserRules),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func NewImplicitRole() Role {
|
|||
Namespaces: []string{defaults.Namespace},
|
||||
Logins: []string{teleport.TraitInternalRoleVariable},
|
||||
NodeLabels: map[string]string{Wildcard: Wildcard},
|
||||
Rules: utils.CopyStringMapSlices(DefaultImplicitRules),
|
||||
Rules: CopyRulesSlice(DefaultImplicitRules),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func RoleForUser(u User) Role {
|
|||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
NodeLabels: map[string]string{Wildcard: Wildcard},
|
||||
Rules: utils.CopyStringMapSlices(DefaultUserRules),
|
||||
Rules: CopyRulesSlice(DefaultUserRules),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ func RoleForCertAuthority(ca CertAuthority) Role {
|
|||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
NodeLabels: map[string]string{Wildcard: Wildcard},
|
||||
Rules: utils.CopyStringMapSlices(DefaultCertAuthorityRules),
|
||||
Rules: CopyRulesSlice(DefaultCertAuthorityRules),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -248,9 +248,9 @@ type Role interface {
|
|||
SetNodeLabels(RoleConditionType, map[string]string)
|
||||
|
||||
// GetRules gets all allow or deny rules.
|
||||
GetRules(rct RoleConditionType) map[string][]string
|
||||
GetRules(rct RoleConditionType) []Rule
|
||||
// SetRules sets an allow or deny rule.
|
||||
SetRules(rct RoleConditionType, rrs map[string][]string)
|
||||
SetRules(rct RoleConditionType, rules []Rule)
|
||||
}
|
||||
|
||||
// RoleV3 represents role resource specification
|
||||
|
@ -285,8 +285,7 @@ func (r *RoleV3) Equals(other Role) bool {
|
|||
if !utils.StringMapsEqual(r.GetNodeLabels(condition), other.GetNodeLabels(condition)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !utils.StringMapSlicesEqual(r.GetRules(condition), other.GetRules(condition)) {
|
||||
if !RuleSlicesEqual(r.GetRules(condition), other.GetRules(condition)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -439,7 +438,7 @@ func (r *RoleV3) SetNodeLabels(rct RoleConditionType, labels map[string]string)
|
|||
}
|
||||
|
||||
// GetRules gets all allow or deny rules.
|
||||
func (r *RoleV3) GetRules(rct RoleConditionType) map[string][]string {
|
||||
func (r *RoleV3) GetRules(rct RoleConditionType) []Rule {
|
||||
if rct == Allow {
|
||||
return r.Spec.Allow.Rules
|
||||
}
|
||||
|
@ -447,8 +446,8 @@ func (r *RoleV3) GetRules(rct RoleConditionType) map[string][]string {
|
|||
}
|
||||
|
||||
// SetRules sets an allow or deny rule.
|
||||
func (r *RoleV3) SetRules(rct RoleConditionType, rrs map[string][]string) {
|
||||
rcopy := utils.CopyStringMapSlices(rrs)
|
||||
func (r *RoleV3) SetRules(rct RoleConditionType, in []Rule) {
|
||||
rcopy := CopyRulesSlice(in)
|
||||
|
||||
if rct == Allow {
|
||||
r.Spec.Allow.Rules = rcopy
|
||||
|
@ -478,7 +477,7 @@ func (r *RoleV3) CheckAndSetDefaults() error {
|
|||
r.Spec.Allow.NodeLabels = map[string]string{Wildcard: Wildcard}
|
||||
}
|
||||
if r.Spec.Allow.Rules == nil {
|
||||
r.Spec.Allow.Rules = utils.CopyStringMapSlices(DefaultUserRules)
|
||||
r.Spec.Allow.Rules = CopyRulesSlice(DefaultUserRules)
|
||||
}
|
||||
|
||||
// if we find {{ or }} but the syntax is invalid, the role is invalid
|
||||
|
@ -647,7 +646,7 @@ type RoleConditions struct {
|
|||
|
||||
// Rules is a list of rules and their access levels. Rules are a high level
|
||||
// construct used for access control.
|
||||
Rules RoleRules `json:"rules,omitempty" yaml:"rules,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty" yaml:"rules,omitempty"`
|
||||
}
|
||||
|
||||
// Equals returns true if the role conditions (logins, namespaces, labels,
|
||||
|
@ -662,51 +661,134 @@ func (r *RoleConditions) Equals(o RoleConditions) bool {
|
|||
if !utils.StringMapsEqual(r.NodeLabels, o.NodeLabels) {
|
||||
return false
|
||||
}
|
||||
if !utils.StringMapSlicesEqual(r.Rules, o.Rules) {
|
||||
if len(r.Rules) != len(o.Rules) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range r.Rules {
|
||||
if !r.Rules[i].Equals(o.Rules[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RoleRules is a map of resources and their verbs. Role rules can be used
|
||||
// to allow or deny access to resources.
|
||||
type RoleRules map[string][]string
|
||||
// NewRule creates a rule based on a resource name and a list of verbs
|
||||
func NewRule(resource string, verbs []string) Rule {
|
||||
return Rule{
|
||||
Resources: []string{resource},
|
||||
Verbs: verbs,
|
||||
}
|
||||
}
|
||||
|
||||
type rules struct {
|
||||
// Rule represents allow or deny rule that is executed to check
|
||||
// if user or service have access to resource
|
||||
type Rule struct {
|
||||
// Resources is a list of
|
||||
Resources []string `json:"resources"`
|
||||
Verbs []string `json:"verbs"`
|
||||
Where string `json:"where,omitempty"`
|
||||
Actions []string `json:"actions,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON is used to convert between the internal representation of
|
||||
// rules and the format defined in RoleSpecV3.
|
||||
func (rrs *RoleRules) MarshalJSON() ([]byte, error) {
|
||||
var r []rules
|
||||
for resource, verbs := range *rrs {
|
||||
r = append(r, rules{Resources: []string{resource}, Verbs: verbs})
|
||||
// HasVerb returns true if the rule has verb,
|
||||
// this method also matches wildcard
|
||||
func (r *Rule) HasVerb(verb string) bool {
|
||||
for _, v := range r.Verbs {
|
||||
if v == verb {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(r)
|
||||
return false
|
||||
}
|
||||
|
||||
// UnmarshalJSON is used to convert between the internal representation of
|
||||
// rules and the format defined in RoleSpecV3.
|
||||
func (rrs *RoleRules) UnmarshalJSON(data []byte) error {
|
||||
var r []rules
|
||||
err := json.Unmarshal(data, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
// Equals returns true if the rule equals to another
|
||||
func (r *Rule) Equals(other Rule) bool {
|
||||
if !utils.StringSlicesEqual(r.Resources, other.Resources) {
|
||||
return false
|
||||
}
|
||||
if !utils.StringSlicesEqual(r.Verbs, other.Verbs) {
|
||||
return false
|
||||
}
|
||||
if !utils.StringSlicesEqual(r.Actions, other.Actions) {
|
||||
return false
|
||||
}
|
||||
if r.Where != other.Where {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RuleSet maps resource to a set of rules defined for it
|
||||
type RuleSet map[string][]Rule
|
||||
|
||||
// MatchRule tests if the resource name and verb are in a given list of rules.
|
||||
func (set RuleSet) Match(resource string, verb string) bool {
|
||||
// empty set matches nothing
|
||||
if len(set) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
rmap := make(map[string][]string)
|
||||
for _, rule := range r {
|
||||
for _, resource := range rule.Resources {
|
||||
rmap[resource] = rule.Verbs
|
||||
// check for wildcard resource matcher
|
||||
for _, rule := range set[Wildcard] {
|
||||
if rule.HasVerb(Wildcard) || rule.HasVerb(verb) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
*rrs = rmap
|
||||
return nil
|
||||
// check for matching resource by name
|
||||
for _, rule := range set[resource] {
|
||||
if rule.HasVerb(Wildcard) || rule.HasVerb(verb) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Slice returns slice from a set
|
||||
func (set RuleSet) Slice() []Rule {
|
||||
var out []Rule
|
||||
for _, rules := range set {
|
||||
out = append(out, rules...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// MakeRuleSet converts slice of rules to the set of rules
|
||||
func MakeRuleSet(rules []Rule) RuleSet {
|
||||
set := make(RuleSet)
|
||||
for _, rule := range rules {
|
||||
for _, resource := range rule.Resources {
|
||||
rules, ok := set[resource]
|
||||
if !ok {
|
||||
set[resource] = []Rule{rule}
|
||||
} else {
|
||||
rules = append(rules, rule)
|
||||
set[resource] = rules
|
||||
}
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// CopyRulesSlice copies input slice of Rules and returns the copy
|
||||
func CopyRulesSlice(in []Rule) []Rule {
|
||||
out := make([]Rule, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
// RuleSlicesEqual returns true if two rule slices are equal
|
||||
func RuleSlicesEqual(a, b []Rule) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if !a[i].Equals(b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// RoleV2 represents role resource specification
|
||||
|
@ -900,7 +982,7 @@ func (r *RoleV2) V3() *RoleV3 {
|
|||
}
|
||||
|
||||
// translate old v2 resources to v3 rules
|
||||
rules := make(map[string][]string)
|
||||
rules := []Rule{}
|
||||
for resource, actions := range r.GetResources() {
|
||||
var verbs []string
|
||||
|
||||
|
@ -916,7 +998,7 @@ func (r *RoleV2) V3() *RoleV3 {
|
|||
verbs = []string{VerbReadSecrets, VerbCreate, VerbUpdate, VerbDelete}
|
||||
}
|
||||
|
||||
rules[resource] = utils.Deduplicate(verbs)
|
||||
rules = append(rules, NewRule(resource, verbs))
|
||||
}
|
||||
role.Spec.Allow.Rules = rules
|
||||
|
||||
|
@ -1036,30 +1118,6 @@ func NewRoleSet(roles ...Role) RoleSet {
|
|||
// RoleSet is a set of roles that implements access control functionality
|
||||
type RoleSet []Role
|
||||
|
||||
// MatchRule tests if the resource name and verb are in a given list of rules.
|
||||
func MatchRule(rules map[string][]string, resource string, verb string) bool {
|
||||
// empty selector matches nothing
|
||||
if len(rules) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// check for wildcard resource matcher
|
||||
for _, action := range rules[Wildcard] {
|
||||
if action == Wildcard || action == verb {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// check for matching resource by name
|
||||
for _, action := range rules[resource] {
|
||||
if action == Wildcard || action == verb {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MatchLogin returns true if attempted login matches any of the logins
|
||||
func MatchLogin(logins []string, login string) bool {
|
||||
for _, l := range logins {
|
||||
|
@ -1235,8 +1293,7 @@ func (set RoleSet) CheckAccessToRule(namespace string, resource string, verb str
|
|||
// check deny: a single match on a deny rule prohibits access
|
||||
for _, role := range set {
|
||||
matchNamespace := MatchNamespace(role.GetNamespaces(Deny), ProcessNamespace(namespace))
|
||||
matchRule := MatchRule(role.GetRules(Deny), resource, verb)
|
||||
if matchNamespace && matchRule {
|
||||
if matchNamespace && MakeRuleSet(role.GetRules(Deny)).Match(resource, verb) {
|
||||
return trace.AccessDenied("%v access to %v in namespace %v is denied for %v: deny rule matched", verb, resource, namespace, role)
|
||||
}
|
||||
}
|
||||
|
@ -1244,8 +1301,7 @@ func (set RoleSet) CheckAccessToRule(namespace string, resource string, verb str
|
|||
// check allow: if rule matches, grant access to resource
|
||||
for _, role := range set {
|
||||
matchNamespace := MatchNamespace(role.GetNamespaces(Allow), ProcessNamespace(namespace))
|
||||
matchRule := MatchRule(role.GetRules(Allow), resource, verb)
|
||||
if matchNamespace && matchRule {
|
||||
if matchNamespace && MakeRuleSet(role.GetRules(Allow)).Match(resource, verb) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,8 +134,8 @@ func (s *RoleSuite) TestRoleParse(c *C) {
|
|||
Allow: RoleConditions{
|
||||
NodeLabels: map[string]string{"a": "b"},
|
||||
Namespaces: []string{"system", "default"},
|
||||
Rules: map[string][]string{
|
||||
KindRole: []string{VerbRead, VerbList},
|
||||
Rules: []Rule{
|
||||
NewRule(KindRole, []string{VerbRead, VerbList}),
|
||||
},
|
||||
},
|
||||
Deny: RoleConditions{
|
||||
|
@ -342,8 +342,8 @@ func (s *RoleSuite) TestCheckRuleAccess(c *C) {
|
|||
Spec: RoleSpecV3{
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
Rules: map[string][]string{
|
||||
KindSession: []string{VerbRead},
|
||||
Rules: []Rule{
|
||||
NewRule(KindSession, []string{VerbRead}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -365,8 +365,8 @@ func (s *RoleSuite) TestCheckRuleAccess(c *C) {
|
|||
Spec: RoleSpecV3{
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{"system"},
|
||||
Rules: map[string][]string{
|
||||
KindSession: []string{VerbRead},
|
||||
Rules: []Rule{
|
||||
NewRule(KindSession, []string{VerbRead}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -379,8 +379,8 @@ func (s *RoleSuite) TestCheckRuleAccess(c *C) {
|
|||
Spec: RoleSpecV3{
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
Rules: map[string][]string{
|
||||
KindSession: []string{VerbCreate, VerbRead},
|
||||
Rules: []Rule{
|
||||
NewRule(KindSession, []string{VerbCreate, VerbRead}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -404,14 +404,14 @@ func (s *RoleSuite) TestCheckRuleAccess(c *C) {
|
|||
Spec: RoleSpecV3{
|
||||
Deny: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
Rules: map[string][]string{
|
||||
KindSession: []string{VerbCreate},
|
||||
Rules: []Rule{
|
||||
NewRule(KindSession, []string{VerbCreate}),
|
||||
},
|
||||
},
|
||||
Allow: RoleConditions{
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
Rules: map[string][]string{
|
||||
KindSession: []string{VerbCreate},
|
||||
Rules: []Rule{
|
||||
NewRule(KindSession, []string{VerbCreate}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -393,8 +393,8 @@ func (s *ServicesTestSuite) RolesCRUD(c *C) {
|
|||
Logins: []string{"root", "bob"},
|
||||
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
|
||||
Namespaces: []string{"default", "system"},
|
||||
Rules: map[string][]string{
|
||||
services.KindRole: services.RO(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.KindRole, services.RO()),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package utils
|
||||
package shell
|
||||
|
||||
/*
|
||||
#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
|
43
lib/shell/shell_test.go
Normal file
43
lib/shell/shell_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type ShellSuite struct {
|
||||
}
|
||||
|
||||
var _ = check.Suite(&ShellSuite{})
|
||||
|
||||
func (s *ShellSuite) TestGetShell(c *check.C) {
|
||||
shell, err := GetLoginShell("root")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(shell == "/bin/bash" || shell == "/bin/sh", check.Equals, true)
|
||||
|
||||
shell, err = GetLoginShell("non-existent-user")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(err.Error(), check.Matches, "user: unknown user non-existent-user")
|
||||
|
||||
shell, err = GetLoginShell("daemon")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(shell == "/usr/sbin/nologin" ||
|
||||
shell == "/sbin/nologin" ||
|
||||
shell == "/usr/bin/nologin" ||
|
||||
shell == "/usr/bin/false", check.Equals, true)
|
||||
}
|
|
@ -34,10 +34,10 @@ import (
|
|||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/events"
|
||||
"github.com/gravitational/teleport/lib/shell"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/kardianos/osext"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -118,7 +118,7 @@ func prepInteractiveCommand(ctx *ctx) (*exec.Cmd, error) {
|
|||
// determine shell for the given OS user:
|
||||
if ctx.exec.cmdName == "" {
|
||||
runShell = true
|
||||
ctx.exec.cmdName, err = utils.GetLoginShell(ctx.login)
|
||||
ctx.exec.cmdName, err = shell.GetLoginShell(ctx.login)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, trace.Wrap(err)
|
||||
|
@ -167,7 +167,7 @@ func prepareCommand(ctx *ctx) (*exec.Cmd, error) {
|
|||
}
|
||||
|
||||
// get user's shell:
|
||||
shell, err := utils.GetLoginShell(ctx.login)
|
||||
shell, err := shell.GetLoginShell(ctx.login)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
|
|
|
@ -935,7 +935,7 @@ func newUpack(username string, allowedLogins []string, a *auth.AuthServer) (*upa
|
|||
}
|
||||
role := services.RoleForUser(user)
|
||||
rules := role.GetRules(services.Allow)
|
||||
rules[services.Wildcard] = services.RW()
|
||||
rules = append(rules, services.NewRule(services.Wildcard, services.RW()))
|
||||
role.SetRules(services.Allow, rules)
|
||||
role.SetLogins(services.Allow, allowedLogins)
|
||||
err = a.UpsertRole(role, backend.Forever)
|
||||
|
|
|
@ -72,20 +72,3 @@ func (s *UtilsSuite) TestMiscFunctions(c *check.C) {
|
|||
c.Assert(Deduplicate([]string{"a", "b"}), check.DeepEquals, []string{"a", "b"})
|
||||
c.Assert(Deduplicate([]string{"a", "b", "b", "a", "c"}), check.DeepEquals, []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func (s *UtilsSuite) TestGetShell(c *check.C) {
|
||||
shell, err := GetLoginShell("root")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(shell == "/bin/bash" || shell == "/bin/sh", check.Equals, true)
|
||||
|
||||
shell, err = GetLoginShell("non-existent-user")
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(err.Error(), check.Matches, "user: unknown user non-existent-user")
|
||||
|
||||
shell, err = GetLoginShell("daemon")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(shell == "/usr/sbin/nologin" ||
|
||||
shell == "/sbin/nologin" ||
|
||||
shell == "/usr/bin/nologin" ||
|
||||
shell == "/usr/bin/false", check.Equals, true)
|
||||
}
|
||||
|
|
|
@ -209,7 +209,7 @@ func (s *WebSuite) SetUpTest(c *C) {
|
|||
role := services.RoleForUser(teleUser)
|
||||
role.SetLogins(services.Allow, []string{s.user})
|
||||
rules := role.GetRules(services.Allow)
|
||||
rules[services.Wildcard] = services.RW()
|
||||
rules = append(rules, services.NewRule(services.Wildcard, services.RW()))
|
||||
role.SetRules(services.Allow, rules)
|
||||
err = s.authServer.UpsertRole(role, backend.Forever)
|
||||
c.Assert(err, IsNil)
|
||||
|
@ -457,8 +457,8 @@ func (s *WebSuite) TestSAMLSuccess(c *C) {
|
|||
Allow: services.RoleConditions{
|
||||
NodeLabels: map[string]string{services.Wildcard: services.Wildcard},
|
||||
Namespaces: []string{defaults.Namespace},
|
||||
Rules: map[string][]string{
|
||||
services.Wildcard: services.RW(),
|
||||
Rules: []services.Rule{
|
||||
services.NewRule(services.Wildcard, services.RW()),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -109,15 +109,6 @@ func (a *RoleAccess) initAdmin(teleRole services.Role) {
|
|||
}
|
||||
|
||||
func (a *RoleAccess) applyAdmin(role services.Role) {
|
||||
if a.Admin.Enabled {
|
||||
allowAllNamespaces(role)
|
||||
applyResourceAccess(role, adminRules, services.RW())
|
||||
} else {
|
||||
rules := role.GetRules(services.Allow)
|
||||
delete(rules, services.Wildcard)
|
||||
role.SetRules(services.Allow, rules)
|
||||
applyResourceAccess(role, adminRules, services.RO())
|
||||
}
|
||||
}
|
||||
|
||||
func (a *RoleAccess) applySSH(teleRole services.Role) {
|
||||
|
@ -147,10 +138,11 @@ func none() []string {
|
|||
return nil
|
||||
}
|
||||
|
||||
func hasFullAccess(rules map[string][]string, resources []string) bool {
|
||||
func hasFullAccess(rules []services.Rule, resources []string) bool {
|
||||
set := services.MakeRuleSet(rules)
|
||||
for _, resource := range resources {
|
||||
hasRead := services.MatchRule(rules, resource, services.ActionRead)
|
||||
hasWrite := services.MatchRule(rules, resource, services.ActionWrite)
|
||||
hasRead := set.Match(resource, services.ActionRead)
|
||||
hasWrite := set.Match(resource, services.ActionWrite)
|
||||
|
||||
if !(hasRead && hasWrite) {
|
||||
return false
|
||||
|
@ -159,11 +151,3 @@ func hasFullAccess(rules map[string][]string, resources []string) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func applyResourceAccess(role services.Role, rules []string, verbs []string) {
|
||||
rs := role.GetRules(services.Allow)
|
||||
for _, rule := range rules {
|
||||
rs[rule] = verbs
|
||||
}
|
||||
role.SetRules(services.Allow, rs)
|
||||
}
|
||||
|
|
|
@ -127,13 +127,10 @@ func (n *namespaceCollection) writeYAML(w io.Writer) error {
|
|||
return trace.Wrap(err)
|
||||
}
|
||||
|
||||
func printActions(resources map[string][]string) string {
|
||||
func printActions(rules []services.Rule) string {
|
||||
pairs := []string{}
|
||||
for key, actions := range resources {
|
||||
if key == services.Wildcard {
|
||||
return fmt.Sprintf("<all resources>: %v", strings.Join(actions, ","))
|
||||
}
|
||||
pairs = append(pairs, fmt.Sprintf("%v:%v", key, strings.Join(actions, ",")))
|
||||
for _, rule := range rules {
|
||||
pairs = append(pairs, fmt.Sprintf("%v:%v", strings.Join(rule.Resources, ","), strings.Join(rule.Verbs, ",")))
|
||||
}
|
||||
return strings.Join(pairs, ",")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue