Adds support to ACL package for session policies.

This commit is contained in:
James Phillips 2016-12-12 20:20:28 -08:00
parent 99e810f9c7
commit 60d4322c49
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
4 changed files with 215 additions and 0 deletions

View File

@ -95,6 +95,13 @@ type ACL interface {
// service // service
ServiceWrite(string) bool ServiceWrite(string) bool
// SessionRead checks for permission to read sessions for a given node.
SessionRead(string) bool
// SessionWrite checks for permission to create sessions for a given
// node.
SessionWrite(string) bool
// Snapshot checks for permission to take and restore snapshots. // Snapshot checks for permission to take and restore snapshots.
Snapshot() bool Snapshot() bool
} }
@ -175,6 +182,14 @@ func (s *StaticACL) ServiceWrite(string) bool {
return s.defaultAllow return s.defaultAllow
} }
func (s *StaticACL) SessionRead(string) bool {
return s.defaultAllow
}
func (s *StaticACL) SessionWrite(string) bool {
return s.defaultAllow
}
func (s *StaticACL) Snapshot() bool { func (s *StaticACL) Snapshot() bool {
return s.allowManage return s.allowManage
} }
@ -224,6 +239,9 @@ type PolicyACL struct {
// serviceRules contains the service policies // serviceRules contains the service policies
serviceRules *radix.Tree serviceRules *radix.Tree
// sessionRules contains the session policies
sessionRules *radix.Tree
// eventRules contains the user event policies // eventRules contains the user event policies
eventRules *radix.Tree eventRules *radix.Tree
@ -247,6 +265,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
keyRules: radix.New(), keyRules: radix.New(),
nodeRules: radix.New(), nodeRules: radix.New(),
serviceRules: radix.New(), serviceRules: radix.New(),
sessionRules: radix.New(),
eventRules: radix.New(), eventRules: radix.New(),
preparedQueryRules: radix.New(), preparedQueryRules: radix.New(),
} }
@ -266,6 +285,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
p.serviceRules.Insert(sp.Name, sp.Policy) p.serviceRules.Insert(sp.Name, sp.Policy)
} }
// Load the session policy
for _, sp := range policy.Sessions {
p.sessionRules.Insert(sp.Node, sp.Policy)
}
// Load the event policy // Load the event policy
for _, ep := range policy.Events { for _, ep := range policy.Events {
p.eventRules.Insert(ep.Event, ep.Policy) p.eventRules.Insert(ep.Event, ep.Policy)
@ -547,3 +571,39 @@ func (p *PolicyACL) ServiceWrite(name string) bool {
// No matching rule, use the parent. // No matching rule, use the parent.
return p.parent.ServiceWrite(name) return p.parent.ServiceWrite(name)
} }
// 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
}
}
// No matching rule, use the parent.
return p.parent.SessionRead(node)
}
// 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
}
}
// No matching rule, use the parent.
return p.parent.SessionWrite(node)
}

View File

@ -83,6 +83,12 @@ func TestStaticACL(t *testing.T) {
if !all.ServiceWrite("foobar") { if !all.ServiceWrite("foobar") {
t.Fatalf("should allow") t.Fatalf("should allow")
} }
if !all.SessionRead("foobar") {
t.Fatalf("should allow")
}
if !all.SessionWrite("foobar") {
t.Fatalf("should allow")
}
if all.Snapshot() { if all.Snapshot() {
t.Fatalf("should not allow") t.Fatalf("should not allow")
} }
@ -141,6 +147,12 @@ func TestStaticACL(t *testing.T) {
if none.ServiceWrite("foobar") { if none.ServiceWrite("foobar") {
t.Fatalf("should not allow") t.Fatalf("should not allow")
} }
if none.SessionRead("foobar") {
t.Fatalf("should not allow")
}
if none.SessionWrite("foobar") {
t.Fatalf("should not allow")
}
if none.Snapshot() { if none.Snapshot() {
t.Fatalf("should not allow") t.Fatalf("should not allow")
} }
@ -193,6 +205,12 @@ func TestStaticACL(t *testing.T) {
if !manage.ServiceWrite("foobar") { if !manage.ServiceWrite("foobar") {
t.Fatalf("should allow") t.Fatalf("should allow")
} }
if !manage.SessionRead("foobar") {
t.Fatalf("should allow")
}
if !manage.SessionWrite("foobar") {
t.Fatalf("should allow")
}
if !manage.Snapshot() { if !manage.Snapshot() {
t.Fatalf("should allow") t.Fatalf("should allow")
} }
@ -661,3 +679,86 @@ func TestPolicyACL_Node(t *testing.T) {
} }
} }
} }
func TestPolicyACL_Session(t *testing.T) {
deny := DenyAll()
policyRoot := &Policy{
Sessions: []*SessionPolicy{
&SessionPolicy{
Node: "root-nope",
Policy: PolicyDeny,
},
&SessionPolicy{
Node: "root-ro",
Policy: PolicyRead,
},
&SessionPolicy{
Node: "root-rw",
Policy: PolicyWrite,
},
&SessionPolicy{
Node: "override",
Policy: PolicyDeny,
},
},
}
root, err := New(deny, policyRoot)
if err != nil {
t.Fatalf("err: %v", err)
}
policy := &Policy{
Sessions: []*SessionPolicy{
&SessionPolicy{
Node: "child-nope",
Policy: PolicyDeny,
},
&SessionPolicy{
Node: "child-ro",
Policy: PolicyRead,
},
&SessionPolicy{
Node: "child-rw",
Policy: PolicyWrite,
},
&SessionPolicy{
Node: "override",
Policy: PolicyWrite,
},
},
}
acl, err := New(root, policy)
if err != nil {
t.Fatalf("err: %v", err)
}
type sessioncase struct {
inp string
read bool
write bool
}
cases := []sessioncase{
{"nope", false, false},
{"root-nope", false, false},
{"root-ro", true, false},
{"root-rw", true, true},
{"root-nope-prefix", false, false},
{"root-ro-prefix", true, false},
{"root-rw-prefix", true, true},
{"child-nope", false, false},
{"child-ro", true, false},
{"child-rw", true, true},
{"child-nope-prefix", false, false},
{"child-ro-prefix", true, false},
{"child-rw-prefix", true, true},
{"override", true, true},
}
for _, c := range cases {
if c.read != acl.SessionRead(c.inp) {
t.Fatalf("Read fail: %#v", c)
}
if c.write != acl.SessionWrite(c.inp) {
t.Fatalf("Write fail: %#v", c)
}
}
}

View File

@ -19,6 +19,7 @@ type Policy struct {
Keys []*KeyPolicy `hcl:"key,expand"` Keys []*KeyPolicy `hcl:"key,expand"`
Nodes []*NodePolicy `hcl:"node,expand"` Nodes []*NodePolicy `hcl:"node,expand"`
Services []*ServicePolicy `hcl:"service,expand"` Services []*ServicePolicy `hcl:"service,expand"`
Sessions []*SessionPolicy `hcl:"session,expand"`
Events []*EventPolicy `hcl:"event,expand"` Events []*EventPolicy `hcl:"event,expand"`
PreparedQueries []*PreparedQueryPolicy `hcl:"query,expand"` PreparedQueries []*PreparedQueryPolicy `hcl:"query,expand"`
Keyring string `hcl:"keyring"` Keyring string `hcl:"keyring"`
@ -55,6 +56,17 @@ func (s *ServicePolicy) GoString() string {
return fmt.Sprintf("%#v", *s) return fmt.Sprintf("%#v", *s)
} }
// SessionPolicy represents a policy for making sessions tied to specific node
// name prefixes.
type SessionPolicy struct {
Node string `hcl:",key"`
Policy string
}
func (s *SessionPolicy) GoString() string {
return fmt.Sprintf("%#v", *s)
}
// EventPolicy represents a user event policy. // EventPolicy represents a user event policy.
type EventPolicy struct { type EventPolicy struct {
Event string `hcl:",key"` Event string `hcl:",key"`
@ -125,6 +137,13 @@ func Parse(rules string) (*Policy, error) {
} }
} }
// Validate the session policies
for _, sp := range p.Sessions {
if !isPolicyValid(sp.Policy) {
return nil, fmt.Errorf("Invalid session policy: %#v", sp)
}
}
// Validate the user event policies // Validate the user event policies
for _, ep := range p.Events { for _, ep := range p.Events {
if !isPolicyValid(ep.Policy) { if !isPolicyValid(ep.Policy) {

View File

@ -46,6 +46,12 @@ service "" {
service "foo" { service "foo" {
policy = "read" policy = "read"
} }
session "foo" {
policy = "write"
}
session "bar" {
policy = "deny"
}
query "" { query "" {
policy = "read" policy = "read"
} }
@ -129,6 +135,16 @@ query "bar" {
Policy: PolicyRead, Policy: PolicyRead,
}, },
}, },
Sessions: []*SessionPolicy{
&SessionPolicy{
Node: "foo",
Policy: PolicyWrite,
},
&SessionPolicy{
Node: "bar",
Policy: PolicyDeny,
},
},
} }
out, err := Parse(inp) out, err := Parse(inp)
@ -199,6 +215,14 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
"foo": { "foo": {
"policy": "read" "policy": "read"
} }
},
"session": {
"foo": {
"policy": "write"
},
"bar": {
"policy": "deny"
}
} }
}` }`
exp := &Policy{ exp := &Policy{
@ -274,6 +298,16 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
Policy: PolicyRead, Policy: PolicyRead,
}, },
}, },
Sessions: []*SessionPolicy{
&SessionPolicy{
Node: "foo",
Policy: PolicyWrite,
},
&SessionPolicy{
Node: "bar",
Policy: PolicyDeny,
},
},
} }
out, err := Parse(inp) out, err := Parse(inp)
@ -331,6 +365,7 @@ func TestACLPolicy_Bad_Policy(t *testing.T) {
`operator = "nope"`, `operator = "nope"`,
`query "" { policy = "nope" }`, `query "" { policy = "nope" }`,
`service "" { policy = "nope" }`, `service "" { policy = "nope" }`,
`session "" { policy = "nope" }`,
} }
for _, c := range cases { for _, c := range cases {
_, err := Parse(c) _, err := Parse(c)