From 193f93107a7ca32258331ebadf0a5ff2addf1f66 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 4 Mar 2018 00:38:04 -0800 Subject: [PATCH] acl: implement IntentionRead/Write methods on ACL interface --- acl/acl.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ acl/acl_test.go | 44 ++++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/acl/acl.go b/acl/acl.go index 73bcc4fc36..4ac88f01c9 100644 --- a/acl/acl.go +++ b/acl/acl.go @@ -60,6 +60,13 @@ type ACL interface { // EventWrite determines if a specific event may be fired. EventWrite(string) bool + // IntentionRead determines if a specific intention can be read. + IntentionRead(string) bool + + // IntentionWrite determines if a specific intention can be + // created, modified, or deleted. + IntentionWrite(string) bool + // KeyList checks for permission to list keys under a prefix KeyList(string) bool @@ -154,6 +161,14 @@ func (s *StaticACL) EventWrite(string) bool { return s.defaultAllow } +func (s *StaticACL) IntentionRead(string) bool { + return s.defaultAllow +} + +func (s *StaticACL) IntentionWrite(string) bool { + return s.defaultAllow +} + func (s *StaticACL) KeyRead(string) bool { return s.defaultAllow } @@ -275,6 +290,9 @@ type PolicyACL struct { // agentRules contains the agent policies agentRules *radix.Tree + // intentionRules contains the service intention policies + intentionRules *radix.Tree + // keyRules contains the key policies keyRules *radix.Tree @@ -308,6 +326,7 @@ func New(parent ACL, policy *Policy, sentinel sentinel.Evaluator) (*PolicyACL, e p := &PolicyACL{ parent: parent, agentRules: radix.New(), + intentionRules: radix.New(), keyRules: radix.New(), nodeRules: radix.New(), serviceRules: radix.New(), @@ -347,6 +366,25 @@ func New(parent ACL, policy *Policy, sentinel sentinel.Evaluator) (*PolicyACL, e sentinelPolicy: sp.Sentinel, } p.serviceRules.Insert(sp.Name, policyRule) + + // Determine the intention. The intention could be blank (not set). + // If the intention is not set, the value depends on the value of + // the service policy. + intention := sp.Intentions + if intention == "" { + switch sp.Policy { + case PolicyRead, PolicyWrite: + intention = PolicyRead + default: + intention = PolicyDeny + } + } + + policyRule = PolicyRule{ + aclPolicy: intention, + sentinelPolicy: sp.Sentinel, + } + p.intentionRules.Insert(sp.Name, policyRule) } // Load the session policy @@ -455,6 +493,44 @@ func (p *PolicyACL) EventWrite(name string) bool { return p.parent.EventWrite(name) } +// IntentionRead checks if writing (creating, updating, or deleting) of an +// 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 { + pr := rule.(PolicyRule) + switch pr.aclPolicy { + case PolicyRead, PolicyWrite: + return true + default: + return false + } + } + + // No matching rule, use the parent. + return p.parent.IntentionRead(prefix) +} + +// IntentionWrite checks if writing (creating, updating, or deleting) of an +// 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 { + pr := rule.(PolicyRule) + switch pr.aclPolicy { + case PolicyWrite: + return true + default: + return false + } + } + + // No matching rule, use the parent. + return p.parent.IntentionWrite(prefix) +} + // KeyRead returns if a key is allowed to be read func (p *PolicyACL) KeyRead(key string) bool { // Look for a matching rule diff --git a/acl/acl_test.go b/acl/acl_test.go index 02ae0efb46..85f35f6066 100644 --- a/acl/acl_test.go +++ b/acl/acl_test.go @@ -53,6 +53,9 @@ func TestStaticACL(t *testing.T) { if !all.EventWrite("foobar") { t.Fatalf("should allow") } + if !all.IntentionWrite("foobar") { + t.Fatalf("should allow") + } if !all.KeyRead("foobar") { t.Fatalf("should allow") } @@ -123,6 +126,9 @@ func TestStaticACL(t *testing.T) { if none.EventWrite("") { t.Fatalf("should not allow") } + if none.IntentionWrite("foo") { + t.Fatalf("should not allow") + } if none.KeyRead("foobar") { t.Fatalf("should not allow") } @@ -187,6 +193,9 @@ func TestStaticACL(t *testing.T) { if !manage.EventWrite("foobar") { t.Fatalf("should allow") } + if !manage.IntentionWrite("foobar") { + t.Fatalf("should allow") + } if !manage.KeyRead("foobar") { t.Fatalf("should allow") } @@ -305,8 +314,14 @@ func TestPolicyACL(t *testing.T) { Policy: PolicyDeny, }, &ServicePolicy{ - Name: "barfoo", - Policy: PolicyWrite, + Name: "barfoo", + Policy: PolicyWrite, + Intentions: PolicyWrite, + }, + &ServicePolicy{ + Name: "intbaz", + Policy: PolicyWrite, + Intentions: PolicyDeny, }, }, } @@ -344,6 +359,31 @@ func TestPolicyACL(t *testing.T) { } } + // Test the intentions + type intentioncase struct { + inp string + read bool + write bool + } + icases := []intentioncase{ + {"other", true, false}, + {"foo", true, false}, + {"bar", false, false}, + {"foobar", true, false}, + {"barfo", false, false}, + {"barfoo", true, true}, + {"barfoo2", true, true}, + {"intbaz", false, false}, + } + for _, c := range icases { + if c.read != acl.IntentionRead(c.inp) { + t.Fatalf("Read fail: %#v", c) + } + if c.write != acl.IntentionWrite(c.inp) { + t.Fatalf("Write fail: %#v", c) + } + } + // Test the services type servicecase struct { inp string