Adds support to the ACL package for agent policies.

This commit is contained in:
James Phillips 2016-12-12 23:05:11 -08:00
parent 34da7ccd64
commit 022baeea13
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
4 changed files with 218 additions and 0 deletions

View File

@ -41,6 +41,14 @@ type ACL interface {
// ACLModify checks for permission to manipulate ACLs
ACLModify() bool
// AgentRead checks for permission to read from agent endpoints for a
// given node.
AgentRead(string) bool
// AgentWrite checks for permission to make changes via agent endpoints
// for a given node.
AgentWrite(string) bool
// EventRead determines if a specific event can be queried.
EventRead(string) bool
@ -122,6 +130,14 @@ func (s *StaticACL) ACLModify() bool {
return s.allowManage
}
func (s *StaticACL) AgentRead(string) bool {
return s.defaultAllow
}
func (s *StaticACL) AgentWrite(string) bool {
return s.defaultAllow
}
func (s *StaticACL) EventRead(string) bool {
return s.defaultAllow
}
@ -230,6 +246,9 @@ type PolicyACL struct {
// no matching rule.
parent ACL
// agentRules contains the agent policies
agentRules *radix.Tree
// keyRules contains the key policies
keyRules *radix.Tree
@ -262,6 +281,7 @@ type PolicyACL struct {
func New(parent ACL, policy *Policy) (*PolicyACL, error) {
p := &PolicyACL{
parent: parent,
agentRules: radix.New(),
keyRules: radix.New(),
nodeRules: radix.New(),
serviceRules: radix.New(),
@ -270,6 +290,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
preparedQueryRules: radix.New(),
}
// Load the agent policy
for _, ap := range policy.Agents {
p.agentRules.Insert(ap.Node, ap.Policy)
}
// Load the key policy
for _, kp := range policy.Keys {
p.keyRules.Insert(kp.Prefix, kp.Policy)
@ -319,6 +344,44 @@ func (p *PolicyACL) ACLModify() bool {
return p.parent.ACLModify()
}
// AgentRead checks for permission to read from agent endpoints for a given
// 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
}
}
// No matching rule, use the parent.
return p.parent.AgentRead(node)
}
// AgentWrite checks for permission to make changes via agent endpoints for a
// given node.
func (p *PolicyACL) AgentWrite(node string) bool {
// Check for an exact rule or catch-all
_, rule, ok := p.agentRules.LongestPrefix(node)
if ok {
switch rule {
case PolicyWrite:
return true
default:
return false
}
}
// No matching rule, use the parent.
return p.parent.AgentWrite(node)
}
// Snapshot checks if taking and restoring snapshots is allowed.
func (p *PolicyACL) Snapshot() bool {
return p.parent.Snapshot()

View File

@ -41,6 +41,12 @@ func TestStaticACL(t *testing.T) {
if all.ACLModify() {
t.Fatalf("should not allow")
}
if !all.AgentRead("foobar") {
t.Fatalf("should allow")
}
if !all.AgentWrite("foobar") {
t.Fatalf("should allow")
}
if !all.EventRead("foobar") {
t.Fatalf("should allow")
}
@ -99,6 +105,12 @@ func TestStaticACL(t *testing.T) {
if none.ACLModify() {
t.Fatalf("should not allow")
}
if none.AgentRead("foobar") {
t.Fatalf("should not allow")
}
if none.AgentWrite("foobar") {
t.Fatalf("should not allow")
}
if none.EventRead("foobar") {
t.Fatalf("should not allow")
}
@ -163,6 +175,12 @@ func TestStaticACL(t *testing.T) {
if !manage.ACLModify() {
t.Fatalf("should allow")
}
if !manage.AgentRead("foobar") {
t.Fatalf("should allow")
}
if !manage.AgentWrite("foobar") {
t.Fatalf("should allow")
}
if !manage.EventRead("foobar") {
t.Fatalf("should allow")
}
@ -545,6 +563,89 @@ func TestPolicyACL_Parent(t *testing.T) {
}
}
func TestPolicyACL_Agent(t *testing.T) {
deny := DenyAll()
policyRoot := &Policy{
Agents: []*AgentPolicy{
&AgentPolicy{
Node: "root-nope",
Policy: PolicyDeny,
},
&AgentPolicy{
Node: "root-ro",
Policy: PolicyRead,
},
&AgentPolicy{
Node: "root-rw",
Policy: PolicyWrite,
},
&AgentPolicy{
Node: "override",
Policy: PolicyDeny,
},
},
}
root, err := New(deny, policyRoot)
if err != nil {
t.Fatalf("err: %v", err)
}
policy := &Policy{
Agents: []*AgentPolicy{
&AgentPolicy{
Node: "child-nope",
Policy: PolicyDeny,
},
&AgentPolicy{
Node: "child-ro",
Policy: PolicyRead,
},
&AgentPolicy{
Node: "child-rw",
Policy: PolicyWrite,
},
&AgentPolicy{
Node: "override",
Policy: PolicyWrite,
},
},
}
acl, err := New(root, policy)
if err != nil {
t.Fatalf("err: %v", err)
}
type agentcase struct {
inp string
read bool
write bool
}
cases := []agentcase{
{"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.AgentRead(c.inp) {
t.Fatalf("Read fail: %#v", c)
}
if c.write != acl.AgentWrite(c.inp) {
t.Fatalf("Write fail: %#v", c)
}
}
}
func TestPolicyACL_Keyring(t *testing.T) {
type keyringcase struct {
inp string

View File

@ -16,6 +16,7 @@ const (
// an ACL configuration.
type Policy struct {
ID string `hcl:"-"`
Agents []*AgentPolicy `hcl:"agent,expand"`
Keys []*KeyPolicy `hcl:"key,expand"`
Nodes []*NodePolicy `hcl:"node,expand"`
Services []*ServicePolicy `hcl:"service,expand"`
@ -26,6 +27,17 @@ type Policy struct {
Operator string `hcl:"operator"`
}
// AgentPolicy represents a policy for working with agent endpoints on nodes
// with specific name prefixes.
type AgentPolicy struct {
Node string `hcl:",key"`
Policy string
}
func (a *AgentPolicy) GoString() string {
return fmt.Sprintf("%#v", *a)
}
// KeyPolicy represents a policy for a key
type KeyPolicy struct {
Prefix string `hcl:",key"`
@ -116,6 +128,13 @@ func Parse(rules string) (*Policy, error) {
return nil, fmt.Errorf("Failed to parse ACL rules: %v", err)
}
// Validate the agent policy
for _, ap := range p.Agents {
if !isPolicyValid(ap.Policy) {
return nil, fmt.Errorf("Invalid agent policy: %#v", ap)
}
}
// Validate the key policy
for _, kp := range p.Keys {
if !isPolicyValid(kp.Policy) {

View File

@ -8,6 +8,12 @@ import (
func TestACLPolicy_Parse_HCL(t *testing.T) {
inp := `
agent "foo" {
policy = "read"
}
agent "bar" {
policy = "write"
}
event "" {
policy = "read"
}
@ -63,6 +69,16 @@ query "bar" {
}
`
exp := &Policy{
Agents: []*AgentPolicy{
&AgentPolicy{
Node: "foo",
Policy: PolicyRead,
},
&AgentPolicy{
Node: "bar",
Policy: PolicyWrite,
},
},
Events: []*EventPolicy{
&EventPolicy{
Event: "",
@ -159,6 +175,14 @@ query "bar" {
func TestACLPolicy_Parse_JSON(t *testing.T) {
inp := `{
"agent": {
"foo": {
"policy": "write"
},
"bar": {
"policy": "deny"
}
},
"event": {
"": {
"policy": "read"
@ -226,6 +250,16 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
}
}`
exp := &Policy{
Agents: []*AgentPolicy{
&AgentPolicy{
Node: "foo",
Policy: PolicyWrite,
},
&AgentPolicy{
Node: "bar",
Policy: PolicyDeny,
},
},
Events: []*EventPolicy{
&EventPolicy{
Event: "",
@ -358,6 +392,7 @@ operator = ""
func TestACLPolicy_Bad_Policy(t *testing.T) {
cases := []string{
`agent "" { policy = "nope" }`,
`event "" { policy = "nope" }`,
`key "" { policy = "nope" }`,
`keyring = "nope"`,