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:
parent
8a0250d277
commit
a538936367
|
@ -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
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue