2015-03-18 19:17:03 +00:00
|
|
|
package vault
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-04-08 23:43:17 +00:00
|
|
|
"time"
|
2015-03-18 19:17:03 +00:00
|
|
|
|
2015-04-08 23:43:17 +00:00
|
|
|
"github.com/armon/go-metrics"
|
2015-11-06 22:27:15 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2015-04-06 23:41:48 +00:00
|
|
|
"github.com/hashicorp/golang-lru"
|
2016-05-16 20:11:33 +00:00
|
|
|
"github.com/hashicorp/vault/helper/strutil"
|
2015-03-18 19:17:03 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
|
|
|
)
|
|
|
|
|
2015-03-18 21:00:42 +00:00
|
|
|
const (
|
|
|
|
// policySubPath is the sub-path used for the policy store
|
|
|
|
// view. This is nested under the system view.
|
|
|
|
policySubPath = "policy/"
|
2015-04-06 23:41:48 +00:00
|
|
|
|
|
|
|
// policyCacheSize is the number of policies that are kept cached
|
|
|
|
policyCacheSize = 1024
|
2016-05-02 04:08:07 +00:00
|
|
|
|
2016-05-16 20:11:33 +00:00
|
|
|
// cubbyholeResponseWrappingPolicyName is the name of the fixed policy
|
2016-06-10 17:48:46 +00:00
|
|
|
cubbyholeResponseWrappingPolicyName = "response-wrapping"
|
2016-05-16 20:11:33 +00:00
|
|
|
|
2016-05-02 04:08:07 +00:00
|
|
|
// cubbyholeResponseWrappingPolicy is the policy that ensures cubbyhole
|
|
|
|
// response wrapping can always succeed
|
|
|
|
cubbyholeResponseWrappingPolicy = `
|
|
|
|
path "cubbyhole/response" {
|
|
|
|
capabilities = ["create", "read"]
|
|
|
|
}
|
|
|
|
`
|
2015-03-18 21:00:42 +00:00
|
|
|
)
|
|
|
|
|
2016-05-16 20:11:33 +00:00
|
|
|
var (
|
|
|
|
immutablePolicies = []string{
|
|
|
|
"root",
|
|
|
|
cubbyholeResponseWrappingPolicyName,
|
|
|
|
}
|
2016-07-25 02:27:41 +00:00
|
|
|
nonAssignablePolicies = []string{
|
|
|
|
cubbyholeResponseWrappingPolicyName,
|
|
|
|
}
|
2016-05-16 20:11:33 +00:00
|
|
|
)
|
|
|
|
|
2015-03-18 19:17:03 +00:00
|
|
|
// PolicyStore is used to provide durable storage of policy, and to
|
|
|
|
// manage ACLs associated with them.
|
|
|
|
type PolicyStore struct {
|
2015-03-18 21:00:42 +00:00
|
|
|
view *BarrierView
|
2016-01-07 14:26:08 +00:00
|
|
|
lru *lru.TwoQueueCache
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
|
|
|
|
2015-07-06 01:14:15 +00:00
|
|
|
// PolicyEntry is used to store a policy by name
|
|
|
|
type PolicyEntry struct {
|
|
|
|
Version int
|
|
|
|
Raw string
|
|
|
|
}
|
|
|
|
|
2015-03-18 19:17:03 +00:00
|
|
|
// NewPolicyStore creates a new PolicyStore that is backed
|
|
|
|
// using a given view. It used used to durable store and manage named policy.
|
2016-04-21 13:52:42 +00:00
|
|
|
func NewPolicyStore(view *BarrierView, system logical.SystemView) *PolicyStore {
|
2015-03-18 19:17:03 +00:00
|
|
|
p := &PolicyStore{
|
2015-03-18 21:00:42 +00:00
|
|
|
view: view,
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
2016-04-21 20:32:06 +00:00
|
|
|
if !system.CachingDisabled() {
|
2016-04-21 13:52:42 +00:00
|
|
|
cache, _ := lru.New2Q(policyCacheSize)
|
|
|
|
p.lru = cache
|
|
|
|
}
|
|
|
|
|
2015-03-18 19:17:03 +00:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2015-03-18 21:00:42 +00:00
|
|
|
// setupPolicyStore is used to initialize the policy store
|
|
|
|
// when the vault is being unsealed.
|
|
|
|
func (c *Core) setupPolicyStore() error {
|
|
|
|
// Create a sub-view
|
2015-09-04 20:58:12 +00:00
|
|
|
view := c.systemBarrierView.SubView(policySubPath)
|
2015-03-18 21:00:42 +00:00
|
|
|
|
|
|
|
// Create the policy store
|
2016-04-21 13:52:42 +00:00
|
|
|
c.policyStore = NewPolicyStore(view, &dynamicSystemView{core: c})
|
2015-11-06 16:52:26 +00:00
|
|
|
|
2015-11-06 22:27:15 +00:00
|
|
|
// Ensure that the default policy exists, and if not, create it
|
|
|
|
policy, err := c.policyStore.GetPolicy("default")
|
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("error fetching default policy from store: {{err}}", err)
|
|
|
|
}
|
|
|
|
if policy == nil {
|
|
|
|
err := c.policyStore.createDefaultPolicy()
|
2015-11-06 16:52:26 +00:00
|
|
|
if err != nil {
|
2015-11-06 22:27:15 +00:00
|
|
|
return err
|
2015-11-06 16:52:26 +00:00
|
|
|
}
|
2015-11-06 22:27:15 +00:00
|
|
|
}
|
2016-05-02 04:08:07 +00:00
|
|
|
|
|
|
|
// Ensure that the cubbyhole response wrapping policy exists
|
2016-05-16 20:11:33 +00:00
|
|
|
policy, err = c.policyStore.GetPolicy(cubbyholeResponseWrappingPolicyName)
|
2016-05-02 04:08:07 +00:00
|
|
|
if err != nil {
|
2016-07-25 17:29:57 +00:00
|
|
|
return errwrap.Wrapf("error fetching response-wrapping policy from store: {{err}}", err)
|
2016-05-02 04:08:07 +00:00
|
|
|
}
|
|
|
|
if policy == nil || policy.Raw != cubbyholeResponseWrappingPolicy {
|
|
|
|
err := c.policyStore.createCubbyholeResponseWrappingPolicy()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-18 21:00:42 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// teardownPolicyStore is used to reverse setupPolicyStore
|
|
|
|
// when the vault is being sealed.
|
|
|
|
func (c *Core) teardownPolicyStore() error {
|
2015-11-06 16:52:26 +00:00
|
|
|
c.policyStore = nil
|
2015-03-18 21:00:42 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-18 19:17:03 +00:00
|
|
|
// SetPolicy is used to create or update the given policy
|
|
|
|
func (ps *PolicyStore) SetPolicy(p *Policy) error {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"policy", "set_policy"}, time.Now())
|
2015-03-18 19:17:03 +00:00
|
|
|
if p.Name == "" {
|
|
|
|
return fmt.Errorf("policy name missing")
|
|
|
|
}
|
2016-07-25 13:46:10 +00:00
|
|
|
if strutil.StrListContains(immutablePolicies, p.Name) {
|
2016-05-16 20:11:33 +00:00
|
|
|
return fmt.Errorf("cannot update %s policy", p.Name)
|
|
|
|
}
|
2015-03-18 19:17:03 +00:00
|
|
|
|
2016-05-02 04:08:07 +00:00
|
|
|
return ps.setPolicyInternal(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ps *PolicyStore) setPolicyInternal(p *Policy) error {
|
2015-07-06 01:14:15 +00:00
|
|
|
// Create the entry
|
|
|
|
entry, err := logical.StorageEntryJSON(p.Name, &PolicyEntry{
|
|
|
|
Version: 2,
|
|
|
|
Raw: p.Raw,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create entry: %v", err)
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
|
|
|
if err := ps.view.Put(entry); err != nil {
|
|
|
|
return fmt.Errorf("failed to persist policy: %v", err)
|
|
|
|
}
|
2015-04-06 23:41:48 +00:00
|
|
|
|
2016-04-22 13:41:32 +00:00
|
|
|
if ps.lru != nil {
|
2016-04-21 13:52:42 +00:00
|
|
|
// Update the LRU cache
|
|
|
|
ps.lru.Add(p.Name, p)
|
|
|
|
}
|
2015-03-18 19:17:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPolicy is used to fetch the named policy
|
|
|
|
func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"policy", "get_policy"}, time.Now())
|
2016-04-22 13:41:32 +00:00
|
|
|
if ps.lru != nil {
|
2016-04-21 13:52:42 +00:00
|
|
|
// Check for cached policy
|
|
|
|
if raw, ok := ps.lru.Get(name); ok {
|
|
|
|
return raw.(*Policy), nil
|
|
|
|
}
|
2015-04-06 23:41:48 +00:00
|
|
|
}
|
2015-03-24 18:27:21 +00:00
|
|
|
|
|
|
|
// Special case the root policy
|
|
|
|
if name == "root" {
|
|
|
|
p := &Policy{Name: "root"}
|
2016-04-22 13:41:32 +00:00
|
|
|
if ps.lru != nil {
|
2016-04-21 13:52:42 +00:00
|
|
|
ps.lru.Add(p.Name, p)
|
|
|
|
}
|
2015-03-24 18:27:21 +00:00
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2015-03-18 19:17:03 +00:00
|
|
|
// Load the policy in
|
|
|
|
out, err := ps.view.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to read policy: %v", err)
|
|
|
|
}
|
|
|
|
if out == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-07-06 01:14:15 +00:00
|
|
|
// In Vault 0.1.X we stored the raw policy, but in
|
|
|
|
// Vault 0.2 we switch to the PolicyEntry
|
|
|
|
policyEntry := new(PolicyEntry)
|
|
|
|
var policy *Policy
|
|
|
|
if err := out.DecodeJSON(policyEntry); err == nil {
|
|
|
|
// Parse normally
|
|
|
|
p, err := Parse(policyEntry.Raw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parse policy: %v", err)
|
|
|
|
}
|
|
|
|
p.Name = name
|
|
|
|
policy = p
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// On error, attempt to use V1 parsing
|
|
|
|
p, err := Parse(string(out.Value))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to parse policy: %v", err)
|
|
|
|
}
|
|
|
|
p.Name = name
|
|
|
|
|
|
|
|
// V1 used implicit glob, we need to do a fix-up
|
|
|
|
for _, pp := range p.Paths {
|
|
|
|
pp.Glob = true
|
|
|
|
}
|
|
|
|
policy = p
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
2015-04-06 23:43:05 +00:00
|
|
|
|
2016-04-22 13:41:32 +00:00
|
|
|
if ps.lru != nil {
|
2016-04-21 13:52:42 +00:00
|
|
|
// Update the LRU cache
|
|
|
|
ps.lru.Add(name, policy)
|
|
|
|
}
|
|
|
|
|
2015-07-06 01:14:15 +00:00
|
|
|
return policy, nil
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListPolicies is used to list the available policies
|
|
|
|
func (ps *PolicyStore) ListPolicies() ([]string, error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"policy", "list_policies"}, time.Now())
|
2015-03-18 19:17:03 +00:00
|
|
|
// Scan the view, since the policy names are the same as the
|
|
|
|
// key names.
|
2016-07-25 02:35:54 +00:00
|
|
|
keys, err := CollectKeys(ps.view)
|
2016-07-25 02:27:41 +00:00
|
|
|
|
|
|
|
for _, nonAssignable := range nonAssignablePolicies {
|
|
|
|
deleteIndex := -1
|
|
|
|
//Find indices of non-assignable policies in keys
|
|
|
|
for index, key := range keys {
|
|
|
|
if key == nonAssignable {
|
|
|
|
// Delete collection outside the loop
|
|
|
|
deleteIndex = index
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Remove non-assignable policies when found
|
|
|
|
if deleteIndex != -1 {
|
|
|
|
keys = append(keys[:deleteIndex], keys[deleteIndex+1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys, err
|
2015-03-18 19:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DeletePolicy is used to delete the named policy
|
|
|
|
func (ps *PolicyStore) DeletePolicy(name string) error {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"policy", "delete_policy"}, time.Now())
|
2016-07-25 13:46:10 +00:00
|
|
|
if strutil.StrListContains(immutablePolicies, name) {
|
2016-05-16 20:11:33 +00:00
|
|
|
return fmt.Errorf("cannot delete %s policy", name)
|
2015-03-24 18:27:21 +00:00
|
|
|
}
|
2015-11-06 22:27:15 +00:00
|
|
|
if name == "default" {
|
|
|
|
return fmt.Errorf("cannot delete default policy")
|
|
|
|
}
|
2015-03-18 19:17:03 +00:00
|
|
|
if err := ps.view.Delete(name); err != nil {
|
|
|
|
return fmt.Errorf("failed to delete policy: %v", err)
|
|
|
|
}
|
2015-04-06 23:41:48 +00:00
|
|
|
|
2016-04-22 13:41:32 +00:00
|
|
|
if ps.lru != nil {
|
2016-04-21 13:52:42 +00:00
|
|
|
// Clear the cache
|
|
|
|
ps.lru.Remove(name)
|
|
|
|
}
|
2015-03-18 19:17:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ACL is used to return an ACL which is built using the
|
|
|
|
// named policies.
|
|
|
|
func (ps *PolicyStore) ACL(names ...string) (*ACL, error) {
|
|
|
|
// Fetch the policies
|
|
|
|
var policy []*Policy
|
|
|
|
for _, name := range names {
|
|
|
|
p, err := ps.GetPolicy(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get policy '%s': %v", name, err)
|
|
|
|
}
|
|
|
|
policy = append(policy, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the ACL
|
|
|
|
acl, err := NewACL(policy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to construct ACL: %v", err)
|
|
|
|
}
|
|
|
|
return acl, nil
|
|
|
|
}
|
2015-11-06 16:52:26 +00:00
|
|
|
|
|
|
|
func (ps *PolicyStore) createDefaultPolicy() error {
|
2015-11-06 22:27:15 +00:00
|
|
|
policy, err := Parse(`
|
|
|
|
path "auth/token/lookup-self" {
|
2016-01-16 23:02:31 +00:00
|
|
|
capabilities = ["read"]
|
2015-11-06 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
path "auth/token/renew-self" {
|
2016-01-16 23:02:31 +00:00
|
|
|
capabilities = ["update"]
|
2015-11-06 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
path "auth/token/revoke-self" {
|
2016-01-16 23:02:31 +00:00
|
|
|
capabilities = ["update"]
|
|
|
|
}
|
|
|
|
|
|
|
|
path "cubbyhole/*" {
|
|
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
2015-11-06 22:27:15 +00:00
|
|
|
}
|
2016-02-03 18:50:47 +00:00
|
|
|
|
|
|
|
path "cubbyhole" {
|
|
|
|
capabilities = ["list"]
|
|
|
|
}
|
2015-11-06 22:27:15 +00:00
|
|
|
`)
|
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("error parsing default policy: {{err}}", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if policy == nil {
|
|
|
|
return fmt.Errorf("parsing default policy resulted in nil policy")
|
|
|
|
}
|
|
|
|
|
|
|
|
policy.Name = "default"
|
2016-05-02 04:08:07 +00:00
|
|
|
return ps.setPolicyInternal(policy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ps *PolicyStore) createCubbyholeResponseWrappingPolicy() error {
|
|
|
|
policy, err := Parse(cubbyholeResponseWrappingPolicy)
|
|
|
|
if err != nil {
|
2016-05-16 20:11:33 +00:00
|
|
|
return errwrap.Wrapf(fmt.Sprintf("error parsing %s policy: {{err}}", cubbyholeResponseWrappingPolicyName), err)
|
2016-05-02 04:08:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if policy == nil {
|
2016-05-16 20:11:33 +00:00
|
|
|
return fmt.Errorf("parsing %s policy resulted in nil policy", cubbyholeResponseWrappingPolicyName)
|
2016-05-02 04:08:07 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 20:11:33 +00:00
|
|
|
policy.Name = cubbyholeResponseWrappingPolicyName
|
2016-05-02 04:08:07 +00:00
|
|
|
return ps.setPolicyInternal(policy)
|
2015-11-06 16:52:26 +00:00
|
|
|
}
|