Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Intentions ACL enforcement updates #7028

Merged
merged 8 commits into from
Jan 13, 2020
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
32 changes: 32 additions & 0 deletions acl/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package acl

const (
WildcardName = "*"
)

// Config encapsualtes all of the generic configuration parameters used for
// policy parsing and enforcement
type Config struct {
// WildcardName is the string that represents a request to authorize a wildcard permission
WildcardName string

// embedded enterprise configuration
EnterpriseConfig
}

// GetWildcardName will retrieve the configured wildcard name or provide a default
// in the case that the config is Nil or the wildcard name is unset.
func (c *Config) GetWildcardName() string {
mkeeler marked this conversation as resolved.
Show resolved Hide resolved
if c == nil || c.WildcardName == "" {
return WildcardName
}
return c.WildcardName
}

// Close will relinquish any resources this Config might be holding on to or
// managing.
func (c *Config) Close() {
if c != nil {
c.EnterpriseConfig.Close()
}
}
9 changes: 6 additions & 3 deletions acl/acl_oss.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

package acl

// Config stub
type Config struct{}
type EnterpriseConfig struct {
// no fields in OSS
}

func (_ *Config) Close() {}
func (_ *EnterpriseConfig) Close() {
// do nothing
}
11 changes: 11 additions & 0 deletions acl/authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,14 @@ func Enforce(authz Authorizer, rsc Resource, segment string, access string, ctx

return Deny, fmt.Errorf("Invalid access level for %s resource: %s", rsc, access)
}

// NewAuthorizerFromRules is a convenience function to invoke NewPolicyFromSource followed by NewPolicyAuthorizer with
// the parse policy.
func NewAuthorizerFromRules(id string, revision uint64, rules string, syntax SyntaxVersion, conf *Config, meta *EnterprisePolicyMeta) (Authorizer, error) {
mkeeler marked this conversation as resolved.
Show resolved Hide resolved
policy, err := NewPolicyFromSource(id, revision, rules, syntax, conf, meta)
if err != nil {
return nil, err
}

return NewPolicyAuthorizer([]*Policy{policy}, conf)
}
109 changes: 109 additions & 0 deletions acl/policy_authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,107 @@ func newPolicyAuthorizerFromRules(rules *PolicyRules, ent *Config) (Authorizer,
return p, nil
}

// enforceCallbacks are to be passed to anyAllowed or allAllowed. The interface{}
// parameter will be a value stored in the radix.Tree passed to those functions.
// prefixOnly indicates that only we only want to consider the prefix matching rule
// if any. The return value indicates whether this one leaf node in the tree would
// allow, deny or make no decision regarding some authorization.
type enforceCallback func(raw interface{}, prefixOnly bool) EnforcementDecision

func anyAllowed(tree *radix.Tree, enforceFn enforceCallback) EnforcementDecision {
decision := Default

// special case for handling a catch-all prefix rule. If the rule woul Deny access then our default decision
// should be to Deny, but this decision should still be overridable with other more specific rules.
if raw, found := tree.Get(""); found {
decision = enforceFn(raw, true)
if decision == Allow {
return Allow
}
}

tree.Walk(func(path string, raw interface{}) bool {
if enforceFn(raw, false) == Allow {
decision = Allow
return true
}

return false
})

return decision
}

func allAllowed(tree *radix.Tree, enforceFn enforceCallback) EnforcementDecision {
decision := Default

// look for a "" prefix rule
if raw, found := tree.Get(""); found {
// ensure that the empty prefix rule would allow the access
// if it does allow it we still must check all the other rules to ensure
// nothing overrides the top level grant with a different access level
// if not we can return early
decision = enforceFn(raw, true)

// the top level prefix rule denied access so we can return early.
if decision == Deny {
return Deny
}
}

tree.Walk(func(path string, raw interface{}) bool {
if enforceFn(raw, false) == Deny {
decision = Deny
return true
}
return false
})

return decision
}

func (authz *policyAuthorizer) anyAllowed(tree *radix.Tree, requiredPermission AccessLevel) EnforcementDecision {
return anyAllowed(tree, func(raw interface{}, prefixOnly bool) EnforcementDecision {
leaf := raw.(*policyAuthorizerRadixLeaf)
decision := Default

if leaf.prefix != nil {
decision = enforce(leaf.prefix.access, requiredPermission)
}

if prefixOnly || decision == Allow || leaf.exact == nil {
return decision
}

return enforce(leaf.exact.access, requiredPermission)
})
}

func (authz *policyAuthorizer) allAllowed(tree *radix.Tree, requiredPermission AccessLevel) EnforcementDecision {
return allAllowed(tree, func(raw interface{}, prefixOnly bool) EnforcementDecision {
leaf := raw.(*policyAuthorizerRadixLeaf)
prefixDecision := Default

if leaf.prefix != nil {
prefixDecision = enforce(leaf.prefix.access, requiredPermission)
}

if prefixOnly || prefixDecision == Deny || leaf.exact == nil {
return prefixDecision
}

decision := enforce(leaf.exact.access, requiredPermission)

if decision == Default {
// basically this means defer to the prefix decision as the
// authorizer rule made no decision with an exact match rule
return prefixDecision
}

return decision
})
}

// ACLRead checks if listing of ACLs is allowed
func (p *policyAuthorizer) ACLRead(*AuthorizerContext) EnforcementDecision {
if p.aclRule != nil {
Expand Down Expand Up @@ -410,6 +511,10 @@ func (p *policyAuthorizer) IntentionDefaultAllow(_ *AuthorizerContext) Enforceme
// IntentionRead checks if writing (creating, updating, or deleting) of an
// intention is allowed.
func (p *policyAuthorizer) IntentionRead(prefix string, _ *AuthorizerContext) EnforcementDecision {
if prefix == "*" {
return p.anyAllowed(p.intentionRules, AccessRead)
}

if rule, ok := getPolicy(prefix, p.intentionRules); ok {
return enforce(rule.access, AccessRead)
}
Expand All @@ -419,6 +524,10 @@ func (p *policyAuthorizer) IntentionRead(prefix string, _ *AuthorizerContext) En
// IntentionWrite checks if writing (creating, updating, or deleting) of an
// intention is allowed.
func (p *policyAuthorizer) IntentionWrite(prefix string, _ *AuthorizerContext) EnforcementDecision {
if prefix == "*" {
return p.allAllowed(p.intentionRules, AccessWrite)
}

if rule, ok := getPolicy(prefix, p.intentionRules); ok {
return enforce(rule.access, AccessWrite)
}
Expand Down
Loading