Fix ACL enforcement

This creates one function that takes a rule and the required permissions and returns whether it should be allowed and whether to leave the decision to the parent acl.

Then this function is used everywhere. This makes acl enforcement consistent.

There were several places where a default allow policy with explicit deny rules wasnt being handled and several others where it wasn’t using the parent acl appropriately but would lump no policy in with a deny policy. All of that has been fixed.
This commit is contained in:
Matt Keeler 2018-07-24 16:21:56 -04:00
parent ce214f133b
commit 883c5dd001
1 changed files with 101 additions and 141 deletions

View File

@ -328,6 +328,34 @@ type PolicyACL struct {
operatorRule string
}
func enforce(rule string, requiredPermission string) (allow, recurse bool) {
switch rule {
case PolicyWrite:
// grants read, list and write permissions
return true, false
case PolicyList:
// grants read and list permissions
if requiredPermission == PolicyList || requiredPermission == PolicyRead {
return true, false
} else {
return false, false
}
case PolicyRead:
// grants just read permissions
if requiredPermission == PolicyRead {
return true, false
} else {
return false, false
}
case PolicyDeny:
// explicit denial - do not recurse
return false, false
default:
// need to recurse as there was no specific policy set
return false, true
}
}
// New is used to construct a policy based ACL from a set of policies
// and a parent policy to resolve missing cases.
func New(parent ACL, policy *Policy, sentinel sentinel.Evaluator) (*PolicyACL, error) {
@ -433,14 +461,9 @@ func (p *PolicyACL) ACLModify() bool {
// node.
func (p *PolicyACL) AgentRead(node string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.agentRules.LongestPrefix(node)
if ok {
switch rule {
case PolicyRead, PolicyWrite:
return true
default:
return false
if _, rule, ok := p.agentRules.LongestPrefix(node); ok {
if allow, recurse := enforce(rule.(string), PolicyRead); !recurse {
return allow
}
}
@ -455,11 +478,8 @@ func (p *PolicyACL) AgentWrite(node string) bool {
_, rule, ok := p.agentRules.LongestPrefix(node)
if ok {
switch rule {
case PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(rule.(string), PolicyWrite); !recurse {
return allow
}
}
@ -477,15 +497,12 @@ func (p *PolicyACL) Snapshot() bool {
func (p *PolicyACL) EventRead(name string) bool {
// Longest-prefix match on event names
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
switch rule {
case PolicyRead, PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(rule.(string), PolicyRead); !recurse {
return allow
}
}
// Nothing matched, use parent
// No matching rule, use the parent.
return p.parent.EventRead(name)
}
@ -494,7 +511,9 @@ func (p *PolicyACL) EventRead(name string) bool {
func (p *PolicyACL) EventWrite(name string) bool {
// Longest-prefix match event names
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
return rule == PolicyWrite
if allow, recurse := enforce(rule.(string), PolicyWrite); !recurse {
return allow
}
}
// No match, use parent
@ -512,14 +531,10 @@ func (p *PolicyACL) IntentionDefaultAllow() bool {
// intention is allowed.
func (p *PolicyACL) IntentionRead(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.intentionRules.LongestPrefix(prefix)
if ok {
if _, rule, ok := p.intentionRules.LongestPrefix(prefix); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyRead, PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyRead); !recurse {
return allow
}
}
@ -531,14 +546,10 @@ func (p *PolicyACL) IntentionRead(prefix string) bool {
// intention is allowed.
func (p *PolicyACL) IntentionWrite(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.intentionRules.LongestPrefix(prefix)
if ok {
if _, rule, ok := p.intentionRules.LongestPrefix(prefix); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyWrite); !recurse {
return allow
}
}
@ -549,14 +560,10 @@ func (p *PolicyACL) IntentionWrite(prefix string) bool {
// KeyRead returns if a key is allowed to be read
func (p *PolicyACL) KeyRead(key string) bool {
// Look for a matching rule
_, rule, ok := p.keyRules.LongestPrefix(key)
if ok {
if _, rule, ok := p.keyRules.LongestPrefix(key); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyRead, PolicyWrite, PolicyList:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyRead); !recurse {
return allow
}
}
@ -567,14 +574,10 @@ func (p *PolicyACL) KeyRead(key string) bool {
// KeyList returns if a key is allowed to be listed
func (p *PolicyACL) KeyList(key string) bool {
// Look for a matching rule
_, rule, ok := p.keyRules.LongestPrefix(key)
if ok {
if _, rule, ok := p.keyRules.LongestPrefix(key); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyList, PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyList); !recurse {
return allow
}
}
@ -585,13 +588,12 @@ func (p *PolicyACL) KeyList(key string) bool {
// KeyWrite returns if a key is allowed to be written
func (p *PolicyACL) KeyWrite(key string, scope sentinel.ScopeFn) bool {
// Look for a matching rule
_, rule, ok := p.keyRules.LongestPrefix(key)
if ok {
if _, rule, ok := p.keyRules.LongestPrefix(key); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyWrite:
return p.executeCodePolicy(&pr.sentinelPolicy, scope)
default:
if allow, recurse := enforce(pr.aclPolicy, PolicyWrite); !recurse {
if allow {
return p.executeCodePolicy(&pr.sentinelPolicy, scope)
}
return false
}
}
@ -636,48 +638,48 @@ func (p *PolicyACL) KeyWritePrefix(prefix string) bool {
// KeyringRead is used to determine if the keyring can be
// read by the current ACL token.
func (p *PolicyACL) KeyringRead() bool {
switch p.keyringRule {
case PolicyRead, PolicyWrite:
return true
case PolicyDeny:
return false
default:
return p.parent.KeyringRead()
if allow, recurse := enforce(p.keyringRule, PolicyRead); !recurse {
return allow
}
return p.parent.KeyringRead()
}
// KeyringWrite determines if the keyring can be manipulated.
func (p *PolicyACL) KeyringWrite() bool {
if p.keyringRule == PolicyWrite {
return true
if allow, recurse := enforce(p.keyringRule, PolicyWrite); !recurse {
return allow
}
return p.parent.KeyringWrite()
}
// OperatorRead determines if the read-only operator functions are allowed.
func (p *PolicyACL) OperatorRead() bool {
switch p.operatorRule {
case PolicyRead, PolicyWrite:
return true
case PolicyDeny:
return false
default:
return p.parent.OperatorRead()
if allow, recurse := enforce(p.operatorRule, PolicyRead); !recurse {
return allow
}
return p.parent.OperatorRead()
}
// OperatorWrite determines if the state-changing operator functions are
// allowed.
func (p *PolicyACL) OperatorWrite() bool {
if allow, recurse := enforce(p.operatorRule, PolicyWrite); !recurse {
return allow
}
return p.parent.OperatorWrite()
}
// NodeRead checks if reading (discovery) of a node is allowed
func (p *PolicyACL) NodeRead(name string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.nodeRules.LongestPrefix(name)
if ok {
if _, rule, ok := p.nodeRules.LongestPrefix(name); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyRead, PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyRead); !recurse {
return allow
}
}
@ -688,15 +690,10 @@ func (p *PolicyACL) NodeRead(name string) bool {
// NodeWrite checks if writing (registering) a node is allowed
func (p *PolicyACL) NodeWrite(name string, scope sentinel.ScopeFn) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.nodeRules.LongestPrefix(name)
if ok {
if _, rule, ok := p.nodeRules.LongestPrefix(name); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyWrite); !recurse {
return allow
}
}
@ -704,27 +701,13 @@ func (p *PolicyACL) NodeWrite(name string, scope sentinel.ScopeFn) bool {
return p.parent.NodeWrite(name, scope)
}
// OperatorWrite determines if the state-changing operator functions are
// allowed.
func (p *PolicyACL) OperatorWrite() bool {
if p.operatorRule == PolicyWrite {
return true
}
return p.parent.OperatorWrite()
}
// PreparedQueryRead checks if reading (listing) of a prepared query is
// allowed - this isn't execution, just listing its contents.
func (p *PolicyACL) PreparedQueryRead(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.preparedQueryRules.LongestPrefix(prefix)
if ok {
switch rule {
case PolicyRead, PolicyWrite:
return true
default:
return false
if _, rule, ok := p.preparedQueryRules.LongestPrefix(prefix); ok {
if allow, recurse := enforce(rule.(string), PolicyRead); !recurse {
return allow
}
}
@ -736,14 +719,9 @@ func (p *PolicyACL) PreparedQueryRead(prefix string) bool {
// prepared query is allowed.
func (p *PolicyACL) PreparedQueryWrite(prefix string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.preparedQueryRules.LongestPrefix(prefix)
if ok {
switch rule {
case PolicyWrite:
return true
default:
return false
if _, rule, ok := p.preparedQueryRules.LongestPrefix(prefix); ok {
if allow, recurse := enforce(rule.(string), PolicyWrite); !recurse {
return allow
}
}
@ -754,14 +732,10 @@ func (p *PolicyACL) PreparedQueryWrite(prefix string) bool {
// ServiceRead checks if reading (discovery) of a service is allowed
func (p *PolicyACL) ServiceRead(name string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.serviceRules.LongestPrefix(name)
if ok {
if _, rule, ok := p.serviceRules.LongestPrefix(name); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyRead, PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyRead); !recurse {
return allow
}
}
@ -772,14 +746,10 @@ func (p *PolicyACL) ServiceRead(name string) bool {
// ServiceWrite checks if writing (registering) a service is allowed
func (p *PolicyACL) ServiceWrite(name string, scope sentinel.ScopeFn) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.serviceRules.LongestPrefix(name)
if ok {
if _, rule, ok := p.serviceRules.LongestPrefix(name); ok {
pr := rule.(PolicyRule)
switch pr.aclPolicy {
case PolicyWrite:
return true
default:
return false
if allow, recurse := enforce(pr.aclPolicy, PolicyWrite); !recurse {
return allow
}
}
@ -790,14 +760,9 @@ func (p *PolicyACL) ServiceWrite(name string, scope sentinel.ScopeFn) bool {
// SessionRead checks for permission to read sessions for a given node.
func (p *PolicyACL) SessionRead(node string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.sessionRules.LongestPrefix(node)
if ok {
switch rule {
case PolicyRead, PolicyWrite:
return true
default:
return false
if _, rule, ok := p.sessionRules.LongestPrefix(node); ok {
if allow, recurse := enforce(rule.(string), PolicyRead); !recurse {
return allow
}
}
@ -808,14 +773,9 @@ func (p *PolicyACL) SessionRead(node string) bool {
// SessionWrite checks for permission to create sessions for a given node.
func (p *PolicyACL) SessionWrite(node string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.sessionRules.LongestPrefix(node)
if ok {
switch rule {
case PolicyWrite:
return true
default:
return false
if _, rule, ok := p.sessionRules.LongestPrefix(node); ok {
if allow, recurse := enforce(rule.(string), PolicyWrite); !recurse {
return allow
}
}