Merge pull request #762 from hashicorp/issue-732
Create a "default" policy with sensible rules.
This commit is contained in:
commit
201adad4ae
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -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,16 @@ 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. You can override this
|
||||
behavior when using manual token creation (i.e. not via an authentication
|
||||
backend) by setting the "no_default_policy" flag to true. [GH-732]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ type TokenCreateRequest struct {
|
|||
Lease string `json:"lease,omitempty"`
|
||||
TTL string `json:"ttl,omitempty"`
|
||||
NoParent bool `json:"no_parent,omitempty"`
|
||||
NoDefaultPolicy bool `json:"no_default_policy,omitempty"`
|
||||
DisplayName string `json:"display_name"`
|
||||
NumUses int `json:"num_uses"`
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ type TokenCreateCommand struct {
|
|||
func (c *TokenCreateCommand) Run(args []string) int {
|
||||
var format string
|
||||
var id, displayName, lease, ttl string
|
||||
var orphan bool
|
||||
var orphan, noDefaultPolicy bool
|
||||
var metadata map[string]string
|
||||
var numUses int
|
||||
var policies []string
|
||||
|
@ -28,6 +28,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||
flags.StringVar(&lease, "lease", "", "")
|
||||
flags.StringVar(&ttl, "ttl", "", "")
|
||||
flags.BoolVar(&orphan, "orphan", false, "")
|
||||
flags.BoolVar(&noDefaultPolicy, "no-default-policy", false, "")
|
||||
flags.IntVar(&numUses, "use-limit", 0, "")
|
||||
flags.Var((*kvFlag.Flag)(&metadata), "metadata", "")
|
||||
flags.Var((*sliceflag.StringFlag)(&policies), "policy", "")
|
||||
|
@ -60,6 +61,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||
Metadata: metadata,
|
||||
TTL: ttl,
|
||||
NoParent: orphan,
|
||||
NoDefaultPolicy: noDefaultPolicy,
|
||||
DisplayName: displayName,
|
||||
NumUses: numUses,
|
||||
})
|
||||
|
@ -122,6 +124,9 @@ Token Options:
|
|||
root tokens can create orphan tokens. This prevents
|
||||
the new token from being revoked with your token.
|
||||
|
||||
-no-default-policy If specified, the token will not have the "default"
|
||||
policy included in its policy set.
|
||||
|
||||
-policy="name" Policy to associate with this token. This can be
|
||||
specified multiple times.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
@ -1036,6 +1036,45 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Check that we handle excluding the default policy
|
||||
func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
|
||||
// Create a new credential
|
||||
req := logical.TestRequest(t, logical.WriteOperation, "auth/token/create")
|
||||
req.ClientToken = root
|
||||
req.Data["policies"] = []string{"foo"}
|
||||
req.Data["no_default_policy"] = true
|
||||
resp, err := c.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Ensure we got a new client token back
|
||||
clientToken := resp.Auth.ClientToken
|
||||
if clientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
// Check the policy and metadata
|
||||
te, err := c.tokenStore.Lookup(clientToken)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
expect := &TokenEntry{
|
||||
ID: clientToken,
|
||||
Parent: root,
|
||||
Policies: []string{"foo"},
|
||||
Path: "auth/token/create",
|
||||
DisplayName: "token",
|
||||
CreationTime: te.CreationTime,
|
||||
TTL: time.Hour * 24 * 30,
|
||||
}
|
||||
if !reflect.DeepEqual(te, expect) {
|
||||
t.Fatalf("Bad: %#v expect: %#v", te, expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_LimitedUseToken(t *testing.T) {
|
||||
c, _, root := TestCoreUnsealed(t)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("error fetching default policy from store: {{err}}", err)
|
||||
}
|
||||
if policy == nil {
|
||||
c.policyStore.createDefaultPolicy()
|
||||
err := c.policyStore.createDefaultPolicy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*/
|
||||
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)
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -551,6 +551,7 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
Policies []string
|
||||
Metadata map[string]string `mapstructure:"meta"`
|
||||
NoParent bool `mapstructure:"no_parent"`
|
||||
NoDefaultPolicy bool `mapstructure:"no_default_policy"`
|
||||
Lease string
|
||||
TTL string
|
||||
DisplayName string `mapstructure:"display_name"`
|
||||
|
@ -602,6 +603,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"}) && !data.NoDefaultPolicy {
|
||||
te.Policies = append(te.Policies, "default")
|
||||
}
|
||||
|
||||
// Only allow an orphan token if the client has sudo policy
|
||||
if data.NoParent {
|
||||
|
|
|
@ -888,7 +888,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||
|
||||
exp = map[string]interface{}{
|
||||
"id": "client",
|
||||
"policies": []string{"foo"},
|
||||
"policies": []string{"foo", "default"},
|
||||
"path": "auth/token/create",
|
||||
"meta": map[string]string(nil),
|
||||
"display_name": "token",
|
||||
|
|
|
@ -87,6 +87,12 @@ of the header should be "X-Vault-Token" and the value should be the token.
|
|||
If true and set by a root caller, the token will not have the
|
||||
parent token of the caller. This creates a token with no parent.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">no_default_profile</span>
|
||||
<span class="param-flags">optional</span>
|
||||
If true the `default` profile will not be a part of this token's
|
||||
policy set.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">lease</span>
|
||||
<span class="param-flags">optional</span>
|
||||
|
|
Loading…
Reference in New Issue