mirror of https://github.com/status-im/consul.git
acl: initial pass at keyring ACLs
This commit is contained in:
parent
e37b5ecb69
commit
1b8051a783
54
acl/acl.go
54
acl/acl.go
|
@ -58,6 +58,13 @@ type ACL interface {
|
|||
// EventWrite determines if a specific event may be fired.
|
||||
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() bool
|
||||
|
||||
|
@ -101,6 +108,14 @@ func (s *StaticACL) EventWrite(string) bool {
|
|||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) KeyringRead() bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) KeyringWrite() bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) ACLList() bool {
|
||||
return s.allowManage
|
||||
}
|
||||
|
@ -153,6 +168,11 @@ type PolicyACL struct {
|
|||
|
||||
// eventRules contains the user event policies
|
||||
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
|
||||
|
@ -163,6 +183,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
keyRules: radix.New(),
|
||||
serviceRules: radix.New(),
|
||||
eventRules: radix.New(),
|
||||
keyringRules: make(map[string]struct{}),
|
||||
}
|
||||
|
||||
// Load the key policy
|
||||
|
@ -180,6 +201,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
p.eventRules.Insert(ep.Event, ep.Policy)
|
||||
}
|
||||
|
||||
// Load the keyring policy
|
||||
for _, krp := range policy.Keyring {
|
||||
p.keyringRules[krp.Policy] = struct{}{}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -321,6 +347,34 @@ func (p *PolicyACL) EventWrite(name string) bool {
|
|||
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
|
||||
func (p *PolicyACL) ACLList() bool {
|
||||
return p.parent.ACLList()
|
||||
|
|
|
@ -16,6 +16,9 @@ const (
|
|||
EventPolicyRead = "read"
|
||||
EventPolicyWrite = "write"
|
||||
EventPolicyDeny = "deny"
|
||||
KeyringPolicyWrite = "write"
|
||||
KeyringPolicyRead = "read"
|
||||
KeyringPolicyDeny = "deny"
|
||||
)
|
||||
|
||||
// Policy is used to represent the policy specified by
|
||||
|
@ -25,6 +28,7 @@ type Policy struct {
|
|||
Keys []*KeyPolicy `hcl:"key,expand"`
|
||||
Services []*ServicePolicy `hcl:"service,expand"`
|
||||
Events []*EventPolicy `hcl:"event,expand"`
|
||||
Keyring []*KeyringPolicy `hcl:"keyring"`
|
||||
}
|
||||
|
||||
// KeyPolicy represents a policy for a key
|
||||
|
@ -57,6 +61,17 @@ func (e *EventPolicy) GoString() string {
|
|||
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
|
||||
// intermediary set of policies, before being compiled into
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/consul/structs"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
@ -80,6 +82,30 @@ func (m *Internal) KeyringOperation(
|
|||
args *structs.KeyringRequest,
|
||||
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
|
||||
if !args.Forwarded {
|
||||
args.Forwarded = true
|
||||
|
|
Loading…
Reference in New Issue