mirror of https://github.com/status-im/consul.git
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:
parent
ce214f133b
commit
883c5dd001
242
acl/acl.go
242
acl/acl.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue