Allow globbing dis/allowed_policies_glob in token roles (#7277)

* Add allowed_policies_glob and disallowed_policies_glob that are the same as allowed_policies and disallowed_policies but allow glob matching.

* Update changelog, docs, tests, and comments for (dis)allowed_token_glob token role feature.

* Improve docs and unit tests for auth/token role policy globbing.
This commit is contained in:
Tiernan 2021-09-22 01:25:06 +10:00 committed by GitHub
parent 8a0250d277
commit a538936367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 468 additions and 124 deletions

3
changelog/7277.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
auth/token: Add `allowed_policies_glob` and `disallowed_policies_glob` fields to token roles to allow glob matching of policies
```

View File

@ -392,6 +392,16 @@ func (ts *TokenStore) paths() []*framework.Path {
Description: tokenDisallowedPoliciesHelp,
},
"allowed_policies_glob": {
Type: framework.TypeCommaStringSlice,
Description: tokenAllowedPoliciesGlobHelp,
},
"disallowed_policies_glob": {
Type: framework.TypeCommaStringSlice,
Description: tokenDisallowedPoliciesGlobHelp,
},
"orphan": {
Type: framework.TypeBool,
Description: tokenOrphanHelp,
@ -623,6 +633,12 @@ type tsRoleEntry struct {
// List of policies to be not allowed during token creation using this role
DisallowedPolicies []string `json:"disallowed_policies" mapstructure:"disallowed_policies" structs:"disallowed_policies"`
// An extension to AllowedPolicies that instead uses glob matching on policy names
AllowedPoliciesGlob []string `json:"allowed_policies_glob" mapstructure:"allowed_policies_glob" structs:"allowed_policies_glob"`
// An extension to DisallowedPolicies that instead uses glob matching on policy names
DisallowedPoliciesGlob []string `json:"disallowed_policies_glob" mapstructure:"disallowed_policies_glob" structs:"disallowed_policies_glob"`
// If true, tokens created using this role will be orphans
Orphan bool `json:"orphan" mapstructure:"orphan" structs:"orphan"`
@ -2475,7 +2491,8 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
// and shouldn't be added is kept because we want to do subset comparisons
// based on adding default when it's correct to do so.
switch {
case role != nil && (len(role.AllowedPolicies) > 0 || len(role.DisallowedPolicies) > 0):
case role != nil && (len(role.AllowedPolicies) > 0 || len(role.DisallowedPolicies) > 0 ||
len(role.AllowedPoliciesGlob) > 0 || len(role.DisallowedPoliciesGlob) > 0):
// Holds the final set of policies as they get munged
var finalPolicies []string
@ -2487,7 +2504,9 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
// isn't in the disallowed list, add it. This is in line with the idea
// that roles, when allowed/disallowed ar set, allow a subset of
// policies to be set disjoint from the parent token's policies.
if !data.NoDefaultPolicy && !role.TokenNoDefaultPolicy && !strutil.StrListContains(role.DisallowedPolicies, "default") {
if !data.NoDefaultPolicy && !role.TokenNoDefaultPolicy &&
!strutil.StrListContains(role.DisallowedPolicies, "default") &&
!strutil.StrListContainsGlob(role.DisallowedPoliciesGlob, "default") {
localAddDefault = true
}
@ -2496,12 +2515,12 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
finalPolicies = policyutil.SanitizePolicies(data.Policies, localAddDefault)
}
var sanitizedRolePolicies []string
var sanitizedRolePolicies, sanitizedRolePoliciesGlob []string
// First check allowed policies; if policies are specified they will be
// checked, otherwise if an allowed set exists that will be the set
// that is used
if len(role.AllowedPolicies) > 0 {
if len(role.AllowedPolicies) > 0 || len(role.AllowedPoliciesGlob) > 0 {
// Note that if "default" is already in allowed, and also in
// disallowed, this will still result in an error later since this
// doesn't strip out default
@ -2510,8 +2529,13 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
if len(finalPolicies) == 0 {
finalPolicies = sanitizedRolePolicies
} else {
if !strutil.StrListSubset(sanitizedRolePolicies, finalPolicies) {
return logical.ErrorResponse(fmt.Sprintf("token policies (%q) must be subset of the role's allowed policies (%q)", finalPolicies, sanitizedRolePolicies)), logical.ErrInvalidRequest
sanitizedRolePoliciesGlob = policyutil.SanitizePolicies(role.AllowedPoliciesGlob, false)
for _, finalPolicy := range finalPolicies {
if !strutil.StrListContains(sanitizedRolePolicies, finalPolicy) &&
!strutil.StrListContainsGlob(sanitizedRolePoliciesGlob, finalPolicy) {
return logical.ErrorResponse(fmt.Sprintf("token policies (%q) must be subset of the role's allowed policies (%q) or glob policies (%q)", finalPolicies, sanitizedRolePolicies, sanitizedRolePoliciesGlob)), logical.ErrInvalidRequest
}
}
}
} else {
@ -2522,12 +2546,14 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
}
}
if len(role.DisallowedPolicies) > 0 {
if len(role.DisallowedPolicies) > 0 || len(role.DisallowedPoliciesGlob) > 0 {
// We don't add the default here because we only want to disallow it if it's explicitly set
sanitizedRolePolicies = strutil.RemoveDuplicates(role.DisallowedPolicies, true)
sanitizedRolePoliciesGlob = strutil.RemoveDuplicates(role.DisallowedPoliciesGlob, true)
for _, finalPolicy := range finalPolicies {
if strutil.StrListContains(sanitizedRolePolicies, finalPolicy) {
if strutil.StrListContains(sanitizedRolePolicies, finalPolicy) ||
strutil.StrListContainsGlob(sanitizedRolePoliciesGlob, finalPolicy) {
return logical.ErrorResponse(fmt.Sprintf("token policy %q is disallowed by this role", finalPolicy)), logical.ErrInvalidRequest
}
}
@ -3183,18 +3209,20 @@ func (ts *TokenStore) tokenStoreRoleRead(ctx context.Context, req *logical.Reque
// TODO (1.4): Remove "period" and "explicit_max_ttl" if they're zero
resp := &logical.Response{
Data: map[string]interface{}{
"period": int64(role.Period.Seconds()),
"token_period": int64(role.TokenPeriod.Seconds()),
"explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()),
"token_explicit_max_ttl": int64(role.TokenExplicitMaxTTL.Seconds()),
"disallowed_policies": role.DisallowedPolicies,
"allowed_policies": role.AllowedPolicies,
"name": role.Name,
"orphan": role.Orphan,
"path_suffix": role.PathSuffix,
"renewable": role.Renewable,
"token_type": role.TokenType.String(),
"allowed_entity_aliases": role.AllowedEntityAliases,
"period": int64(role.Period.Seconds()),
"token_period": int64(role.TokenPeriod.Seconds()),
"explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()),
"token_explicit_max_ttl": int64(role.TokenExplicitMaxTTL.Seconds()),
"disallowed_policies": role.DisallowedPolicies,
"allowed_policies": role.AllowedPolicies,
"disallowed_policies_glob": role.DisallowedPoliciesGlob,
"allowed_policies_glob": role.AllowedPoliciesGlob,
"name": role.Name,
"orphan": role.Orphan,
"path_suffix": role.PathSuffix,
"renewable": role.Renewable,
"token_type": role.TokenType.String(),
"allowed_entity_aliases": role.AllowedEntityAliases,
},
}
@ -3292,6 +3320,20 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic
} else if req.Operation == logical.CreateOperation {
entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true)
}
allowedPoliciesGlobRaw, ok := data.GetOk("allowed_policies_glob")
if ok {
entry.AllowedPoliciesGlob = policyutil.SanitizePolicies(allowedPoliciesGlobRaw.([]string), policyutil.DoNotAddDefaultPolicy)
} else if req.Operation == logical.CreateOperation {
entry.AllowedPoliciesGlob = policyutil.SanitizePolicies(data.Get("allowed_policies_glob").([]string), policyutil.DoNotAddDefaultPolicy)
}
disallowedPoliciesGlobRaw, ok := data.GetOk("disallowed_policies_glob")
if ok {
entry.DisallowedPoliciesGlob = strutil.RemoveDuplicates(disallowedPoliciesGlobRaw.([]string), true)
} else if req.Operation == logical.CreateOperation {
entry.DisallowedPoliciesGlob = strutil.RemoveDuplicates(data.Get("disallowed_policies_glob").([]string), true)
}
}
// We handle token type a bit differently than tokenutil does so we need to
@ -3779,6 +3821,13 @@ calling token's policies. The parameter is a comma-delimited string of
policy names.`
tokenDisallowedPoliciesHelp = `If set, successful token creation via this role will require that
no policies in the given list are requested. The parameter is a comma-delimited string of policy names.`
tokenAllowedPoliciesGlobHelp = `If set, tokens can be created with any subset of glob matched policies in this
list, rather than the normal semantics of tokens being a subset of the
calling token's policies. The parameter is a comma-delimited string of
policy name globs.`
tokenDisallowedPoliciesGlobHelp = `If set, successful token creation via this role will require that
no requested policies glob match any of policies in this list.
The parameter is a comma-delimited string of policy name globs.`
tokenOrphanHelp = `If true, tokens created via this role
will be orphan tokens (have no parent)`
tokenPeriodHelp = `If set, tokens created via this role

View File

@ -3179,19 +3179,21 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
}
expected := map[string]interface{}{
"name": "test",
"orphan": true,
"token_period": int64(259200),
"period": int64(259200),
"allowed_policies": []string{"test1", "test2"},
"disallowed_policies": []string{},
"path_suffix": "happenin",
"explicit_max_ttl": int64(7200),
"token_explicit_max_ttl": int64(7200),
"renewable": true,
"token_type": "default-service",
"token_num_uses": 123,
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": true,
"token_period": int64(259200),
"period": int64(259200),
"allowed_policies": []string{"test1", "test2"},
"disallowed_policies": []string{},
"allowed_policies_glob": []string{},
"disallowed_policies_glob": []string{},
"path_suffix": "happenin",
"explicit_max_ttl": int64(7200),
"token_explicit_max_ttl": int64(7200),
"renewable": true,
"token_type": "default-service",
"token_num_uses": 123,
"allowed_entity_aliases": []string(nil),
}
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
@ -3240,18 +3242,20 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
}
expected = map[string]interface{}{
"name": "test",
"orphan": true,
"period": int64(284400),
"token_period": int64(284400),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"path_suffix": "happenin",
"token_explicit_max_ttl": int64(288000),
"explicit_max_ttl": int64(288000),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": true,
"period": int64(284400),
"token_period": int64(284400),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"allowed_policies_glob": []string{},
"disallowed_policies_glob": []string{},
"path_suffix": "happenin",
"token_explicit_max_ttl": int64(288000),
"explicit_max_ttl": int64(288000),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
@ -3290,18 +3294,20 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
}
expected = map[string]interface{}{
"name": "test",
"orphan": true,
"explicit_max_ttl": int64(5),
"token_explicit_max_ttl": int64(5),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"path_suffix": "happenin",
"period": int64(0),
"token_period": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": true,
"explicit_max_ttl": int64(5),
"token_explicit_max_ttl": int64(5),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"allowed_policies_glob": []string{},
"disallowed_policies_glob": []string{},
"path_suffix": "happenin",
"period": int64(0),
"token_period": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" {
@ -3340,18 +3346,20 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
}
expected = map[string]interface{}{
"name": "test",
"orphan": true,
"token_explicit_max_ttl": int64(5),
"explicit_max_ttl": int64(5),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"path_suffix": "",
"period": int64(0),
"token_period": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": true,
"token_explicit_max_ttl": int64(5),
"explicit_max_ttl": int64(5),
"allowed_policies": []string{"test3"},
"disallowed_policies": []string{},
"allowed_policies_glob": []string{},
"disallowed_policies_glob": []string{},
"path_suffix": "",
"period": int64(0),
"token_period": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
}
if diff := deep.Equal(expected, resp.Data); diff != nil {
@ -3495,6 +3503,17 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) {
t.Fatalf("err:%v resp:%v", err, resp)
}
// policy containing a glob character in the non-glob disallowed_policies field
req = logical.TestRequest(t, logical.UpdateOperation, "roles/testglobdisabled")
req.ClientToken = root
req.Data = map[string]interface{}{
"disallowed_policies": "test*",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%v", err, resp)
}
// Create a token that has all the policies defined above
req = logical.TestRequest(t, logical.UpdateOperation, "create")
req.ClientToken = root
@ -3508,6 +3527,7 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) {
}
parentToken := resp.Auth.ClientToken
// Test that the parent token's policies are rejected by disallowed_policies
req = logical.TestRequest(t, logical.UpdateOperation, "create/test1")
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
@ -3536,6 +3556,21 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) {
req.ClientToken = parentToken
testMakeTokenViaRequest(t, ts, req)
// Check to be sure 'test*' without globbing matches 'test*'
req = logical.TestRequest(t, logical.UpdateOperation, "create/testglobdisabled")
req.Data["policies"] = []string{"test*"}
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatalf("expected an error response, got %#v", resp)
}
// Check to be sure 'test*' without globbing doesn't match 'test1' or 'test'
req = logical.TestRequest(t, logical.UpdateOperation, "create/testglobdisabled")
req.Data["policies"] = []string{"test1", "test"}
req.ClientToken = parentToken
testMakeTokenViaRequest(t, ts, req)
// Create a role to have 'default' policy disallowed
req = logical.TestRequest(t, logical.UpdateOperation, "roles/default")
req.ClientToken = root
@ -3588,6 +3623,40 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) {
t.Fatalf("bad: %#v", resp)
}
// test not glob matching when using allowed_policies instead of allowed_policies_glob
req = logical.TestRequest(t, logical.UpdateOperation, "roles/testnoglob")
req.ClientToken = root
req.Data = map[string]interface{}{
"allowed_policies": "test*",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
if resp != nil {
t.Fatalf("expected a nil response")
}
req.Path = "create/testnoglob"
req.Data["policies"] = []string{"test"}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil {
t.Fatalf("expected error")
}
req.Data["policies"] = []string{"testfoo"}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil {
t.Fatalf("expected error")
}
req.Data["policies"] = []string{"test*"}
resp = testMakeTokenViaRequest(t, ts, req)
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: %#v", resp)
}
// When allowed_policies is blank, should fall back to a subset of the parent policies
req = logical.TestRequest(t, logical.UpdateOperation, "roles/test")
req.ClientToken = root
@ -3643,6 +3712,201 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) {
}
}
func TestTokenStore_RoleDisallowedPoliciesGlob(t *testing.T) {
var req *logical.Request
var resp *logical.Response
var err error
core, _, root := TestCoreUnsealed(t)
ts := core.tokenStore
ps := core.policyStore
// Create 4 different policies
policy, _ := ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy)
policy.Name = "test1"
if err := ps.SetPolicy(namespace.RootContext(nil), policy); err != nil {
t.Fatal(err)
}
policy, _ = ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy)
policy.Name = "test2"
if err := ps.SetPolicy(namespace.RootContext(nil), policy); err != nil {
t.Fatal(err)
}
policy, _ = ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy)
policy.Name = "test3"
if err := ps.SetPolicy(namespace.RootContext(nil), policy); err != nil {
t.Fatal(err)
}
policy, _ = ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy)
policy.Name = "test3b"
if err := ps.SetPolicy(namespace.RootContext(nil), policy); err != nil {
t.Fatal(err)
}
// Create roles with different disallowed_policies configuration
req = logical.TestRequest(t, logical.UpdateOperation, "roles/test1")
req.ClientToken = root
req.Data = map[string]interface{}{
"disallowed_policies_glob": "test1",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%v", err, resp)
}
req = logical.TestRequest(t, logical.UpdateOperation, "roles/testnot23")
req.ClientToken = root
req.Data = map[string]interface{}{
"disallowed_policies_glob": "test2,test3*",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%v", err, resp)
}
// Create a token that has all the policies defined above
req = logical.TestRequest(t, logical.UpdateOperation, "create")
req.ClientToken = root
req.Data["policies"] = []string{"test1", "test2", "test3", "test3b"}
resp = testMakeTokenViaRequest(t, ts, req)
if resp == nil || resp.Auth == nil {
t.Fatal("got nil response")
}
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: ClientToken; resp:%#v", resp)
}
parentToken := resp.Auth.ClientToken
// Test that the parent token's policies are rejected by disallowed_policies
req = logical.TestRequest(t, logical.UpdateOperation, "create/test1")
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatalf("expected an error response, got %#v", resp)
}
req = logical.TestRequest(t, logical.UpdateOperation, "create/testnot23")
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatalf("expected an error response, got %#v", resp)
}
// Disallowed should act as a blacklist so make sure we can still make
// something with other policies in the request
req = logical.TestRequest(t, logical.UpdateOperation, "create/test1")
req.Data["policies"] = []string{"foo", "bar"}
req.ClientToken = parentToken
testMakeTokenViaRequest(t, ts, req)
// Check to be sure 'test3*' matches 'test3'
req = logical.TestRequest(t, logical.UpdateOperation, "create/testnot23")
req.Data["policies"] = []string{"test3"}
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatalf("expected an error response, got %#v", resp)
}
// Check to be sure 'test3*' matches 'test3b'
req = logical.TestRequest(t, logical.UpdateOperation, "create/testnot23")
req.Data["policies"] = []string{"test3b"}
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatalf("expected an error response, got %#v", resp)
}
// Check that non-blacklisted policies still work
req = logical.TestRequest(t, logical.UpdateOperation, "create/testnot23")
req.Data["policies"] = []string{"test1"}
req.ClientToken = parentToken
testMakeTokenViaRequest(t, ts, req)
// Create a role to have 'default' policy disallowed
req = logical.TestRequest(t, logical.UpdateOperation, "roles/default")
req.ClientToken = root
req.Data = map[string]interface{}{
"disallowed_policies_glob": "default",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%v", err, resp)
}
req = logical.TestRequest(t, logical.UpdateOperation, "create/default")
req.ClientToken = parentToken
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil || resp != nil && !resp.IsError() {
t.Fatal("expected an error response")
}
}
func TestTokenStore_RoleAllowedPoliciesGlob(t *testing.T) {
c, _, root := TestCoreUnsealed(t)
ts := c.tokenStore
// test literal matching works in allowed_policies_glob
req := logical.TestRequest(t, logical.UpdateOperation, "roles/test")
req.ClientToken = root
req.Data = map[string]interface{}{
"allowed_policies_glob": "test1,test2",
}
resp, err := ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
if resp != nil {
t.Fatalf("expected a nil response")
}
req.Data = map[string]interface{}{}
req.Path = "create/test"
req.Data["policies"] = []string{"foo"}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil {
t.Fatalf("expected error")
}
req.Data["policies"] = []string{"test2"}
resp = testMakeTokenViaRequest(t, ts, req)
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: %#v", resp)
}
// test glob matching in allowed_policies_glob
req = logical.TestRequest(t, logical.UpdateOperation, "roles/test")
req.ClientToken = root
req.Data = map[string]interface{}{
"allowed_policies_glob": "test*",
}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err: %v\nresp: %#v", err, resp)
}
if resp != nil {
t.Fatalf("expected a nil response")
}
req.Path = "create/test"
req.Data["policies"] = []string{"footest"}
resp, err = ts.HandleRequest(namespace.RootContext(nil), req)
if err == nil {
t.Fatalf("expected error")
}
req.Data["policies"] = []string{"testfoo", "test2", "test"}
resp = testMakeTokenViaRequest(t, ts, req)
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: %#v", resp)
}
}
func TestTokenStore_RoleOrphan(t *testing.T) {
c, _, root := TestCoreUnsealed(t)
ts := c.tokenStore
@ -4150,18 +4414,20 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
}
expected := map[string]interface{}{
"name": "test",
"orphan": false,
"period": int64(1),
"token_period": int64(1),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(3600),
"explicit_max_ttl": int64(3600),
"renewable": false,
"token_type": "batch",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": false,
"period": int64(1),
"token_period": int64(1),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"allowed_policies_glob": []string(nil),
"disallowed_policies_glob": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(3600),
"explicit_max_ttl": int64(3600),
"renewable": false,
"token_type": "batch",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
@ -4203,18 +4469,20 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
}
expected := map[string]interface{}{
"name": "test",
"orphan": false,
"period": int64(5),
"token_period": int64(5),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(7200),
"explicit_max_ttl": int64(7200),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": false,
"period": int64(5),
"token_period": int64(5),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"allowed_policies_glob": []string(nil),
"disallowed_policies_glob": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(7200),
"explicit_max_ttl": int64(7200),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
@ -4255,18 +4523,20 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
}
expected := map[string]interface{}{
"name": "test",
"orphan": false,
"period": int64(0),
"token_period": int64(7),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(5200),
"explicit_max_ttl": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": false,
"period": int64(0),
"token_period": int64(7),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"allowed_policies_glob": []string(nil),
"disallowed_policies_glob": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(5200),
"explicit_max_ttl": int64(0),
"renewable": false,
"token_type": "default-service",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {
@ -4309,18 +4579,20 @@ func TestTokenStore_RoleTokenFields(t *testing.T) {
}
expected := map[string]interface{}{
"name": "test",
"orphan": false,
"period": int64(0),
"token_period": int64(5),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(7200),
"explicit_max_ttl": int64(0),
"renewable": false,
"token_type": "service",
"allowed_entity_aliases": []string(nil),
"name": "test",
"orphan": false,
"period": int64(0),
"token_period": int64(5),
"allowed_policies": []string(nil),
"disallowed_policies": []string(nil),
"allowed_policies_glob": []string(nil),
"disallowed_policies_glob": []string(nil),
"path_suffix": "",
"token_explicit_max_ttl": int64(7200),
"explicit_max_ttl": int64(0),
"renewable": false,
"token_type": "service",
"allowed_entity_aliases": []string(nil),
}
if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" {

View File

@ -73,7 +73,7 @@ during this call.
- `role_name` `(string: "")` The name of the token role.
- `policies` `(array: "")` A list of policies for the token. This must be a
subset of the policies belonging to the token making the request, unless
subset of the policies belonging to the token making the request, unless
the calling token is root or contains `sudo` capabilities to `auth/token/create`.
If not specified, defaults to all the policies of the calling token.
- `meta` `(map: {})` A map of string to string valued metadata. This is
@ -338,8 +338,8 @@ if there is a lease associated with it.
- `token` `(string: <required>)` - Token to renew. This can be part of the URL
or the body.
- `increment` `(string: "")` - An optional requested increment duration can be
provided. This increment may not be honored, for instance in the case of periodic tokens.
If not supplied, Vault will use the default TTL. This is specified as a numeric string
provided. This increment may not be honored, for instance in the case of periodic tokens.
If not supplied, Vault will use the default TTL. This is specified as a numeric string
with suffix like "30s" or "5m".
### Sample Payload
@ -389,8 +389,8 @@ possible only if there is a lease associated with it.
### Parameters
- `increment` `(string: "")` - An optional requested increment duration can be
provided. This increment may not be honored, for instance in the case of periodic tokens.
If not supplied, Vault will use the default TTL. This is specified as a numeric string
provided. This increment may not be honored, for instance in the case of periodic tokens.
If not supplied, Vault will use the default TTL. This is specified as a numeric string
with suffix like "30s" or "5m".
### Sample Payload
@ -627,6 +627,8 @@ $ curl \
],
"allowed_policies": [],
"disallowed_policies": [],
"allowed_policies_glob": [],
"disallowed_policies_glob": [],
"explicit_max_ttl": 0,
"name": "nomad",
"orphan": false,
@ -690,13 +692,31 @@ tokens created against a role to be revoked using the
tokens being a subset of the calling token's policies. The parameter is a
comma-delimited string of policy names. If at creation time
`no_default_policy` is not set and `"default"` is not contained in
`disallowed_policies`, the `"default"` policy will be added to the created
token automatically.
`disallowed_policies` or glob matched in `disallowed_policies_glob`,
the `"default"` policy will be added to the created token automatically.
- `disallowed_policies` `(list: [])` If set, successful token creation via
this role will require that no policies in the given list are requested. The
parameter is a comma-delimited string of policy names. Adding `"default"` to
this list will prevent `"default"` from being added automatically to created
tokens.
- `allowed_policies_glob` `(list: [])` If set, tokens can be created with any
subset of glob matched policies in this list, rather than the normal semantics
of tokens being a subset of the calling token's policies. The parameter is a
comma-delimited string of policy name globs. If at creation time
`no_default_policy` is not set and `"default"` is not contained in
`disallowed_policies` or glob matched in `disallowed_policies_glob`,
the `"default"` policy will be added to the created token automatically.
If combined with `allowed_policies` policies need to only match one of the two
lists to be permitted. Note that unlike `allowed_policies` the policies listed
in `allowed_policies_glob` will not be added to the token when no policies are
specified in the call to `/auth/token/create/:role_name`.
- `disallowed_policies_glob` `(list: [])` If set, successful token creation via
this role will require that no requested policies glob match any of policies in
this list. The parameter is a comma-delimited string of policy name globs.
Adding any glob that matches `"default"` to this list will prevent `"default"`
from being added automatically to created tokens.
If combined with `disallowed_policies` policies need to only match one of the
two lists to be blocked.
- `orphan` `(bool: false)` - If `true`, tokens created against this policy will
be orphan tokens (they will have no parent). As such, they will not be
automatically revoked by the revocation of any other token.