Merge branch 'master-oss' into cubbyhole-the-world

This commit is contained in:
Jeff Mitchell 2016-05-11 19:29:52 -04:00
commit ce5614bf9b
6 changed files with 489 additions and 129 deletions

View File

@ -46,6 +46,12 @@ FEATURES:
standby nodes are `standby.vault.service.consul`. Sealed vaults are marked standby nodes are `standby.vault.service.consul`. Sealed vaults are marked
critical and are not listed by default in Consul's service discovery. See critical and are not listed by default in Consul's service discovery. See
the documentation for details. [GH-1349] 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: IMPROVEMENTS:

View File

@ -139,6 +139,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
"ttl": float64(0), "ttl": float64(0),
"creation_ttl": float64(0), "creation_ttl": float64(0),
"role": "", "role": "",
"explicit_max_ttl": float64(0),
}, },
"warnings": nilWarnings, "warnings": nilWarnings,
"wrap_info": nil, "wrap_info": nil,

View File

@ -303,6 +303,7 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
"ttl": float64(0), "ttl": float64(0),
"path": "auth/token/root", "path": "auth/token/root",
"role": "", "role": "",
"explicit_max_ttl": float64(0),
} }
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self") resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")
@ -385,6 +386,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
"ttl": float64(0), "ttl": float64(0),
"path": "auth/token/root", "path": "auth/token/root",
"role": "", "role": "",
"explicit_max_ttl": float64(0),
} }
resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self") resp = testHttpGet(t, newRootToken, addr+"/v1/auth/token/lookup-self")

View File

@ -11,8 +11,8 @@ import (
"time" "time"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
"github.com/fatih/structs"
"github.com/hashicorp/go-uuid" "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/helper/salt"
"github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
@ -154,6 +154,12 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error)
Default: "", Default: "",
Description: tokenPathSuffixHelp + pathSuffixSanitize.String(), Description: tokenPathSuffixHelp + pathSuffixSanitize.String(),
}, },
"explicit_max_ttl": &framework.FieldSchema{
Type: framework.TypeDurationSecond,
Default: 0,
Description: tokenExplicitMaxTTLHelp,
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ 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). 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 CreationTime int64 // Time of token creation
TTL time.Duration // Duration set when token was created 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 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"` Period time.Duration `json:"period" mapstructure:"period" structs:"period"`
// If set, a suffix will be set on the token path, making it easier to // 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"` 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 // SetExpirationManager is used to provide the token store with
@ -991,8 +1002,12 @@ func (ts *TokenStore) handleCreateCommon(
if len(data.Policies) == 0 { if len(data.Policies) == 0 {
data.Policies = role.AllowedPolicies data.Policies = role.AllowedPolicies
} else { } else {
if !strutil.StrListSubset(role.AllowedPolicies, data.Policies) { // Sanitize passed-in and role policies before comparison
return logical.ErrorResponse("token policies must be subset of the role's allowed policies"), logical.ErrInvalidRequest 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 // When a role is not in use, only permit policies to be a subset unless
// the client has root or sudo privileges // 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 return logical.ErrorResponse("child policies must be subset of parent"), logical.ErrInvalidRequest
} }
}
// Use a map to filter out/prevent duplicates // Use a map to filter out/prevent duplicates
policyMap := map[string]bool{} policyMap := map[string]bool{}
@ -1061,6 +1082,7 @@ func (ts *TokenStore) handleCreateCommon(
} }
te.TTL = dur te.TTL = dur
} else if data.Lease != "" { } else if data.Lease != "" {
// This block is compatibility
dur, err := time.ParseDuration(data.Lease) dur, err := time.ParseDuration(data.Lease)
if err != nil { if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest 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 // Create the token
if err := ts.create(&te); err != nil { if err := ts.create(&te); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
} }
// Generate the response // Generate the response
resp := &logical.Response{ resp.Auth = &logical.Auth{
Auth: &logical.Auth{
DisplayName: te.DisplayName, DisplayName: te.DisplayName,
Policies: te.Policies, Policies: te.Policies,
Metadata: te.Meta, Metadata: te.Meta,
@ -1101,7 +1144,6 @@ func (ts *TokenStore) handleCreateCommon(
}, },
ClientToken: te.ID, ClientToken: te.ID,
Accessor: te.Accessor, Accessor: te.Accessor,
},
} }
if ts.policyLookupFunc != nil { if ts.policyLookupFunc != nil {
@ -1236,6 +1278,7 @@ func (ts *TokenStore) handleLookup(
"creation_ttl": int64(out.TTL.Seconds()), "creation_ttl": int64(out.TTL.Seconds()),
"ttl": int64(0), "ttl": int64(0),
"role": out.Role, "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") return nil, fmt.Errorf("request auth is nil")
} }
f := framework.LeaseExtend(req.Auth.Increment, 0, ts.System())
te, err := ts.Lookup(req.Auth.ClientToken) te, err := ts.Lookup(req.Auth.ClientToken)
if err != nil { if err != nil {
return nil, fmt.Errorf("error looking up token: %s", err) 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") 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 // No role? Use normal LeaseExtend semantics
if te.Role == "" { if te.Role == "" {
return f(req, d) 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 // 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 // subject to normal maximum TTL checks that would come from calling
// LeaseExtend, so we fast path it. // 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 req.Auth.TTL = role.Period
return &logical.Response{Auth: req.Auth}, nil return &logical.Response{Auth: req.Auth}, nil
} }
@ -1400,12 +1449,14 @@ func (ts *TokenStore) tokenStoreRoleRead(
} }
resp := &logical.Response{ resp := &logical.Response{
Data: structs.New(role).Map(), Data: map[string]interface{}{
} "period": int64(role.Period.Seconds()),
"explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()),
// Make the period nicer "allowed_policies": role.AllowedPolicies,
if role.Period != 0 { "name": role.Name,
resp.Data["period"] = role.Period.Seconds() "orphan": role.Orphan,
"path_suffix": role.PathSuffix,
},
} }
return resp, nil return resp, nil
@ -1461,13 +1512,36 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
entry.Period = time.Second * time.Duration(data.Get("period").(int)) 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") pathSuffixInt, ok := data.GetOk("path_suffix")
if ok { if ok {
pathSuffix := pathSuffixInt.(string) pathSuffix := pathSuffixInt.(string)
if pathSuffix != "" { if pathSuffix != "" {
matched := pathSuffixSanitize.MatchString(pathSuffix) matched := pathSuffixSanitize.MatchString(pathSuffix)
if !matched { 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 entry.PathSuffix = pathSuffix
} }
@ -1485,6 +1559,12 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
entry.AllowedPolicies = strings.Split(data.Get("allowed_policies").(string), ",") 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 // Store it
jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry) jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry)
if err != nil { if err != nil {
@ -1494,7 +1574,7 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(
return nil, err return nil, err
} }
return nil, nil return resp, nil
} }
const ( const (
@ -1533,5 +1613,11 @@ will contain the given suffix as a part of
their path. This can be used to assist use their path. This can be used to assist use
of the 'revoke-prefix' endpoint later on. of the 'revoke-prefix' endpoint later on.
The given suffix must match the regular 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.`
) )

View File

@ -979,6 +979,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
"creation_ttl": int64(0), "creation_ttl": int64(0),
"ttl": int64(0), "ttl": int64(0),
"role": "", "role": "",
"explicit_max_ttl": int64(0),
} }
if resp.Data["creation_time"].(int64) == 0 { if resp.Data["creation_time"].(int64) == 0 {
@ -1014,6 +1015,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
"creation_ttl": int64(3600), "creation_ttl": int64(3600),
"ttl": int64(3600), "ttl": int64(3600),
"role": "", "role": "",
"explicit_max_ttl": int64(0),
} }
if resp.Data["creation_time"].(int64) == 0 { if resp.Data["creation_time"].(int64) == 0 {
@ -1055,6 +1057,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) {
"creation_ttl": int64(3600), "creation_ttl": int64(3600),
"ttl": int64(3600), "ttl": int64(3600),
"role": "", "role": "",
"explicit_max_ttl": int64(0),
} }
if resp.Data["creation_time"].(int64) == 0 { if resp.Data["creation_time"].(int64) == 0 {
@ -1119,6 +1122,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) {
"creation_ttl": int64(0), "creation_ttl": int64(0),
"ttl": int64(0), "ttl": int64(0),
"role": "", "role": "",
"explicit_max_ttl": int64(0),
} }
if resp.Data["creation_time"].(int64) == 0 { if resp.Data["creation_time"].(int64) == 0 {
@ -1265,9 +1269,10 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
expected := map[string]interface{}{ expected := map[string]interface{}{
"name": "test", "name": "test",
"orphan": true, "orphan": true,
"period": float64(259200), "period": int64(259200),
"allowed_policies": []string{"test1", "test2"}, "allowed_policies": []string{"test1", "test2"},
"path_suffix": "happenin", "path_suffix": "happenin",
"explicit_max_ttl": int64(0),
} }
if !reflect.DeepEqual(expected, resp.Data) { if !reflect.DeepEqual(expected, resp.Data) {
@ -1305,9 +1310,57 @@ func TestTokenStore_RoleCRUD(t *testing.T) {
expected = map[string]interface{}{ expected = map[string]interface{}{
"name": "test", "name": "test",
"orphan": true, "orphan": true,
"period": float64(284400), "period": int64(284400),
"allowed_policies": []string{"test3"}, "allowed_policies": []string{"test3"},
"path_suffix": "happenin", "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) { 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")
}
}
}

View File

@ -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 each renewal. So long as they continue to be renewed, they will never
expire. The parameter is an integer duration of seconds. Tokens issued expire. The parameter is an integer duration of seconds. Tokens issued
track updates to the role value; the new period takes effect upon next 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>
<li> <li>
<span class="param">path_suffix</span> <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 part of their path, and then tokens with the old suffix can be revoked
via `sys/revoke-prefix`. via `sys/revoke-prefix`.
</li> </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> </ul>
</dd> </dd>