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

@ -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.