diff --git a/acl/acl.go b/acl/acl.go index 24b62d0b42..1a8a6d2bc8 100644 --- a/acl/acl.go +++ b/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() diff --git a/acl/policy.go b/acl/policy.go index 1b14b61ac6..0327b2ef2b 100644 --- a/acl/policy.go +++ b/acl/policy.go @@ -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 } diff --git a/consul/internal_endpoint.go b/consul/internal_endpoint.go index 639083416f..1806104f66 100644 --- a/consul/internal_endpoint.go +++ b/consul/internal_endpoint.go @@ -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