acl: initial pass at keyring ACLs

This commit is contained in:
Ryan Uber 2015-07-06 18:28:09 -06:00
parent 503fa1eed1
commit 4ef6545583
3 changed files with 106 additions and 0 deletions

View File

@ -58,6 +58,13 @@ type ACL interface {
// EventWrite determines if a specific event may be fired. // EventWrite determines if a specific event may be fired.
EventWrite(string) bool EventWrite(string) bool
// KeyringRead determines if the encryption keyring used in
// the gossip layer can be read.
KeyringRead() bool
// KeyringWrite determines if the keyring can be manipulated
KeyringWrite() bool
// ACLList checks for permission to list all the ACLs // ACLList checks for permission to list all the ACLs
ACLList() bool ACLList() bool
@ -101,6 +108,14 @@ func (s *StaticACL) EventWrite(string) bool {
return s.defaultAllow return s.defaultAllow
} }
func (s *StaticACL) KeyringRead() bool {
return s.defaultAllow
}
func (s *StaticACL) KeyringWrite() bool {
return s.defaultAllow
}
func (s *StaticACL) ACLList() bool { func (s *StaticACL) ACLList() bool {
return s.allowManage return s.allowManage
} }
@ -153,6 +168,11 @@ type PolicyACL struct {
// eventRules contains the user event policies // eventRules contains the user event policies
eventRules *radix.Tree eventRules *radix.Tree
// keyringRules contains the keyring policies. The keyring has
// a very simple yes/no without prefix mathing, so here we
// don't need to use a radix tree.
keyringRules map[string]struct{}
} }
// New is used to construct a policy based ACL from a set of policies // New is used to construct a policy based ACL from a set of policies
@ -163,6 +183,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
keyRules: radix.New(), keyRules: radix.New(),
serviceRules: radix.New(), serviceRules: radix.New(),
eventRules: radix.New(), eventRules: radix.New(),
keyringRules: make(map[string]struct{}),
} }
// Load the key policy // Load the key policy
@ -180,6 +201,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
p.eventRules.Insert(ep.Event, ep.Policy) p.eventRules.Insert(ep.Event, ep.Policy)
} }
// Load the keyring policy
for _, krp := range policy.Keyring {
p.keyringRules[krp.Policy] = struct{}{}
}
return p, nil return p, nil
} }
@ -321,6 +347,34 @@ func (p *PolicyACL) EventWrite(name string) bool {
return p.parent.EventWrite(name) return p.parent.EventWrite(name)
} }
// KeyringRead is used to determine if the keyring can be
// read by the current ACL token.
func (p *PolicyACL) KeyringRead() bool {
// First check for an explicit deny
if _, ok := p.keyringRules[KeyringPolicyDeny]; ok {
return false
}
// Now check for read or write. Write implies read.
_, ok := p.keyringRules[KeyringPolicyRead]
if !ok {
_, ok = p.keyringRules[KeyringPolicyWrite]
}
return ok
}
// KeyringWrite determines if the keyring can be manipulated.
func (p *PolicyACL) KeyringWrite() bool {
// First check for an explicit deny
if _, ok := p.keyringRules[KeyringPolicyDeny]; ok {
return false
}
// Check for read permission
_, ok := p.keyringRules[KeyringPolicyWrite]
return ok
}
// ACLList checks if listing of ACLs is allowed // ACLList checks if listing of ACLs is allowed
func (p *PolicyACL) ACLList() bool { func (p *PolicyACL) ACLList() bool {
return p.parent.ACLList() return p.parent.ACLList()

View File

@ -16,6 +16,9 @@ const (
EventPolicyRead = "read" EventPolicyRead = "read"
EventPolicyWrite = "write" EventPolicyWrite = "write"
EventPolicyDeny = "deny" EventPolicyDeny = "deny"
KeyringPolicyWrite = "write"
KeyringPolicyRead = "read"
KeyringPolicyDeny = "deny"
) )
// Policy is used to represent the policy specified by // Policy is used to represent the policy specified by
@ -25,6 +28,7 @@ type Policy struct {
Keys []*KeyPolicy `hcl:"key,expand"` Keys []*KeyPolicy `hcl:"key,expand"`
Services []*ServicePolicy `hcl:"service,expand"` Services []*ServicePolicy `hcl:"service,expand"`
Events []*EventPolicy `hcl:"event,expand"` Events []*EventPolicy `hcl:"event,expand"`
Keyring []*KeyringPolicy `hcl:"keyring"`
} }
// KeyPolicy represents a policy for a key // KeyPolicy represents a policy for a key
@ -57,6 +61,17 @@ func (e *EventPolicy) GoString() string {
return fmt.Sprintf("%#v", *e) return fmt.Sprintf("%#v", *e)
} }
// KeyringPolicy represents a policy for the encryption keyring.
type KeyringPolicy struct {
// We only need a single field for the keyring, since access
// is binary (allowed or disallowed) and no prefix is respected.
Policy string
}
func (k *KeyringPolicy) GoString() string {
return fmt.Sprintf("%#v", *k)
}
// Parse is used to parse the specified ACL rules into an // Parse is used to parse the specified ACL rules into an
// intermediary set of policies, before being compiled into // intermediary set of policies, before being compiled into
// the ACL // the ACL
@ -105,5 +120,16 @@ func Parse(rules string) (*Policy, error) {
} }
} }
// Validate the keyring policy
for _, krp := range p.Keyring {
switch krp.Policy {
case KeyringPolicyRead:
case KeyringPolicyWrite:
case KeyringPolicyDeny:
default:
return nil, fmt.Errorf("Invalid keyring policy: %#v", krp)
}
}
return p, nil return p, nil
} }

View File

@ -1,6 +1,8 @@
package consul package consul
import ( import (
"fmt"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/serf/serf" "github.com/hashicorp/serf/serf"
) )
@ -80,6 +82,30 @@ func (m *Internal) KeyringOperation(
args *structs.KeyringRequest, args *structs.KeyringRequest,
reply *structs.KeyringResponses) error { reply *structs.KeyringResponses) error {
// Check ACLs
acl, err := m.srv.resolveToken(args.Token)
if err != nil {
return err
}
if acl != nil {
switch args.Operation {
case structs.KeyringList:
if !acl.KeyringRead() {
return fmt.Errorf("Reading keyring denied by ACLs")
}
case structs.KeyringInstall:
fallthrough
case structs.KeyringUse:
fallthrough
case structs.KeyringRemove:
if !acl.KeyringWrite() {
return fmt.Errorf("Modifying keyring denied due to ACLs")
}
default:
panic("Invalid keyring operation")
}
}
// Only perform WAN keyring querying and RPC forwarding once // Only perform WAN keyring querying and RPC forwarding once
if !args.Forwarded { if !args.Forwarded {
args.Forwarded = true args.Forwarded = true