Create a "default" policy with sensible rules.

It is forced to be included with each token, but can be changed (but not
deleted).

Fixes #732
This commit is contained in:
Jeff Mitchell 2015-11-06 17:27:15 -05:00
parent 1a621b7000
commit d6693129de
7 changed files with 63 additions and 20 deletions

View File

@ -8,6 +8,9 @@ DEPRECATIONS/BREAKING CHANGES:
* Default etcd port number: the default connection string for the `etcd`
physical store uses port 2379 instead of port 4001, which is the port used
by the supported version 2.x of etcd. [GH-753]
* As noted below in the FEATURES section, if your Vault installation contains
a policy called `default`, new tokens created will inherit this policy
automatically.
FEATURES:
@ -15,6 +18,14 @@ FEATURES:
supports pushing CRLs into the mount and using the contained serial numbers
for revocation checking. See the documentation for the `cert` backend for
more info. [GH-330]
* **Default Policy**: Vault now ensures that a policy named `default` is added
to every token. This policy cannot be deleted, but it can be modified
(including to an empty policy). There are three endpoints allowed in the
default `default` policy, related to token self-management: `lookup-self`,
which allows a token to retrieve its own information, and `revoke-self` and
`renew-self`, which are self-explanatory. If your existing Vault
installation contains a policy called `default`, it will not be overridden,
but it will be added to each new token created. [GH-732]
IMPROVEMENTS:

View File

@ -17,7 +17,7 @@ func TestSysPolicies(t *testing.T) {
var actual map[string]interface{}
expected := map[string]interface{}{
"policies": []interface{}{"root"},
"policies": []interface{}{"default", "root"},
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -61,7 +61,7 @@ func TestSysWritePolicy(t *testing.T) {
var actual map[string]interface{}
expected := map[string]interface{}{
"policies": []interface{}{"foo", "root"},
"policies": []interface{}{"default", "foo", "root"},
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -88,7 +88,7 @@ func TestSysDeletePolicy(t *testing.T) {
var actual map[string]interface{}
expected := map[string]interface{}{
"policies": []interface{}{"root"},
"policies": []interface{}{"default", "root"},
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)

View File

@ -648,6 +648,10 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, *log
TTL: auth.TTL,
}
if !strListSubset(te.Policies, []string{"root"}) {
te.Policies = append(te.Policies, "default")
}
if err := c.tokenStore.create(&te); err != nil {
c.logger.Printf("[ERR] core: failed to create token: %v", err)
return nil, auth, ErrInternalError

View File

@ -5,8 +5,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/uuid"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/physical"
)
@ -821,7 +821,7 @@ func TestCore_HandleLogin_Token(t *testing.T) {
expect := &TokenEntry{
ID: clientToken,
Parent: "",
Policies: []string{"foo", "bar"},
Policies: []string{"foo", "bar", "default"},
Path: "auth/foo/login",
Meta: map[string]string{
"user": "armon",
@ -1020,7 +1020,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) {
expect := &TokenEntry{
ID: clientToken,
Parent: root,
Policies: []string{"foo"},
Policies: []string{"foo", "default"},
Path: "auth/token/create",
DisplayName: "token",
CreationTime: te.CreationTime,

View File

@ -430,7 +430,7 @@ func TestSystemBackend_policyList(t *testing.T) {
}
exp := map[string]interface{}{
"keys": []string{"root"},
"keys": []string{"default", "root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
@ -482,7 +482,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) {
}
exp = map[string]interface{}{
"keys": []string{"foo", "root"},
"keys": []string{"default", "foo", "root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)
@ -516,7 +516,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) {
}
exp = map[string]interface{}{
"keys": []string{"root"},
"keys": []string{"default", "root"},
}
if !reflect.DeepEqual(resp.Data, exp) {
t.Fatalf("got: %#v expect: %#v", resp.Data, exp)

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/golang-lru"
"github.com/hashicorp/vault/logical"
)
@ -51,16 +52,17 @@ func (c *Core) setupPolicyStore() error {
// Create the policy store
c.policyStore = NewPolicyStore(view)
/*
// Ensure that the default policy exists, and if not, create it
policy, err := c.policyStore.GetPolicy("default")
// 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()
if err != nil {
return errwrap.Wrapf("error fetching default policy from store: {{err}}", err)
return err
}
if policy == nil {
c.policyStore.createDefaultPolicy()
}
*/
}
return nil
}
@ -169,6 +171,9 @@ func (ps *PolicyStore) DeletePolicy(name string) error {
if name == "root" {
return fmt.Errorf("cannot delete root policy")
}
if name == "default" {
return fmt.Errorf("cannot delete default policy")
}
if err := ps.view.Delete(name); err != nil {
return fmt.Errorf("failed to delete policy: %v", err)
}
@ -199,8 +204,28 @@ func (ps *PolicyStore) ACL(names ...string) (*ACL, error) {
return acl, nil
}
/*
func (ps *PolicyStore) createDefaultPolicy() error {
return nil
policy, err := Parse(`
path "auth/token/lookup-self" {
policy = "read"
}
path "auth/token/renew-self" {
policy = "write"
}
path "auth/token/revoke-self" {
policy = "write"
}
`)
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"
return ps.SetPolicy(policy)
}
*/

View File

@ -602,6 +602,9 @@ func (ts *TokenStore) handleCreateCommon(
return logical.ErrorResponse("child policies must be subset of parent"), logical.ErrInvalidRequest
}
te.Policies = data.Policies
if !strListSubset(te.Policies, []string{"root"}) {
te.Policies = append(te.Policies, "default")
}
// Only allow an orphan token if the client has sudo policy
if data.NoParent {