Merge branch 'master-oss' into cubbyhole-the-world
This commit is contained in:
commit
ce5614bf9b
|
@ -46,6 +46,12 @@ FEATURES:
|
|||
standby nodes are `standby.vault.service.consul`. Sealed vaults are marked
|
||||
critical and are not listed by default in Consul's service discovery. See
|
||||
the documentation for details. [GH-1349]
|
||||
* **Explicit Maximum Token TTLs using Token Roles**: If using token roles, you
|
||||
can now set explicit maximum TTLs on tokens that do not honor changes in the
|
||||
system- or mount-set values. This is useful, for instance, when the max TTL
|
||||
of the system or the `auth/token` mount must be set high to accommodate
|
||||
certain needs but you want more granular restrictions on tokens being issued
|
||||
directly from `auth/token`. [GH-1399]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
|
|||
"ttl": float64(0),
|
||||
"creation_ttl": float64(0),
|
||||
"role": "",
|
||||
"explicit_max_ttl": float64(0),
|
||||
},
|
||||
"warnings": nilWarnings,
|
||||
"wrap_info": nil,
|
||||
|
|
|
@ -303,6 +303,7 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
|
|||
"ttl": float64(0),
|
||||
"path": "auth/token/root",
|
||||
"role": "",
|
||||
"explicit_max_ttl": float64(0),
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||
|
@ -385,6 +386,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
|||
"ttl": float64(0),
|
||||
"path": "auth/token/root",
|
||||
"role": "",
|
||||
"explicit_max_ttl": float64(0),
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/helper/policyutil"
|
||||
"github.com/hashicorp/vault/helper/salt"
|
||||
"github.com/hashicorp/vault/helper/strutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
|
@ -154,6 +154,12 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error)
|
|||
Default: "",
|
||||
Description: tokenPathSuffixHelp + pathSuffixSanitize.String(),
|
||||
},
|
||||
|
||||
"explicit_max_ttl": &framework.FieldSchema{
|
||||
Type: framework.TypeDurationSecond,
|
||||
Default: 0,
|
||||
Description: tokenExplicitMaxTTLHelp,
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
@ -418,6 +424,7 @@ type TokenEntry struct {
|
|||
NumUses int // Used to restrict the number of uses (zero is unlimited). This is to support one-time-tokens (generalized).
|
||||
CreationTime int64 // Time of token creation
|
||||
TTL time.Duration // Duration set when token was created
|
||||
ExplicitMaxTTL time.Duration // Explicit maximum TTL on the token
|
||||
Role string // If set, the role that was used for parameters at creation time
|
||||
}
|
||||
|
||||
|
@ -438,8 +445,12 @@ type tsRoleEntry struct {
|
|||
Period time.Duration `json:"period" mapstructure:"period" structs:"period"`
|
||||
|
||||
// If set, a suffix will be set on the token path, making it easier to
|
||||
// revoke using 'revoke-prefix'.
|
||||
// revoke using 'revoke-prefix'
|
||||
PathSuffix string `json:"path_suffix" mapstructure:"path_suffix" structs:"path_suffix"`
|
||||
|
||||
// If set, the token entry will have an explicit maximum TTL set, rather
|
||||
// than deferring to role/mount values
|
||||
ExplicitMaxTTL time.Duration `json:"explicit_max_ttl" mapstructure:"explicit_max_ttl" structs:"explicit_max_ttl"`
|
||||
}
|
||||
|
||||
// SetExpirationManager is used to provide the token store with
|
||||
|
@ -991,8 +1002,12 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
if len(data.Policies) == 0 {
|
||||
data.Policies = role.AllowedPolicies
|
||||
} else {
|
||||
if !strutil.StrListSubset(role.AllowedPolicies, data.Policies) {
|
||||
return logical.ErrorResponse("token policies must be subset of the role's allowed policies"), logical.ErrInvalidRequest
|
||||
// Sanitize passed-in and role policies before comparison
|
||||
sanitizedInputPolicies := policyutil.SanitizePolicies(data.Policies)
|
||||
sanitizedRolePolicies := policyutil.SanitizePolicies(role.AllowedPolicies)
|
||||
|
||||
if !strutil.StrListSubset(sanitizedRolePolicies, sanitizedInputPolicies) {
|
||||
return logical.ErrorResponse(fmt.Sprintf("token policies (%v) must be subset of the role's allowed policies (%v)", sanitizedInputPolicies, sanitizedRolePolicies)), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1001,9 +1016,15 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
|
||||
// When a role is not in use, only permit policies to be a subset unless
|
||||
// the client has root or sudo privileges
|
||||
case !isSudo && !strutil.StrListSubset(parent.Policies, data.Policies):
|
||||
case !isSudo:
|
||||
// Sanitize passed-in and parent policies before comparison
|
||||
sanitizedInputPolicies := policyutil.SanitizePolicies(data.Policies)
|
||||
sanitizedParentPolicies := policyutil.SanitizePolicies(parent.Policies)
|
||||
|
||||
if !strutil.StrListSubset(sanitizedParentPolicies, sanitizedInputPolicies) {
|
||||
return logical.ErrorResponse("child policies must be subset of parent"), logical.ErrInvalidRequest
|
||||
}
|
||||
}
|
||||
|
||||
// Use a map to filter out/prevent duplicates
|
||||
policyMap := map[string]bool{}
|
||||
|
@ -1061,6 +1082,7 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
}
|
||||
te.TTL = dur
|
||||
} else if data.Lease != "" {
|
||||
// This block is compatibility
|
||||
dur, err := time.ParseDuration(data.Lease)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
|
@ -1084,14 +1106,35 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
}
|
||||
}
|
||||
|
||||
resp := &logical.Response{}
|
||||
|
||||
if role != nil && role.ExplicitMaxTTL != 0 {
|
||||
sysView := ts.System()
|
||||
|
||||
// Limit the lease duration
|
||||
if sysView.MaxLeaseTTL() != time.Duration(0) && role.ExplicitMaxTTL > sysView.MaxLeaseTTL() {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"role explicit max TTL of %d is greater than system/mount allowed value of %d seconds",
|
||||
role.ExplicitMaxTTL.Seconds(), sysView.MaxLeaseTTL().Seconds())), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if te.TTL > role.ExplicitMaxTTL {
|
||||
resp.AddWarning(fmt.Sprintf(
|
||||
"Requested TTL higher than role explicit max TTL; value being capped to %d seconds",
|
||||
role.ExplicitMaxTTL.Seconds()))
|
||||
te.TTL = role.ExplicitMaxTTL
|
||||
}
|
||||
|
||||
te.ExplicitMaxTTL = role.ExplicitMaxTTL
|
||||
}
|
||||
|
||||
// Create the token
|
||||
if err := ts.create(&te); err != nil {
|
||||
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Generate the response
|
||||
resp := &logical.Response{
|
||||
Auth: &logical.Auth{
|
||||
resp.Auth = &logical.Auth{
|
||||
DisplayName: te.DisplayName,
|
||||
Policies: te.Policies,
|
||||
Metadata: te.Meta,
|
||||
|
@ -1101,7 +1144,6 @@ func (ts *TokenStore) handleCreateCommon(
|
|||
},
|
||||
ClientToken: te.ID,
|
||||
Accessor: te.Accessor,
|
||||
},
|
||||
}
|
||||
|
||||
if ts.policyLookupFunc != nil {
|
||||
|
@ -1236,6 +1278,7 @@ func (ts *TokenStore) handleLookup(
|
|||
"creation_ttl": int64(out.TTL.Seconds()),
|
||||
"ttl": int64(0),
|
||||
"role": out.Role,
|
||||
"explicit_max_ttl": int64(out.ExplicitMaxTTL.Seconds()),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1311,8 +1354,6 @@ func (ts *TokenStore) authRenew(
|
|||
return nil, fmt.Errorf("request auth is nil")
|
||||
}
|
||||
|
||||
f := framework.LeaseExtend(req.Auth.Increment, 0, ts.System())
|
||||
|
||||
te, err := ts.Lookup(req.Auth.ClientToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error looking up token: %s", err)
|
||||
|
@ -1321,6 +1362,8 @@ func (ts *TokenStore) authRenew(
|
|||
return nil, fmt.Errorf("no token entry found during lookup")
|
||||
}
|
||||
|
||||
f := framework.LeaseExtend(req.Auth.Increment, te.ExplicitMaxTTL, ts.System())
|
||||
|
||||
// No role? Use normal LeaseExtend semantics
|
||||
if te.Role == "" {
|
||||
return f(req, d)
|
||||
|
@ -1339,7 +1382,13 @@ func (ts *TokenStore) authRenew(
|
|||
// periodic token is always the same (the role's period value). It is not
|
||||
// subject to normal maximum TTL checks that would come from calling
|
||||
// LeaseExtend, so we fast path it.
|
||||
if role.Period != 0 {
|
||||
//
|
||||
// The one wrinkle here is if the token has an explicit max TTL. Roles
|
||||
// don't support having both configured, but they could be changed. We
|
||||
// don't support tokens that are both periodic and have an explicit max
|
||||
// TTL, so if the token has one, we treat it as a regular token even if the
|
||||
// role is periodic.
|
||||
if role.Period != 0 && te.ExplicitMaxTTL == 0 {
|
||||
req.Auth.TTL = role.Period
|
||||
return &logical.Response{Auth: req.Auth}, nil
|
||||
}
|
||||
|
@ -1400,12 +1449,14 @@ func (ts *TokenStore) tokenStoreRoleRead(
|
|||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: structs.New(role).Map(),
|
||||
}
|
||||
|
||||
// Make the period nicer
|
||||
if role.Period != 0 {
|
||||
resp.Data["period"] = role.Period.Seconds()
|
||||
Data: map[string]interface{}{
|
||||
"period": int64(role.Period.Seconds()),
|
||||
"explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()),
|
||||
"allowed_policies": role.AllowedPolicies,
|
||||
"name": role.Name,
|
||||
"orphan": role.Orphan,
|
||||
"path_suffix": role.PathSuffix,
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
@ -1461,13 +1512,36 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
|
|||
entry.Period = time.Second * time.Duration(data.Get("period").(int))
|
||||
}
|
||||
|
||||
var resp *logical.Response
|
||||
|
||||
explicitMaxTTLInt, ok := data.GetOk("explicit_max_ttl")
|
||||
if ok {
|
||||
entry.ExplicitMaxTTL = time.Second * time.Duration(explicitMaxTTLInt.(int))
|
||||
} else if req.Operation == logical.CreateOperation {
|
||||
entry.ExplicitMaxTTL = time.Second * time.Duration(data.Get("explicit_max_ttl").(int))
|
||||
}
|
||||
if entry.ExplicitMaxTTL != 0 {
|
||||
sysView := ts.System()
|
||||
|
||||
if sysView.MaxLeaseTTL() != time.Duration(0) && entry.ExplicitMaxTTL > sysView.MaxLeaseTTL() {
|
||||
if resp == nil {
|
||||
resp = &logical.Response{}
|
||||
}
|
||||
resp.AddWarning(fmt.Sprintf(
|
||||
"Given explicit max TTL of %d is greater than system/mount allowed value of %d seconds; until this is fixed attempting to create tokens against this role will result in an error",
|
||||
entry.ExplicitMaxTTL.Seconds(), sysView.MaxLeaseTTL().Seconds()))
|
||||
}
|
||||
}
|
||||
|
||||
pathSuffixInt, ok := data.GetOk("path_suffix")
|
||||
if ok {
|
||||
pathSuffix := pathSuffixInt.(string)
|
||||
if pathSuffix != "" {
|
||||
matched := pathSuffixSanitize.MatchString(pathSuffix)
|
||||
if !matched {
|
||||
return logical.ErrorResponse(fmt.Sprintf("given role path suffix contains invalid characters; must match %s", pathSuffixSanitize.String())), nil
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"given role path suffix contains invalid characters; must match %s",
|
||||
pathSuffixSanitize.String())), nil
|
||||
}
|
||||
entry.PathSuffix = pathSuffix
|
||||
}
|
||||
|
@ -1485,6 +1559,12 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
|
|||
entry.AllowedPolicies = strings.Split(data.Get("allowed_policies").(string), ",")
|
||||
}
|
||||
|
||||
// Explicit max TTLs and periods cannot be used at the same time since the
|
||||
// purpose of a periodic token is to escape max TTL semantics
|
||||
if entry.Period > 0 && entry.ExplicitMaxTTL > 0 {
|
||||
return logical.ErrorResponse("a role cannot be used to issue both periodic tokens and tokens with explicit max TTLs"), logical.ErrInvalidRequest
|
||||
}
|
||||
|
||||
// Store it
|
||||
jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry)
|
||||
if err != nil {
|
||||
|
@ -1494,7 +1574,7 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1533,5 +1613,11 @@ will contain the given suffix as a part of
|
|||
their path. This can be used to assist use
|
||||
of the 'revoke-prefix' endpoint later on.
|
||||
The given suffix must match the regular
|
||||
expression `
|
||||
expression.`
|
||||
tokenExplicitMaxTTLHelp = `If set, tokens created via this role
|
||||
carry an explicit maximum TTL. During renewal,
|
||||
the current maximum TTL values of the role
|
||||
and the mount are not checked for changes,
|
||||
and any updates to these values will have
|
||||
no effect on the token being renewed.`
|
||||
)
|
||||
|
|
|
@ -979,6 +979,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||
"creation_ttl": int64(0),
|
||||
"ttl": int64(0),
|
||||
"role": "",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
|
@ -1014,6 +1015,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||
"creation_ttl": int64(3600),
|
||||
"ttl": int64(3600),
|
||||
"role": "",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
|
@ -1055,6 +1057,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
|
|||
"creation_ttl": int64(3600),
|
||||
"ttl": int64(3600),
|
||||
"role": "",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
|
@ -1119,6 +1122,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
|
|||
"creation_ttl": int64(0),
|
||||
"ttl": int64(0),
|
||||
"role": "",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if resp.Data["creation_time"].(int64) == 0 {
|
||||
|
@ -1265,9 +1269,10 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
expected := map[string]interface{}{
|
||||
"name": "test",
|
||||
"orphan": true,
|
||||
"period": float64(259200),
|
||||
"period": int64(259200),
|
||||
"allowed_policies": []string{"test1", "test2"},
|
||||
"path_suffix": "happenin",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
|
@ -1305,9 +1310,57 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
|
|||
expected = map[string]interface{}{
|
||||
"name": "test",
|
||||
"orphan": true,
|
||||
"period": float64(284400),
|
||||
"period": int64(284400),
|
||||
"allowed_policies": []string{"test3"},
|
||||
"path_suffix": "happenin",
|
||||
"explicit_max_ttl": int64(0),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
t.Fatalf("expected:\n%v\nactual:\n%v\n", expected, resp.Data)
|
||||
}
|
||||
|
||||
// Now test setting explicit max ttl at the same time as period, which
|
||||
// should be an error
|
||||
req.Operation = logical.CreateOperation
|
||||
req.Data = map[string]interface{}{
|
||||
"explicit_max_ttl": "5",
|
||||
}
|
||||
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
|
||||
// Now set explicit max ttl and clear the period
|
||||
req.Operation = logical.CreateOperation
|
||||
req.Data = map[string]interface{}{
|
||||
"explicit_max_ttl": "5",
|
||||
"period": "0s",
|
||||
}
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Data = map[string]interface{}{}
|
||||
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("got a nil response")
|
||||
}
|
||||
|
||||
expected = map[string]interface{}{
|
||||
"name": "test",
|
||||
"orphan": true,
|
||||
"explicit_max_ttl": int64(5),
|
||||
"allowed_policies": []string{"test3"},
|
||||
"path_suffix": "happenin",
|
||||
"period": int64(0),
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, resp.Data) {
|
||||
|
@ -1623,3 +1676,205 @@ func TestTokenStore_RolePeriod(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) {
|
||||
core, _, _, root := TestCoreWithTokenStore(t)
|
||||
|
||||
core.defaultLeaseTTL = 5 * time.Second
|
||||
core.maxLeaseTTL = 5 * time.Hour
|
||||
|
||||
// Note: these requests are sent to Core since Core handles registration
|
||||
// with the expiration manager and we need the storage to be consistent
|
||||
|
||||
// Make sure we can't make it larger than the system/mount max; we should get a warning on role write and an error on token creation
|
||||
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/roles/test")
|
||||
req.ClientToken = root
|
||||
req.Data = map[string]interface{}{
|
||||
"explicit_max_ttl": "100h",
|
||||
}
|
||||
|
||||
resp, err := core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatalf("expected a warning")
|
||||
}
|
||||
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/create/test"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
}
|
||||
|
||||
// Reset to a good explicit max
|
||||
req = logical.TestRequest(t, logical.UpdateOperation, "auth/token/roles/test")
|
||||
req.ClientToken = root
|
||||
req.Data = map[string]interface{}{
|
||||
"explicit_max_ttl": "6s",
|
||||
}
|
||||
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("expected a nil response")
|
||||
}
|
||||
|
||||
// This first set of logic is to verify that a normal non-root token will
|
||||
// be given a TTL of 5 seconds, and that renewing will cause the TTL to
|
||||
// increase
|
||||
{
|
||||
req.Path = "auth/token/create"
|
||||
req.Data = map[string]interface{}{
|
||||
"policies": []string{"default"},
|
||||
}
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
req.ClientToken = resp.Auth.ClientToken
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
ttl := resp.Data["ttl"].(int64)
|
||||
if ttl > 5 {
|
||||
t.Fatalf("TTL too large")
|
||||
}
|
||||
|
||||
// Let the TTL go down a bit to 3 seconds
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/renew-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
ttl = resp.Data["ttl"].(int64)
|
||||
if ttl < 4 {
|
||||
t.Fatalf("TTL too small after renewal")
|
||||
}
|
||||
}
|
||||
|
||||
// Now we create a token against the role. After renew our max should still
|
||||
// be the same.
|
||||
{
|
||||
req.ClientToken = root
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/create/test"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("response was nil")
|
||||
}
|
||||
if resp.Auth == nil {
|
||||
t.Fatal(fmt.Sprintf("response auth was nil, resp is %#v", *resp))
|
||||
}
|
||||
if resp.Auth.ClientToken == "" {
|
||||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
|
||||
req.ClientToken = resp.Auth.ClientToken
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
ttl := resp.Data["ttl"].(int64)
|
||||
if ttl > 6 {
|
||||
t.Fatalf("TTL too big")
|
||||
}
|
||||
maxTTL := resp.Data["explicit_max_ttl"].(int64)
|
||||
if maxTTL != 6 {
|
||||
t.Fatalf("expected 6 for explicit max TTL, got %d", maxTTL)
|
||||
}
|
||||
|
||||
// Let the TTL go down a bit to 3 seconds (4 against explicit max)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/renew-self"
|
||||
req.Data = map[string]interface{}{
|
||||
"increment": 300,
|
||||
}
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
ttl = resp.Data["ttl"].(int64)
|
||||
if ttl > 4 {
|
||||
t.Fatalf("TTL too big")
|
||||
}
|
||||
|
||||
// Let the TTL go down a bit more to 2 seconds (2 against explicit max)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/renew-self"
|
||||
req.Data = map[string]interface{}{
|
||||
"increment": 300,
|
||||
}
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v %v", err, resp)
|
||||
}
|
||||
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
ttl = resp.Data["ttl"].(int64)
|
||||
if ttl > 2 {
|
||||
t.Fatalf("TTL too big")
|
||||
}
|
||||
|
||||
// It should expire
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
req.Operation = logical.UpdateOperation
|
||||
req.Path = "auth/token/renew-self"
|
||||
req.Data = map[string]interface{}{
|
||||
"increment": 300,
|
||||
}
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
|
||||
req.Operation = logical.ReadOperation
|
||||
req.Path = "auth/token/lookup-self"
|
||||
resp, err = core.HandleRequest(req)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -601,7 +601,7 @@ of the header should be "X-Vault-Token" and the value should be the token.
|
|||
each renewal. So long as they continue to be renewed, they will never
|
||||
expire. The parameter is an integer duration of seconds. Tokens issued
|
||||
track updates to the role value; the new period takes effect upon next
|
||||
renew.
|
||||
renew. This cannot be used in conjunction with `explicit_max_ttl`.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">path_suffix</span>
|
||||
|
@ -614,6 +614,16 @@ of the header should be "X-Vault-Token" and the value should be the token.
|
|||
part of their path, and then tokens with the old suffix can be revoked
|
||||
via `sys/revoke-prefix`.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">explicit_max_ttl</span>
|
||||
<span class="param-flags">optional</span>
|
||||
If set, tokens created with this role have an explicit max TTL set upon
|
||||
them. This maximum token TTL *cannot* be changed later, and unlike with
|
||||
normal tokens, updates to the role or the system/mount max TTL value
|
||||
will have no effect at renewal time -- the token will never be able to
|
||||
be renewed or used past the value set at issue time. This cannot be
|
||||
used in conjunction with `period`.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
|
|
Loading…
Reference in New Issue