Core handling of TTLs (#4230)

* govet cleanup in token store

* adding general ttl handling to login requests

* consolidating TTL calculation to system view

* deprecate LeaseExtend

* deprecate LeaseExtend

* set the increment to the correct value

* move calculateTTL out of SystemView

* remove unused value

* add back clearing of lease id

* implement core ttl in some backends

* removing increment and issue time from lease options

* adding ttl tests, fixing some compile issue

* adding ttl tests

* fixing some explicit max TTL logic

* fixing up some tests

* removing unneeded test

* off by one errors...

* adding back some logic for bc

* adding period to return on renewal

* tweaking max ttl capping slightly

* use the appropriate precision for ttl calculation

* deprecate proto fields instead of delete

* addressing feedback

* moving TTL handling for backends to core

* mongo is a secret backend not auth

* adding estimated ttl for backends that also manage the expiration time

* set the estimate values before calling the renew request

* moving calculate TTL to framework, revert removal of increment and issue time from logical

* minor edits

* addressing feedback

* address more feedback
This commit is contained in:
Chris Hoffman
2018-04-03 12:20:20 -04:00
committed by GitHub
parent fcdb4bcd29
commit 6e3520658f
50 changed files with 589 additions and 763 deletions

View File

@@ -20,7 +20,6 @@ func TestCopy_auth(t *testing.T) {
expected := logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: 1 * time.Hour,
IssueTime: time.Now(),
},
ClientToken: "foo",
@@ -184,7 +183,6 @@ func TestHash(t *testing.T) {
&logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: 1 * time.Hour,
IssueTime: now,
},
ClientToken: "foo",
@@ -192,7 +190,6 @@ func TestHash(t *testing.T) {
&logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: 1 * time.Hour,
IssueTime: now,
},
ClientToken: "hmac-sha256:08ba357e274f528065766c770a639abf6809b39ccfd37c2a3157c7f51954da0a",

View File

@@ -146,7 +146,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, fmt.Errorf("policies do not match")
}
return framework.LeaseExtend(0, 0, b.System())(ctx, req, d)
return &logical.Response{Auth: req.Auth}, nil
}
func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, appId, userId string) (string, *logical.Response, error) {

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/logical"
@@ -107,17 +106,11 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, data
return nil, fmt.Errorf("role %s does not exist during renewal", roleName)
}
// If a period is provided, set that as part of resp.Auth.Period and return a
// response immediately. Let expiration manager handle renewal from there on.
if role.Period > time.Duration(0) {
resp := &logical.Response{
Auth: req.Auth,
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = role.TokenTTL
resp.Auth.MaxTTL = role.TokenMaxTTL
resp.Auth.Period = role.Period
return resp, nil
}
return framework.LeaseExtend(role.TokenTTL, role.TokenMaxTTL, b.System())(ctx, req, data)
}
const pathLoginHelpSys = "Issue a token based on the credentials supplied"

View File

@@ -147,7 +147,6 @@ func generateRenewRequest(s logical.Storage, auth *logical.Auth) *logical.Reques
renewReq.Auth.Metadata = auth.Metadata
renewReq.Auth.LeaseOptions = auth.LeaseOptions
renewReq.Auth.Policies = auth.Policies
renewReq.Auth.IssueTime = time.Now()
renewReq.Auth.Period = auth.Period
return renewReq

View File

@@ -112,11 +112,6 @@ func (b *backend) validateCredentials(ctx context.Context, req *logical.Request,
return nil, "", metadata, "", fmt.Errorf("failed to validate role_id"), nil
}
// Calculate the TTL boundaries since this reflects the properties of the token issued
if role.TokenTTL, role.TokenMaxTTL, err = b.SanitizeTTL(role.TokenTTL, role.TokenMaxTTL); err != nil {
return nil, "", metadata, "", nil, err
}
var secretID string
if role.BindSecretID {
// If 'bind_secret_id' was set on role, look for the field 'secret_id'

View File

@@ -1733,7 +1733,6 @@ func generateRenewRequest(s logical.Storage, auth *logical.Auth) *logical.Reques
renewReq.Auth.Metadata = auth.Metadata
renewReq.Auth.LeaseOptions = auth.LeaseOptions
renewReq.Auth.Policies = auth.Policies
renewReq.Auth.IssueTime = time.Now()
renewReq.Auth.Period = auth.Period
return renewReq

View File

@@ -809,6 +809,7 @@ func (b *backend) pathLoginUpdateEc2(ctx context.Context, req *logical.Request,
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: roleEntry.TTL,
MaxTTL: shortestMaxTTL,
},
Alias: &logical.Alias{
Name: identityDocParsed.InstanceID,
@@ -826,16 +827,6 @@ func (b *backend) pathLoginUpdateEc2(ctx context.Context, req *logical.Request,
resp.Auth.Metadata["nonce"] = clientNonce
}
// In this case no role value was set so pull in what will be assigned by
// Core for comparison
if resp.Auth.TTL == 0 {
resp.Auth.TTL = b.System().DefaultLeaseTTL()
}
if resp.Auth.TTL > shortestMaxTTL {
resp.Auth.TTL = shortestMaxTTL
resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", resp.Auth.TTL, shortestMaxTTL))
}
return resp, nil
}
@@ -1025,17 +1016,11 @@ func (b *backend) pathLoginRenewIam(ctx context.Context, req *logical.Request, d
}
}
// If a period is provided, set that as part of resp.Auth.Period and return a
// response immediately. Let expiration manager handle renewal from there on.
if roleEntry.Period > time.Duration(0) {
resp := &logical.Response{
Auth: req.Auth,
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = roleEntry.TTL
resp.Auth.MaxTTL = roleEntry.MaxTTL
resp.Auth.Period = roleEntry.Period
return resp, nil
}
return framework.LeaseExtend(roleEntry.TTL, roleEntry.MaxTTL, b.System())(ctx, req, data)
}
func (b *backend) pathLoginRenewEc2(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
@@ -1115,17 +1100,11 @@ func (b *backend) pathLoginRenewEc2(ctx context.Context, req *logical.Request, d
return nil, err
}
// If a period is provided, set that as part of resp.Auth.Period and return a
// response immediately. Let expiration manager handle renewal from there on.
if roleEntry.Period > time.Duration(0) {
resp := &logical.Response{
Auth: req.Auth,
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = roleEntry.TTL
resp.Auth.MaxTTL = shortestMaxTTL
resp.Auth.Period = roleEntry.Period
return resp, nil
}
return framework.LeaseExtend(roleEntry.TTL, shortestMaxTTL, b.System())(ctx, req, data)
}
func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {

View File

@@ -1416,7 +1416,6 @@ func Test_Renew(t *testing.T) {
req.Auth.Metadata = resp.Auth.Metadata
req.Auth.LeaseOptions = resp.Auth.LeaseOptions
req.Auth.Policies = resp.Auth.Policies
req.Auth.IssueTime = time.Now()
req.Auth.Period = resp.Auth.Period
// Normal renewal

View File

@@ -11,7 +11,6 @@ import (
"errors"
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/helper/certutil"
"github.com/hashicorp/vault/helper/policyutil"
@@ -72,11 +71,6 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
return nil, nil
}
ttl := matched.Entry.TTL
if ttl == 0 {
ttl = b.System().DefaultLeaseTTL()
}
clientCerts := req.Connection.ConnState.PeerCertificates
if len(clientCerts) == 0 {
return logical.ErrorResponse("no client certificate found"), nil
@@ -101,7 +95,8 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
},
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: ttl,
TTL: matched.Entry.TTL,
MaxTTL: matched.Entry.MaxTTL,
},
Alias: &logical.Alias{
Name: clientCerts[0].SerialNumber.String(),
@@ -109,20 +104,6 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
},
}
if matched.Entry.MaxTTL > time.Duration(0) {
// Cap maxTTL to the sysview's max TTL
maxTTL := matched.Entry.MaxTTL
if maxTTL > b.System().MaxLeaseTTL() {
maxTTL = b.System().MaxLeaseTTL()
}
// Cap TTL to MaxTTL
if resp.Auth.TTL > maxTTL {
resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", (resp.Auth.TTL / time.Second), (maxTTL / time.Second)))
resp.Auth.TTL = maxTTL
}
}
// Generate a response
return resp, nil
}
@@ -175,17 +156,11 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, fmt.Errorf("policies have changed, not renewing")
}
// If a period is provided, set that as part of resp.Auth.Period and return a
// response immediately. Let expiration manager handle renewal from there on.
if cert.Period > time.Duration(0) {
resp := &logical.Response{
Auth: req.Auth,
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = cert.TTL
resp.Auth.MaxTTL = cert.MaxTTL
resp.Auth.Period = cert.Period
return resp, nil
}
return framework.LeaseExtend(cert.TTL, cert.MaxTTL, b.System())(ctx, req, d)
}
func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, d *framework.FieldData) (*ParsedCert, *logical.Response, error) {

View File

@@ -67,7 +67,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
return nil, err
}
ttl, _, err := b.SanitizeTTLStr(config.TTL.String(), config.MaxTTL.String())
ttl, maxTTL, err := b.SanitizeTTLStr(config.TTL.String(), config.MaxTTL.String())
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("error sanitizing TTLs: %s", err)), nil
}
@@ -85,6 +85,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
DisplayName: *verifyResp.User.Login,
LeaseOptions: logical.LeaseOptions{
TTL: ttl,
MaxTTL: maxTTL,
Renewable: true,
},
Alias: &logical.Alias{
@@ -133,10 +134,9 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, err
}
resp, err := framework.LeaseExtend(config.TTL, config.MaxTTL, b.System())(ctx, req, d)
if err != nil {
return nil, err
}
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = config.TTL
resp.Auth.MaxTTL = config.MaxTTL
// Remove old aliases
resp.Auth.GroupAliases = nil

View File

@@ -111,10 +111,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, fmt.Errorf("policies have changed, not renewing")
}
resp, err = framework.LeaseExtend(0, 0, b.System())(ctx, req, d)
if err != nil {
return nil, err
}
resp.Auth = req.Auth
// Remove old aliases
resp.Auth.GroupAliases = nil

View File

@@ -89,6 +89,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
DisplayName: username,
LeaseOptions: logical.LeaseOptions{
TTL: cfg.TTL,
MaxTTL: cfg.MaxTTL,
Renewable: true,
},
Alias: &logical.Alias{
@@ -96,21 +97,6 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
},
}
if resp.Auth.TTL == 0 {
resp.Auth.TTL = b.System().DefaultLeaseTTL()
}
if cfg.MaxTTL > 0 {
maxTTL := cfg.MaxTTL
if maxTTL > b.System().MaxLeaseTTL() {
maxTTL = b.System().MaxLeaseTTL()
}
if resp.Auth.TTL > maxTTL {
resp.Auth.TTL = maxTTL
resp.AddWarning(fmt.Sprintf("Effective TTL of '%s' exceeded the effective max_ttl of '%s'; TTL value is capped accordingly", resp.Auth.TTL, maxTTL))
}
}
for _, groupName := range groupNames {
if groupName == "" {
continue
@@ -141,10 +127,9 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, err
}
resp, err = framework.LeaseExtend(cfg.TTL, cfg.MaxTTL, b.System())(ctx, req, d)
if err != nil {
return nil, err
}
resp.Auth = req.Auth
resp.Auth.TTL = cfg.TTL
resp.Auth.MaxTTL = cfg.MaxTTL
// Remove old aliases
resp.Auth.GroupAliases = nil

View File

@@ -88,6 +88,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
}
}
resp.Auth = req.Auth
resp.Auth = &logical.Auth{
Policies: policies,
Metadata: map[string]string{
@@ -126,7 +127,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, fmt.Errorf("policies have changed, not renewing")
}
return framework.LeaseExtend(0, 0, b.System())(ctx, req, d)
return &logical.Response{Auth: req.Auth}, nil
}
func (b *backend) RadiusLogin(ctx context.Context, req *logical.Request, username string, password string) ([]string, *logical.Response, error) {

View File

@@ -18,57 +18,6 @@ const (
testSysMaxTTL = time.Hour * 20
)
func TestBackend_TTLDurations(t *testing.T) {
data1 := map[string]interface{}{
"password": "password",
"policies": "root",
"ttl": "21h",
"max_ttl": "11h",
}
data2 := map[string]interface{}{
"password": "password",
"policies": "root",
"ttl": "10h",
"max_ttl": "21h",
}
data3 := map[string]interface{}{
"password": "password",
"policies": "root",
"ttl": "10h",
"max_ttl": "10h",
}
data4 := map[string]interface{}{
"password": "password",
"policies": "root",
"ttl": "11h",
"max_ttl": "5h",
}
data5 := map[string]interface{}{
"password": "password",
}
b, err := Factory(context.Background(), &logical.BackendConfig{
Logger: nil,
System: &logical.StaticSystemView{
DefaultLeaseTTLVal: testSysTTL,
MaxLeaseTTLVal: testSysMaxTTL,
},
})
if err != nil {
t.Fatalf("Unable to create backend: %s", err)
}
logicaltest.Test(t, logicaltest.TestCase{
Backend: b,
Steps: []logicaltest.TestStep{
testUsersWrite(t, "test", data1, true),
testUsersWrite(t, "test", data2, true),
testUsersWrite(t, "test", data3, false),
testUsersWrite(t, "test", data4, false),
testLoginWrite(t, "test", data5, false),
testLoginWrite(t, "wrong", data5, true),
},
})
}
func TestBackend_basic(t *testing.T) {
b, err := Factory(context.Background(), &logical.BackendConfig{
Logger: nil,

View File

@@ -91,6 +91,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
DisplayName: username,
LeaseOptions: logical.LeaseOptions{
TTL: user.TTL,
MaxTTL: user.MaxTTL,
Renewable: true,
},
Alias: &logical.Alias{
@@ -115,7 +116,10 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
return nil, fmt.Errorf("policies have changed, not renewing")
}
return framework.LeaseExtend(user.TTL, user.MaxTTL, b.System())(ctx, req, d)
resp := &logical.Response{Auth: req.Auth}
resp.Auth.TTL = user.TTL
resp.Auth.MaxTTL = user.MaxTTL
return resp, nil
}
const pathLoginSyn = `

View File

@@ -243,6 +243,7 @@ func (b *backend) secretAccessKeysCreate(
}
resp.Secret.TTL = lease.Lease
resp.Secret.MaxTTL = lease.LeaseMax
if usernameWarning != "" {
resp.AddWarning(usernameWarning)
@@ -271,8 +272,10 @@ func (b *backend) secretAccessKeysRenew(ctx context.Context, req *logical.Reques
lease = &configLease{}
}
f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System())
return f(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = lease.Lease
resp.Secret.MaxTTL = lease.LeaseMax
return resp, nil
}
func secretAccessKeysRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -47,7 +47,9 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
return nil, fmt.Errorf("unable to load role: %s", err)
}
return framework.LeaseExtend(role.Lease, 0, b.System())(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = role.Lease
return resp, nil
}
func (b *backend) secretCredsRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -206,7 +206,6 @@ func TestBackend_renew_revoke(t *testing.T) {
}
generatedSecret := resp.Secret
generatedSecret.IssueTime = time.Now()
generatedSecret.TTL = 6 * time.Hour
var d struct {

View File

@@ -28,14 +28,15 @@ func secretToken(b *backend) *framework.Secret {
}
func (b *backend) secretTokenRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
resp := &logical.Response{Secret: req.Secret}
roleRaw, ok := req.Secret.InternalData["role"]
if !ok || roleRaw == nil {
return framework.LeaseExtend(0, 0, b.System())(ctx, req, d)
return resp, nil
}
role, ok := roleRaw.(string)
if !ok {
return framework.LeaseExtend(0, 0, b.System())(ctx, req, d)
return resp, nil
}
entry, err := req.Storage.Get(ctx, "policy/"+role)
@@ -50,8 +51,8 @@ func (b *backend) secretTokenRenew(ctx context.Context, req *logical.Request, d
if err := entry.DecodeJSON(&result); err != nil {
return nil, err
}
return framework.LeaseExtend(result.Lease, 0, b.System())(ctx, req, d)
resp.Secret.TTL = result.Lease
return resp, nil
}
func secretTokenRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -1,6 +1,5 @@
// Code generated by protoc-gen-go.
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: builtin/logical/database/dbplugin/database.proto
// DO NOT EDIT!
/*
Package dbplugin is a generated protocol buffer package.

View File

@@ -3,6 +3,7 @@ package database
import (
"context"
"fmt"
"time"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@@ -42,12 +43,6 @@ func (b *databaseBackend) secretCredsRenew() framework.OperationFunc {
return nil, fmt.Errorf("error during renew: could not find role with name %s", req.Secret.InternalData["role"])
}
f := framework.LeaseExtend(role.DefaultTTL, role.MaxTTL, b.System())
resp, err := f(ctx, req, data)
if err != nil {
return nil, err
}
// Get the Database object
db, err := b.GetConnection(ctx, req.Storage, role.DBName)
if err != nil {
@@ -58,13 +53,24 @@ func (b *databaseBackend) secretCredsRenew() framework.OperationFunc {
defer db.RUnlock()
// Make sure we increase the VALID UNTIL endpoint for this user.
if expireTime := resp.Secret.ExpirationTime(); !expireTime.IsZero() {
ttl, _, err := framework.CalculateTTL(b.System(), req.Secret.Increment, role.DefaultTTL, 0, role.MaxTTL, 0, req.Secret.IssueTime)
if err != nil {
return nil, err
}
if ttl > 0 {
expireTime := time.Now().Add(ttl)
// Adding a small buffer since the TTL will be calculated again after this call
// to ensure the database credential does not expire before the lease
expireTime = expireTime.Add(5 * time.Second)
err := db.RenewUser(ctx, role.Statements, username, expireTime)
if err != nil {
b.CloseIfShutdown(db, err)
return nil, err
}
}
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = role.DefaultTTL
resp.Secret.MaxTTL = role.MaxTTL
return resp, nil
}
}

View File

@@ -41,8 +41,10 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
leaseConfig = &configLease{}
}
f := framework.LeaseExtend(leaseConfig.TTL, leaseConfig.MaxTTL, b.System())
return f(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = leaseConfig.TTL
resp.Secret.MaxTTL = leaseConfig.MaxTTL
return resp, nil
}
func (b *backend) secretCredsRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -41,8 +41,10 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
leaseConfig = &configLease{}
}
f := framework.LeaseExtend(leaseConfig.TTL, leaseConfig.TTLMax, b.System())
return f(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = leaseConfig.TTL
resp.Secret.MaxTTL = leaseConfig.TTLMax
return resp, nil
}
func (b *backend) secretCredsRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -130,11 +130,8 @@ func (b *backend) pathRoleCreateRead(ctx context.Context, req *logical.Request,
"role": name,
})
ttl := lease.Lease
if ttl == 0 || (lease.LeaseMax > 0 && ttl > lease.LeaseMax) {
ttl = lease.LeaseMax
}
resp.Secret.TTL = ttl
resp.Secret.TTL = lease.Lease
resp.Secret.MaxTTL = lease.LeaseMax
return resp, nil
}

View File

@@ -52,8 +52,10 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
lease = &configLease{}
}
f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System())
return f(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = lease.Lease
resp.Secret.MaxTTL = lease.LeaseMax
return resp, nil
}
func (b *backend) secretCredsRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -202,7 +202,6 @@ func TestBackend_renew_revoke(t *testing.T) {
}
generatedSecret := resp.Secret
generatedSecret.IssueTime = time.Now()
generatedSecret.TTL = 6 * time.Hour
var d struct {

View File

@@ -81,6 +81,7 @@ func (b *backend) pathTokenRead(ctx context.Context, req *logical.Request, d *fr
"accessor_id": token.AccessorID,
})
resp.Secret.TTL = leaseConfig.TTL
resp.Secret.MaxTTL = leaseConfig.MaxTTL
return resp, nil
}

View File

@@ -36,8 +36,10 @@ func (b *backend) secretTokenRenew(ctx context.Context, req *logical.Request, d
if lease == nil {
lease = &configLease{}
}
return framework.LeaseExtend(lease.TTL, lease.MaxTTL, b.System())(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = lease.TTL
resp.Secret.MaxTTL = lease.MaxTTL
return resp, nil
}
func (b *backend) secretTokenRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -5,6 +5,7 @@ import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
@@ -59,14 +60,16 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
lease = &configLease{}
}
f := framework.LeaseExtend(lease.Lease, lease.LeaseMax, b.System())
resp, err := f(ctx, req, d)
// Make sure we increase the VALID UNTIL endpoint for this user.
ttl, _, err := framework.CalculateTTL(b.System(), req.Secret.Increment, lease.Lease, 0, lease.LeaseMax, 0, req.Secret.IssueTime)
if err != nil {
return nil, err
}
// Make sure we increase the VALID UNTIL endpoint for this user.
if expireTime := resp.Secret.ExpirationTime(); !expireTime.IsZero() {
if ttl > 0 {
expireTime := time.Now().Add(ttl)
// Adding a small buffer since the TTL will be calculated again afeter this call
// to ensure the database credential does not expire before the lease
expireTime = expireTime.Add(5 * time.Second)
expiration := expireTime.Format("2006-01-02 15:04:05-0700")
query := fmt.Sprintf(
@@ -83,6 +86,9 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
}
}
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = lease.Lease
resp.Secret.MaxTTL = lease.LeaseMax
return resp, nil
}

View File

@@ -105,11 +105,8 @@ func (b *backend) pathCredsRead(ctx context.Context, req *logical.Request, d *fr
}
if lease != nil {
ttl := lease.TTL
if ttl == 0 || (lease.MaxTTL > 0 && ttl > lease.MaxTTL) {
ttl = lease.MaxTTL
}
resp.Secret.TTL = ttl
resp.Secret.TTL = lease.TTL
resp.Secret.MaxTTL = lease.MaxTTL
}
return resp, nil

View File

@@ -40,7 +40,10 @@ func (b *backend) secretCredsRenew(ctx context.Context, req *logical.Request, d
lease = &configLease{}
}
return framework.LeaseExtend(lease.TTL, lease.MaxTTL, b.System())(ctx, req, d)
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = lease.TTL
resp.Secret.MaxTTL = lease.MaxTTL
return resp, nil
}
// Revoke the previously issued secret

View File

@@ -32,8 +32,7 @@ func secretDynamicKey(b *backend) *framework.Secret {
}
func (b *backend) secretDynamicKeyRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
f := framework.LeaseExtend(0, 0, b.System())
return f(ctx, req, d)
return &logical.Response{Secret: req.Secret}, nil
}
func (b *backend) secretDynamicKeyRevoke(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

View File

@@ -70,7 +70,7 @@ func TestAuthTokenCreate(t *testing.T) {
t.Fatal(err)
}
if secret.Auth.LeaseDuration != 1800 {
t.Errorf("expected 1800 seconds, got %q", secret.Auth.LeaseDuration)
t.Errorf("expected 1800 seconds, got %d", secret.Auth.LeaseDuration)
}
explicitMaxCreateRequest.ExplicitMaxTTL = "2h"

View File

@@ -49,6 +49,10 @@ type Auth struct {
// specified by this period.
Period time.Duration `json:"period" mapstructure:"period" structs:"period"`
// ExplicitMaxTTL is the max TTL that constrains periodic tokens. For normal
// tokens, this value is constrained by the configured max ttl.
ExplicitMaxTTL time.Duration `json:"-" mapstructure:"-" structs:"-"`
// Number of allowed uses of the issued token
NumUses int `json:"num_uses" mapstructure:"num_uses" structs:"num_uses"`

View File

@@ -291,24 +291,6 @@ func (b *Backend) SanitizeTTLStr(ttlStr, maxTTLStr string) (ttl, maxTTL time.Dur
}
}
ttl, maxTTL, err = b.SanitizeTTL(ttl, maxTTL)
return
}
// SanitizeTTL caps the boundaries of ttl and max_ttl values to the
// backend mount's max_ttl value.
func (b *Backend) SanitizeTTL(ttl, maxTTL time.Duration) (time.Duration, time.Duration, error) {
sysMaxTTL := b.System().MaxLeaseTTL()
if ttl > sysMaxTTL {
return 0, 0, fmt.Errorf("\"ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
if maxTTL > sysMaxTTL {
return 0, 0, fmt.Errorf("\"max_ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
}
if ttl > maxTTL && maxTTL != 0 {
ttl = maxTTL
}
return ttl, maxTTL, nil
}

View File

@@ -245,37 +245,6 @@ func TestBackendHandleRequest_renew(t *testing.T) {
}
}
func TestBackendHandleRequest_renewExtend(t *testing.T) {
sysView := logical.StaticSystemView{
DefaultLeaseTTLVal: 5 * time.Minute,
MaxLeaseTTLVal: 30 * time.Hour,
}
secret := &Secret{
Type: "foo",
Renew: LeaseExtend(0, 0, sysView),
DefaultDuration: 5 * time.Minute,
}
b := &Backend{
Secrets: []*Secret{secret},
}
req := logical.RenewRequest("/foo", secret.Response(nil, nil).Secret, nil)
req.Secret.IssueTime = time.Now()
req.Secret.Increment = 1 * time.Hour
resp, err := b.HandleRequest(context.Background(), req)
if err != nil {
t.Fatalf("err: %s", err)
}
if resp == nil || resp.Secret == nil {
t.Fatal("should have secret")
}
if resp.Secret.TTL < 59*time.Minute || resp.Secret.TTL > 61*time.Minute {
t.Fatalf("bad: %s", resp.Secret.TTL)
}
}
func TestBackendHandleRequest_revoke(t *testing.T) {
var called uint32
callback := func(context.Context, *logical.Request, *FieldData) (*logical.Response, error) {

View File

@@ -8,80 +8,99 @@ import (
"github.com/hashicorp/vault/logical"
)
// LeaseExtend returns an OperationFunc that can be used to simply extend the
// lease of the auth/secret for the duration that was requested. The parameters
// provided are used to determine the lease's new TTL value.
//
// backendIncrement is the backend's requested increment -- perhaps from a user
// request, perhaps from a role/config value. If not set, uses the mount/system
// value.
//
// backendMax is the backend's requested increment -- this can be more
// restrictive than the mount/system value but not less.
//
// systemView is the system view from the calling backend, used to determine
// and/or correct default/max times.
// LeaseExtend is left for backwards compatibility for plugins. This function
// now just passes back the data that was passed into it to be processed in core.
// DEPRECATED
func LeaseExtend(backendIncrement, backendMax time.Duration, systemView logical.SystemView) OperationFunc {
return func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) {
var leaseOpts *logical.LeaseOptions
switch {
case req.Auth != nil:
leaseOpts = &req.Auth.LeaseOptions
req.Auth.TTL = backendIncrement
req.Auth.MaxTTL = backendMax
return &logical.Response{Auth: req.Auth}, nil
case req.Secret != nil:
leaseOpts = &req.Secret.LeaseOptions
default:
req.Secret.TTL = backendIncrement
req.Secret.MaxTTL = backendMax
return &logical.Response{Secret: req.Secret}, nil
}
return nil, fmt.Errorf("no lease options for request")
}
}
// CalculateTTL takes all the user-specified, backend, and system inputs and calculates
// a TTL for a lease
func CalculateTTL(sysView logical.SystemView, increment, backendTTL, period, backendMaxTTL, explicitMaxTTL time.Duration, startTime time.Time) (ttl time.Duration, warnings []string, errors error) {
// Truncate all times to the second since that is the lowest precision for
// TTLs
now := time.Now().Truncate(time.Second)
if startTime.IsZero() {
startTime = now
} else {
startTime = startTime.Truncate(time.Second)
}
// Use the mount's configured max unless the backend specifies
// something more restrictive (perhaps from a role configuration
// parameter)
max := systemView.MaxLeaseTTL()
if backendMax > 0 && backendMax < max {
max = backendMax
maxTTL := sysView.MaxLeaseTTL()
if backendMaxTTL > 0 && backendMaxTTL < maxTTL {
maxTTL = backendMaxTTL
}
if explicitMaxTTL > 0 && explicitMaxTTL < maxTTL {
maxTTL = explicitMaxTTL
}
// Should never happen, but guard anyways
if max < 0 {
return nil, fmt.Errorf("max TTL is negative")
if maxTTL <= 0 {
return 0, nil, fmt.Errorf("max TTL must be greater than zero")
}
var maxValidTime time.Time
switch {
case period > 0:
// Cap the period value to the sys max_ttl value
if period > maxTTL {
warnings = append(warnings,
fmt.Sprintf("period of %q exceeded the effective max_ttl of %q; period value is capped accordingly", period, maxTTL))
period = maxTTL
}
ttl = period
if explicitMaxTTL > 0 {
maxValidTime = startTime.Add(explicitMaxTTL)
}
default:
switch {
case increment > 0:
ttl = increment
case backendTTL > 0:
ttl = backendTTL
default:
ttl = sysView.DefaultLeaseTTL()
}
// We cannot go past this time
maxValidTime := leaseOpts.IssueTime.Add(max)
maxValidTime = startTime.Add(maxTTL)
}
// Get the current time
now := time.Now()
if !maxValidTime.IsZero() {
// Determine the max valid TTL
maxValidTTL := maxValidTime.Sub(now)
// If we are past the max TTL, we shouldn't be in this function...but
// fast path out if we are
if maxValidTime.Before(now) {
return nil, fmt.Errorf("past the max TTL, cannot renew")
if maxValidTTL < 0 {
return 0, nil, fmt.Errorf("past the max TTL, cannot renew")
}
// Basic max safety checks have passed, now let's figure out our
// increment. We'll use the user-supplied value first, then backend-provided default if possible, or the
// mount/system default if not.
increment := leaseOpts.Increment
if increment <= 0 {
if backendIncrement > 0 {
increment = backendIncrement
} else {
increment = systemView.DefaultLeaseTTL()
}
}
// We are proposing a time of the current time plus the increment
proposedExpiration := now.Add(increment)
// If the proposed expiration is after the maximum TTL of the lease,
// cap the increment to whatever is left
if maxValidTime.Before(proposedExpiration) {
increment = maxValidTime.Sub(now)
if maxValidTTL-ttl < 0 {
warnings = append(warnings,
fmt.Sprintf("TTL of %q exceeded the effective max_ttl of %q; TTL value is capped accordingly", ttl, maxValidTTL))
ttl = maxValidTTL
}
}
// Set the lease
leaseOpts.TTL = increment
return &logical.Response{Auth: req.Auth, Secret: req.Secret}, nil
}
return ttl, warnings, nil
}

View File

@@ -1,27 +1,26 @@
package framework
import (
"context"
"testing"
"time"
"github.com/hashicorp/vault/logical"
)
func TestLeaseExtend(t *testing.T) {
func TestCalculateTTL(t *testing.T) {
testSysView := logical.StaticSystemView{
DefaultLeaseTTLVal: 5 * time.Hour,
MaxLeaseTTLVal: 30 * time.Hour,
}
now := time.Now().Round(time.Hour)
cases := map[string]struct {
Increment time.Duration
BackendDefault time.Duration
BackendMax time.Duration
Increment time.Duration
Period time.Duration
ExplicitMaxTTL time.Duration
Result time.Duration
Warnings int
Error bool
}{
"valid request, good bounds, increment is preferred": {
@@ -52,6 +51,7 @@ func TestLeaseExtend(t *testing.T) {
BackendDefault: 40 * time.Hour,
BackendMax: 45 * time.Hour,
Result: 30 * time.Hour,
Warnings: 1,
},
"all request values are larger than the system view, so the system view limits": {
@@ -59,6 +59,7 @@ func TestLeaseExtend(t *testing.T) {
BackendMax: 50 * time.Hour,
Increment: 40 * time.Hour,
Result: 30 * time.Hour,
Warnings: 1,
},
"request within backend max": {
@@ -73,6 +74,7 @@ func TestLeaseExtend(t *testing.T) {
BackendMax: 4 * time.Hour,
Increment: 5 * time.Hour,
Result: 4 * time.Hour,
Warnings: 1,
},
"request is negative, no backend default, use sysview": {
@@ -83,22 +85,34 @@ func TestLeaseExtend(t *testing.T) {
"lease increment too large": {
Increment: 40 * time.Hour,
Result: 30 * time.Hour,
Warnings: 1,
},
"periodic, good request, period is preferred": {
Increment: 3 * time.Hour,
BackendDefault: 4 * time.Hour,
BackendMax: 2 * time.Hour,
Period: 1 * time.Hour,
Result: 1 * time.Hour,
},
"period too large, explicit max ttl is preferred": {
Period: 2 * time.Hour,
ExplicitMaxTTL: 1 * time.Hour,
Result: 1 * time.Hour,
Warnings: 1,
},
"period too large, capped by backend max": {
Period: 2 * time.Hour,
BackendMax: 1 * time.Hour,
Result: 1 * time.Hour,
Warnings: 1,
},
}
for name, tc := range cases {
req := &logical.Request{
Auth: &logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: 1 * time.Hour,
IssueTime: now,
Increment: tc.Increment,
},
},
}
callback := LeaseExtend(tc.BackendDefault, tc.BackendMax, testSysView)
resp, err := callback(context.Background(), req, nil)
ttl, warnings, err := CalculateTTL(testSysView, tc.Increment, tc.BackendDefault, tc.Period, tc.BackendMax, tc.ExplicitMaxTTL, time.Time{})
if (err != nil) != tc.Error {
t.Fatalf("bad: %s\nerr: %s", name, err)
}
@@ -107,9 +121,14 @@ func TestLeaseExtend(t *testing.T) {
}
// Round it to the nearest hour
lease := now.Add(resp.Auth.TTL).Round(time.Hour).Sub(now)
now := time.Now().Round(time.Hour)
lease := now.Add(ttl).Round(time.Hour).Sub(now)
if lease != tc.Result {
t.Fatalf("bad: %s\nlease: %s", name, lease)
}
if tc.Warnings != len(warnings) {
t.Fatalf("bad: %s\nwarning count mismatch, expect %d, got %d: %#v", name, tc.Warnings, len(warnings), warnings)
}
}
}

View File

@@ -7,10 +7,13 @@ import (
// LeaseOptions is an embeddable struct to capture common lease
// settings between a Secret and Auth
type LeaseOptions struct {
// Lease is the duration that this secret is valid for. Vault
// TTL is the duration that this secret is valid for. Vault
// will automatically revoke it after the duration.
TTL time.Duration `json:"lease"`
// MaxTTL is the maximum duration that this secret is valid for.
MaxTTL time.Duration `json:"max_ttl"`
// Renewable, if true, means that this secret can be renewed.
Renewable bool `json:"renewable"`

View File

@@ -581,6 +581,7 @@ type LeaseOptions struct {
Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable" json:"renewable,omitempty"`
Increment int64 `sentinel:"" protobuf:"varint,3,opt,name=increment" json:"increment,omitempty"`
IssueTime *google_protobuf.Timestamp `sentinel:"" protobuf:"bytes,4,opt,name=issue_time,json=issueTime" json:"issue_time,omitempty"`
MaxTTL int64 `sentinel:"" protobuf:"varint,5,opt,name=MaxTTL" json:"MaxTTL,omitempty"`
}
func (m *LeaseOptions) Reset() { *m = LeaseOptions{} }
@@ -616,6 +617,13 @@ func (m *LeaseOptions) GetIssueTime() *google_protobuf.Timestamp {
return nil
}
func (m *LeaseOptions) GetMaxTTL() int64 {
if m != nil {
return m.MaxTTL
}
return 0
}
type Secret struct {
LeaseOptions *LeaseOptions `sentinel:"" protobuf:"bytes,1,opt,name=lease_options,json=leaseOptions" json:"lease_options,omitempty"`
// InternalData is a JSON object that is stored with the secret.
@@ -2353,137 +2361,137 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 2101 bytes of a gzipped FileDescriptorProto
// 2112 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x5b, 0x6f, 0xdb, 0xc8,
0x15, 0x86, 0x24, 0x4b, 0xa2, 0x8e, 0x24, 0x5f, 0x26, 0x4e, 0xca, 0x28, 0xd9, 0x5a, 0xe5, 0x22,
0x59, 0x6d, 0xd0, 0xc8, 0x89, 0x7a, 0xcb, 0xb6, 0xd8, 0x2d, 0x5c, 0xdb, 0x9b, 0x75, 0xd7, 0xde,
0x35, 0x68, 0xa7, 0xdb, 0xa2, 0x05, 0xb4, 0x63, 0xf2, 0x58, 0x26, 0x4c, 0x91, 0xec, 0x70, 0x68,
0x47, 0x4f, 0xfd, 0x17, 0xed, 0x5f, 0xe9, 0x5b, 0x5f, 0x0b, 0xf4, 0xb9, 0xbf, 0xa0, 0xef, 0x7d,
0xe8, 0x2f, 0x28, 0xe6, 0x42, 0x6a, 0x28, 0xc9, 0x4d, 0x0a, 0xb4, 0x6f, 0x73, 0x2e, 0x33, 0xe7,
0xc2, 0x73, 0xbe, 0x33, 0x43, 0xd8, 0x09, 0xe3, 0x49, 0xe0, 0xd1, 0x70, 0x37, 0x09, 0xb3, 0x49,
0x10, 0xed, 0x26, 0x17, 0xbb, 0x17, 0xd4, 0xbb, 0xc6, 0xc8, 0x1f, 0x26, 0x2c, 0xe6, 0x31, 0xa9,
0x26, 0x17, 0xbd, 0x9d, 0x49, 0x1c, 0x4f, 0x42, 0xdc, 0x95, 0x9c, 0x8b, 0xec, 0x72, 0x97, 0x07,
0x53, 0x4c, 0x39, 0x9d, 0x26, 0x4a, 0xc9, 0x69, 0x42, 0xfd, 0x70, 0x9a, 0xf0, 0x99, 0xd3, 0x87,
0xc6, 0x17, 0x48, 0x7d, 0x64, 0xe4, 0x01, 0x34, 0xae, 0xe4, 0xca, 0xae, 0xf4, 0x6b, 0x83, 0x96,
0xab, 0x29, 0xe7, 0xb7, 0x00, 0xa7, 0x62, 0xcf, 0x21, 0x63, 0x31, 0x23, 0x0f, 0xc1, 0x42, 0xc6,
0xc6, 0x7c, 0x96, 0xa0, 0x5d, 0xe9, 0x57, 0x06, 0x5d, 0xb7, 0x89, 0x8c, 0x9d, 0xcf, 0x12, 0x24,
0xdf, 0x01, 0xb1, 0x1c, 0x4f, 0xd3, 0x89, 0x5d, 0xed, 0x57, 0xc4, 0x09, 0xc8, 0xd8, 0x49, 0x3a,
0xc9, 0xf7, 0x78, 0xb1, 0x8f, 0x76, 0xad, 0x5f, 0x19, 0xd4, 0xe4, 0x9e, 0xfd, 0xd8, 0x47, 0xe7,
0x8f, 0x15, 0xa8, 0x9f, 0x52, 0x7e, 0x95, 0x12, 0x02, 0x6b, 0x2c, 0x8e, 0xb9, 0x36, 0x2e, 0xd7,
0x64, 0x00, 0x1b, 0x59, 0x44, 0x33, 0x7e, 0x85, 0x11, 0x0f, 0x3c, 0xca, 0xd1, 0xb7, 0xab, 0x52,
0xbc, 0xc8, 0x26, 0x1f, 0x42, 0x37, 0x8c, 0x3d, 0x1a, 0x8e, 0x53, 0x1e, 0x33, 0x3a, 0x11, 0x76,
0x84, 0x5e, 0x47, 0x32, 0xcf, 0x14, 0x8f, 0x3c, 0x83, 0xad, 0x14, 0x69, 0x38, 0xbe, 0x65, 0x34,
0x29, 0x14, 0xd7, 0xd4, 0x81, 0x42, 0xf0, 0x0d, 0xa3, 0x89, 0xd6, 0x75, 0xfe, 0xd2, 0x80, 0xa6,
0x8b, 0xbf, 0xcf, 0x30, 0xe5, 0x64, 0x1d, 0xaa, 0x81, 0x2f, 0xa3, 0x6d, 0xb9, 0xd5, 0xc0, 0x27,
0x43, 0x20, 0x2e, 0x26, 0xa1, 0x30, 0x1d, 0xc4, 0xd1, 0x7e, 0x98, 0xa5, 0x1c, 0x99, 0x8e, 0x79,
0x85, 0x84, 0x3c, 0x86, 0x56, 0x9c, 0x20, 0x93, 0x3c, 0x99, 0x80, 0x96, 0x3b, 0x67, 0x88, 0xc0,
0x13, 0xca, 0xaf, 0xec, 0x35, 0x29, 0x90, 0x6b, 0xc1, 0xf3, 0x29, 0xa7, 0x76, 0x5d, 0xf1, 0xc4,
0x9a, 0x38, 0xd0, 0x48, 0xd1, 0x63, 0xc8, 0xed, 0x46, 0xbf, 0x32, 0x68, 0x8f, 0x60, 0x98, 0x5c,
0x0c, 0xcf, 0x24, 0xc7, 0xd5, 0x12, 0xf2, 0x18, 0xd6, 0x44, 0x5e, 0xec, 0xa6, 0xd4, 0xb0, 0x84,
0xc6, 0x5e, 0xc6, 0xaf, 0x5c, 0xc9, 0x25, 0x23, 0x68, 0xaa, 0x6f, 0x9a, 0xda, 0x56, 0xbf, 0x36,
0x68, 0x8f, 0x6c, 0xa1, 0xa0, 0xa3, 0x1c, 0xaa, 0x32, 0x48, 0x0f, 0x23, 0xce, 0x66, 0x6e, 0xae,
0x48, 0xbe, 0x07, 0x1d, 0x2f, 0x0c, 0x30, 0xe2, 0x63, 0x1e, 0x5f, 0x63, 0x64, 0xb7, 0xa4, 0x47,
0x6d, 0xc5, 0x3b, 0x17, 0x2c, 0x32, 0x82, 0xfb, 0xa6, 0xca, 0x98, 0x7a, 0x1e, 0xa6, 0x69, 0xcc,
0x6c, 0x90, 0xba, 0xf7, 0x0c, 0xdd, 0x3d, 0x2d, 0x12, 0xc7, 0xfa, 0x41, 0x9a, 0x84, 0x74, 0x36,
0x8e, 0xe8, 0x14, 0xed, 0xb6, 0x3a, 0x56, 0xf3, 0xbe, 0xa2, 0x53, 0x24, 0x3b, 0xd0, 0x9e, 0xc6,
0x59, 0xc4, 0xc7, 0x49, 0x1c, 0x44, 0xdc, 0xee, 0x48, 0x0d, 0x90, 0xac, 0x53, 0xc1, 0x21, 0x1f,
0x80, 0xa2, 0x54, 0x31, 0x76, 0x55, 0x5e, 0x25, 0x47, 0x96, 0xe3, 0x13, 0x58, 0x57, 0xe2, 0xc2,
0x9f, 0x75, 0xa9, 0xd2, 0x95, 0xdc, 0xc2, 0x93, 0x17, 0xd0, 0x92, 0xf5, 0x10, 0x44, 0x97, 0xb1,
0xbd, 0x21, 0xf3, 0x76, 0xcf, 0x48, 0x8b, 0xa8, 0x89, 0xa3, 0xe8, 0x32, 0x76, 0xad, 0x5b, 0xbd,
0x22, 0x9f, 0xc2, 0xa3, 0x52, 0xbc, 0x0c, 0xa7, 0x34, 0x88, 0x82, 0x68, 0x32, 0xce, 0x52, 0x4c,
0xed, 0x4d, 0x59, 0xe1, 0xb6, 0x11, 0xb5, 0x9b, 0x2b, 0xbc, 0x49, 0x31, 0x25, 0x8f, 0xa0, 0x25,
0xea, 0x96, 0xcf, 0xc6, 0x81, 0x6f, 0x6f, 0x49, 0x97, 0x2c, 0xc5, 0x38, 0xf2, 0xc9, 0x47, 0xb0,
0x91, 0xc4, 0x61, 0xe0, 0xcd, 0xc6, 0xf1, 0x0d, 0x32, 0x16, 0xf8, 0x68, 0x93, 0x7e, 0x65, 0x60,
0xb9, 0xeb, 0x8a, 0xfd, 0xb5, 0xe6, 0xae, 0x6a, 0x8d, 0x7b, 0x52, 0x71, 0xa9, 0x35, 0x86, 0x00,
0x5e, 0x1c, 0x45, 0xe8, 0xc9, 0xf2, 0xdb, 0x96, 0x11, 0xae, 0x8b, 0x08, 0xf7, 0x0b, 0xae, 0x6b,
0x68, 0xf4, 0x3e, 0x87, 0x8e, 0x59, 0x0a, 0x64, 0x13, 0x6a, 0xd7, 0x38, 0xd3, 0xe5, 0x2f, 0x96,
0xa4, 0x0f, 0xf5, 0x1b, 0x1a, 0x66, 0x28, 0x4b, 0x5e, 0x17, 0xa2, 0xda, 0xe2, 0x2a, 0xc1, 0x4f,
0xab, 0xaf, 0x2a, 0x0e, 0x85, 0xfa, 0x5e, 0x18, 0xd0, 0x74, 0xe1, 0x3b, 0x55, 0xde, 0xfd, 0x9d,
0xaa, 0xab, 0xbe, 0x13, 0x81, 0x35, 0x59, 0x29, 0xaa, 0x7f, 0xe4, 0xda, 0xf9, 0x57, 0x0d, 0xd6,
0x44, 0x7d, 0x93, 0x1f, 0x41, 0x37, 0x44, 0x9a, 0xe2, 0x38, 0x4e, 0x44, 0x0c, 0xa9, 0xb4, 0xd2,
0x1e, 0x6d, 0x0a, 0xcf, 0x8e, 0x85, 0xe0, 0x6b, 0xc5, 0x77, 0x3b, 0xa1, 0x41, 0x09, 0xd4, 0x08,
0x22, 0x8e, 0x2c, 0xa2, 0xe1, 0x58, 0xf6, 0x9b, 0xb2, 0xdc, 0xc9, 0x99, 0x07, 0xa2, 0xef, 0x16,
0x4b, 0xb5, 0xb6, 0x5c, 0xaa, 0x3d, 0xb0, 0xe4, 0xe7, 0x09, 0x30, 0xd5, 0x78, 0x52, 0xd0, 0x64,
0x04, 0xd6, 0x14, 0x39, 0xd5, 0xed, 0x2c, 0xba, 0xee, 0x41, 0xde, 0x96, 0xc3, 0x13, 0x2d, 0x50,
0x3d, 0x57, 0xe8, 0x2d, 0x35, 0x5d, 0x63, 0xb9, 0xe9, 0x7a, 0x60, 0x15, 0xf9, 0x6a, 0xaa, 0x22,
0xca, 0x69, 0x81, 0xe4, 0x09, 0xb2, 0x20, 0xf6, 0x6d, 0x4b, 0xd6, 0xa2, 0xa6, 0x04, 0x0e, 0x47,
0xd9, 0x54, 0x55, 0x69, 0x4b, 0xe1, 0x70, 0x94, 0x4d, 0x97, 0x8b, 0x12, 0x16, 0x8a, 0x72, 0x07,
0xea, 0x54, 0x7c, 0x49, 0xd9, 0xa5, 0xed, 0x51, 0x4b, 0xfa, 0x2f, 0x18, 0xae, 0xe2, 0x93, 0x21,
0x74, 0x27, 0x2c, 0xce, 0x92, 0xb1, 0x24, 0x31, 0xb5, 0x3b, 0x32, 0x50, 0x43, 0xb1, 0x23, 0xe5,
0x7b, 0x4a, 0xdc, 0xfb, 0x19, 0x74, 0x4b, 0xa1, 0xaf, 0xa8, 0xb1, 0x6d, 0xb3, 0xc6, 0x5a, 0x66,
0x5d, 0xfd, 0xa9, 0x02, 0x1d, 0xf3, 0x9b, 0x8a, 0xcd, 0xe7, 0xe7, 0xc7, 0x72, 0x73, 0xcd, 0x15,
0x4b, 0x01, 0xb8, 0x0c, 0x23, 0xbc, 0xa5, 0x17, 0xa1, 0x3a, 0xc0, 0x72, 0xe7, 0x0c, 0x21, 0x0d,
0x22, 0x8f, 0xe1, 0x14, 0x23, 0xae, 0xe7, 0xd1, 0x9c, 0x41, 0x3e, 0x01, 0x08, 0xd2, 0x34, 0xc3,
0xb1, 0x18, 0x99, 0x12, 0x94, 0xdb, 0xa3, 0xde, 0x50, 0xcd, 0xd3, 0x61, 0x3e, 0x4f, 0x87, 0xe7,
0xf9, 0x3c, 0x75, 0x5b, 0x52, 0x5b, 0xd0, 0xce, 0x1f, 0xa0, 0xa1, 0xf0, 0xf8, 0xff, 0x5a, 0x8f,
0x0f, 0xc1, 0x52, 0x67, 0x07, 0xbe, 0xae, 0xc5, 0xa6, 0xa4, 0x8f, 0x7c, 0xe7, 0x6f, 0x15, 0xb0,
0x5c, 0x4c, 0x93, 0x38, 0x4a, 0xd1, 0x98, 0x17, 0x95, 0x77, 0xce, 0x8b, 0xea, 0xca, 0x79, 0x91,
0x4f, 0xa1, 0x9a, 0x31, 0x85, 0x7a, 0x60, 0x31, 0xf4, 0x03, 0x86, 0x1e, 0xd7, 0x13, 0xab, 0xa0,
0x85, 0xec, 0x96, 0x32, 0x01, 0x74, 0xa9, 0x2c, 0xf5, 0x96, 0x5b, 0xd0, 0xe4, 0xa5, 0x09, 0xb3,
0x6a, 0x80, 0x6d, 0x2b, 0x98, 0x55, 0xee, 0x2e, 0xe3, 0xac, 0xf3, 0xd7, 0x2a, 0x6c, 0x2e, 0x8a,
0x57, 0x7c, 0xec, 0x6d, 0xa8, 0xab, 0x2e, 0xd1, 0x95, 0xc2, 0x97, 0xfa, 0xa3, 0xb6, 0xd0, 0x1f,
0x3f, 0x87, 0xae, 0xc7, 0x50, 0x4e, 0xdf, 0xf7, 0xfd, 0xca, 0x9d, 0x7c, 0x83, 0x60, 0x91, 0x8f,
0x61, 0x53, 0x78, 0x99, 0xa0, 0x3f, 0x07, 0x2d, 0x35, 0xaa, 0x37, 0x34, 0xbf, 0x80, 0xad, 0x67,
0xb0, 0x95, 0xab, 0xce, 0x1b, 0xac, 0x51, 0xd2, 0x3d, 0xcc, 0xfb, 0xec, 0x01, 0x34, 0x2e, 0x63,
0x36, 0xa5, 0x5c, 0x77, 0xb4, 0xa6, 0x44, 0x59, 0x14, 0xfe, 0xca, 0xab, 0x82, 0xa5, 0xca, 0x22,
0x67, 0x8a, 0x0b, 0x94, 0xe8, 0xe0, 0xe2, 0x72, 0x23, 0xbb, 0xdb, 0x72, 0xad, 0xfc, 0x52, 0xe3,
0xfc, 0x1a, 0x36, 0x16, 0xe6, 0xd9, 0x8a, 0x44, 0xce, 0xcd, 0x57, 0x4b, 0xe6, 0x4b, 0x27, 0xd7,
0x16, 0x4e, 0xfe, 0x0d, 0x6c, 0x7d, 0x41, 0x23, 0x3f, 0x44, 0x7d, 0xfe, 0x1e, 0x9b, 0x48, 0xc4,
0xd7, 0xd7, 0xab, 0xb1, 0xbe, 0x38, 0x75, 0xdd, 0x96, 0xe6, 0x1c, 0xf9, 0xe4, 0x09, 0x34, 0x99,
0xd2, 0xd6, 0x85, 0xd7, 0x36, 0x06, 0xae, 0x9b, 0xcb, 0x9c, 0x6f, 0x81, 0x94, 0x8e, 0x16, 0x37,
0xab, 0x19, 0x19, 0x88, 0x02, 0x54, 0x45, 0xa1, 0x0b, 0xbb, 0x63, 0xd6, 0x91, 0x5b, 0x48, 0x49,
0x1f, 0x6a, 0xc8, 0x98, 0x36, 0x21, 0x27, 0xde, 0xfc, 0x1e, 0xeb, 0x0a, 0x91, 0xf3, 0x43, 0xd8,
0x3a, 0x4b, 0xd0, 0x0b, 0x68, 0x28, 0xef, 0xa0, 0xca, 0xc0, 0x0e, 0xd4, 0x45, 0x92, 0xf3, 0x9e,
0x95, 0x20, 0xa6, 0xc4, 0x8a, 0xef, 0x7c, 0x0b, 0xb6, 0xf2, 0xeb, 0xf0, 0x6d, 0x90, 0x72, 0x8c,
0x3c, 0xdc, 0xbf, 0x42, 0xef, 0xfa, 0x7f, 0x18, 0xf9, 0x0d, 0x3c, 0x5c, 0x65, 0x21, 0xf7, 0xaf,
0xed, 0x09, 0x6a, 0x7c, 0x19, 0x67, 0x91, 0xb2, 0x61, 0xb9, 0x20, 0x59, 0x9f, 0x0b, 0x8e, 0xf8,
0x8e, 0x28, 0xf6, 0xa5, 0x1a, 0xfa, 0x34, 0x95, 0xe7, 0xa3, 0x76, 0x77, 0x3e, 0xfe, 0x5c, 0x81,
0xd6, 0x19, 0xf2, 0x2c, 0x91, 0xb1, 0x3c, 0x82, 0xd6, 0x05, 0x8b, 0xaf, 0x91, 0xcd, 0x43, 0xb1,
0x14, 0xe3, 0xc8, 0x27, 0x2f, 0xa1, 0xb1, 0x1f, 0x47, 0x97, 0xc1, 0x44, 0xde, 0xc8, 0xdb, 0xa3,
0x87, 0x0a, 0x5d, 0xf4, 0xde, 0xa1, 0x92, 0xa9, 0xb9, 0xa6, 0x15, 0x49, 0x1f, 0xda, 0xfa, 0xa5,
0xf2, 0xe6, 0xcd, 0xd1, 0x41, 0x3e, 0x47, 0x0d, 0x56, 0xef, 0x13, 0x68, 0x1b, 0x1b, 0xff, 0xab,
0xa9, 0xf0, 0x5d, 0x00, 0x69, 0x5d, 0xe5, 0x68, 0x53, 0x85, 0xaa, 0x77, 0x8a, 0xd0, 0x76, 0xa0,
0x25, 0x6e, 0x1b, 0x4a, 0x4c, 0x60, 0xcd, 0x78, 0xc0, 0xc8, 0xb5, 0xf3, 0x04, 0xb6, 0x8e, 0xa2,
0x1b, 0x1a, 0x06, 0x3e, 0xe5, 0xf8, 0x25, 0xce, 0x64, 0x0a, 0x96, 0x3c, 0x70, 0xce, 0xa0, 0xa3,
0x9f, 0x08, 0xef, 0xe5, 0x63, 0x47, 0xfb, 0xf8, 0x9f, 0x9b, 0xe8, 0x63, 0xd8, 0xd0, 0x87, 0x1e,
0x07, 0xba, 0x85, 0xc4, 0x0c, 0x67, 0x78, 0x19, 0xbc, 0xd5, 0x47, 0x6b, 0xca, 0x79, 0x05, 0x9b,
0x86, 0x6a, 0x11, 0xce, 0x35, 0xce, 0xd2, 0xfc, 0xe9, 0x24, 0xd6, 0x79, 0x06, 0xaa, 0xf3, 0x0c,
0x38, 0xb0, 0xae, 0x77, 0xbe, 0x46, 0x7e, 0x47, 0x74, 0x5f, 0x16, 0x8e, 0xbc, 0x46, 0x7d, 0xf8,
0x53, 0xa8, 0xa3, 0x88, 0xd4, 0x1c, 0x61, 0x66, 0x06, 0x5c, 0x25, 0x5e, 0x61, 0xf0, 0x55, 0x61,
0xf0, 0x34, 0x53, 0x06, 0xdf, 0xf3, 0x2c, 0xe7, 0xc3, 0xc2, 0x8d, 0xd3, 0x8c, 0xdf, 0xf5, 0x45,
0x9f, 0xc0, 0x96, 0x56, 0x3a, 0xc0, 0x10, 0x39, 0xde, 0x11, 0xd2, 0x53, 0x20, 0x25, 0xb5, 0xbb,
0x8e, 0x7b, 0x0c, 0xd6, 0xf9, 0xf9, 0x71, 0x21, 0x2d, 0x63, 0xa3, 0xf3, 0x29, 0x6c, 0x9d, 0x65,
0x7e, 0x7c, 0xca, 0x82, 0x9b, 0x20, 0xc4, 0x89, 0x32, 0x96, 0xbf, 0xdc, 0x2a, 0xc6, 0xcb, 0x6d,
0xe5, 0x34, 0x72, 0x06, 0x40, 0x4a, 0xdb, 0x8b, 0xef, 0x96, 0x66, 0x7e, 0xac, 0x5b, 0x58, 0xae,
0x9d, 0x01, 0x74, 0xce, 0xa9, 0x98, 0xf7, 0xbe, 0xd2, 0xb1, 0xa1, 0xc9, 0x15, 0xad, 0xd5, 0x72,
0xd2, 0x19, 0xc1, 0xf6, 0x3e, 0xf5, 0xae, 0x82, 0x68, 0x72, 0x10, 0xa4, 0xe2, 0x62, 0xa3, 0x77,
0xf4, 0xc0, 0xf2, 0x35, 0x43, 0x6f, 0x29, 0x68, 0xe7, 0x39, 0xdc, 0x37, 0xde, 0xa7, 0x67, 0x9c,
0xe6, 0xf9, 0xd8, 0x86, 0x7a, 0x2a, 0x28, 0xb9, 0xa3, 0xee, 0x2a, 0xc2, 0xf9, 0x0a, 0xb6, 0xcd,
0x01, 0x2c, 0xae, 0x1f, 0x79, 0xe0, 0xf2, 0x62, 0x50, 0x31, 0x2e, 0x06, 0x3a, 0x67, 0xd5, 0xf9,
0x3c, 0xd9, 0x84, 0xda, 0x2f, 0xbf, 0x39, 0xd7, 0xc5, 0x2e, 0x96, 0xce, 0xef, 0x84, 0xf9, 0xf2,
0x79, 0xca, 0x7c, 0xe9, 0x76, 0x50, 0x79, 0x9f, 0xdb, 0xc1, 0x8a, 0x7a, 0x7b, 0x0e, 0x5b, 0x27,
0x61, 0xec, 0x5d, 0x1f, 0x46, 0x46, 0x36, 0x6c, 0x68, 0x62, 0x64, 0x26, 0x23, 0x27, 0x9d, 0x8f,
0x60, 0xe3, 0x38, 0xf6, 0x68, 0x78, 0x22, 0x9e, 0x19, 0x45, 0x16, 0xe4, 0x0f, 0x03, 0xad, 0xaa,
0x08, 0xe7, 0x39, 0xc0, 0xfc, 0xa9, 0x24, 0xe0, 0x97, 0xe1, 0x34, 0xe6, 0x38, 0xa6, 0xbe, 0x9f,
0x57, 0x10, 0x28, 0xd6, 0x9e, 0xef, 0xb3, 0xd1, 0x3f, 0xab, 0xd0, 0xfc, 0x85, 0x02, 0x35, 0xf2,
0x19, 0x74, 0x4b, 0x23, 0x8c, 0xdc, 0x97, 0x6f, 0xa5, 0xc5, 0x81, 0xd9, 0x7b, 0xb0, 0xc4, 0x56,
0x0e, 0xbd, 0x80, 0x8e, 0x39, 0xa0, 0x88, 0x1c, 0x46, 0xf2, 0xc7, 0x4d, 0x4f, 0x9e, 0xb4, 0x3c,
0xbd, 0xce, 0x60, 0x7b, 0xd5, 0xe8, 0x20, 0x8f, 0xe7, 0x16, 0x96, 0xc7, 0x56, 0xef, 0x83, 0xbb,
0xa4, 0xf9, 0xc8, 0x69, 0xee, 0x87, 0x48, 0xa3, 0x2c, 0x31, 0x3d, 0x98, 0x2f, 0xc9, 0x4b, 0xe8,
0x96, 0xc0, 0x53, 0xc5, 0xb9, 0x84, 0xa7, 0xe6, 0x96, 0xa7, 0x50, 0x97, 0x80, 0x4d, 0xba, 0xa5,
0xc9, 0xd1, 0x5b, 0x2f, 0x48, 0x65, 0xbb, 0x0f, 0x6b, 0xf2, 0x99, 0x68, 0x18, 0x96, 0x3b, 0x0a,
0x34, 0x1f, 0xfd, 0xbd, 0x02, 0xcd, 0xfc, 0x17, 0xcf, 0x4b, 0x58, 0x13, 0xb8, 0x48, 0xee, 0x19,
0xd0, 0x92, 0x63, 0x6a, 0x6f, 0x7b, 0x81, 0xa9, 0x0c, 0x0c, 0xa1, 0xf6, 0x1a, 0x39, 0x21, 0x86,
0x50, 0x03, 0x64, 0xef, 0x5e, 0x99, 0x57, 0xe8, 0x9f, 0x66, 0x65, 0x7d, 0x8d, 0x6f, 0x25, 0xfd,
0x02, 0xb9, 0x7e, 0x02, 0x0d, 0x85, 0x3c, 0x2a, 0x29, 0x4b, 0x98, 0xa5, 0x3e, 0xfe, 0x32, 0x46,
0x8d, 0xfe, 0x51, 0x03, 0x38, 0x9b, 0xa5, 0x1c, 0xa7, 0xbf, 0x0a, 0xf0, 0x96, 0x3c, 0x83, 0x8d,
0x03, 0xbc, 0xa4, 0x59, 0xc8, 0xe5, 0x0b, 0x42, 0x74, 0x98, 0x91, 0x13, 0x79, 0x09, 0x2a, 0x00,
0xec, 0x29, 0xb4, 0x4f, 0xe8, 0xdb, 0x77, 0xeb, 0x7d, 0x06, 0xdd, 0x12, 0x2e, 0x69, 0x17, 0x17,
0x91, 0x4e, 0xbb, 0xb8, 0x8c, 0x60, 0x4f, 0xa1, 0xa9, 0xd1, 0xca, 0xb4, 0x21, 0x71, 0xbd, 0x84,
0x62, 0x3f, 0x86, 0x8d, 0x05, 0xac, 0x32, 0xf5, 0xe5, 0x6f, 0xa8, 0x95, 0x58, 0xf6, 0x4a, 0xbc,
0x00, 0xca, 0x78, 0x65, 0x6e, 0x7c, 0xa8, 0x30, 0x62, 0x15, 0xa0, 0xbd, 0x2e, 0xbf, 0x1d, 0xe4,
0xcb, 0xc9, 0x5e, 0x84, 0x94, 0x1c, 0xd0, 0xf2, 0x83, 0x56, 0x41, 0xd3, 0x0b, 0xe8, 0x98, 0xa8,
0xb2, 0xd4, 0x82, 0xcb, 0x90, 0xf3, 0x7d, 0x80, 0x39, 0xb0, 0x98, 0xfa, 0xb2, 0x3c, 0x16, 0x30,
0xe7, 0xa2, 0x21, 0x5f, 0x1b, 0x3f, 0xf8, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4e, 0x53, 0x71,
0xaf, 0xd8, 0x15, 0x00, 0x00,
0x47, 0x4f, 0xfd, 0x17, 0xfd, 0x1b, 0x7d, 0xed, 0x5b, 0x5f, 0x0b, 0xf4, 0xb9, 0xbf, 0xa0, 0xef,
0x7d, 0xe8, 0x2f, 0x28, 0xe6, 0x42, 0x6a, 0x28, 0xc9, 0x4d, 0x0a, 0xb4, 0x6f, 0x73, 0x2e, 0x33,
0xe7, 0xc2, 0x73, 0xbe, 0x33, 0x43, 0xd8, 0x09, 0xe3, 0x49, 0xe0, 0xd1, 0x70, 0x37, 0x09, 0xb3,
0x49, 0x10, 0xed, 0x26, 0x17, 0xbb, 0x17, 0xd4, 0xbb, 0xc6, 0xc8, 0x1f, 0x26, 0x2c, 0xe6, 0x31,
0xa9, 0x26, 0x17, 0xbd, 0x9d, 0x49, 0x1c, 0x4f, 0x42, 0xdc, 0x95, 0x9c, 0x8b, 0xec, 0x72, 0x97,
0x07, 0x53, 0x4c, 0x39, 0x9d, 0x26, 0x4a, 0xc9, 0x69, 0x42, 0xfd, 0x70, 0x9a, 0xf0, 0x99, 0xd3,
0x87, 0xc6, 0x17, 0x48, 0x7d, 0x64, 0xe4, 0x01, 0x34, 0xae, 0xe4, 0xca, 0xae, 0xf4, 0x6b, 0x83,
0x96, 0xab, 0x29, 0xe7, 0xb7, 0x00, 0xa7, 0x62, 0xcf, 0x21, 0x63, 0x31, 0x23, 0x0f, 0xc1, 0x42,
0xc6, 0xc6, 0x7c, 0x96, 0xa0, 0x5d, 0xe9, 0x57, 0x06, 0x5d, 0xb7, 0x89, 0x8c, 0x9d, 0xcf, 0x12,
0x24, 0xdf, 0x01, 0xb1, 0x1c, 0x4f, 0xd3, 0x89, 0x5d, 0xed, 0x57, 0xc4, 0x09, 0xc8, 0xd8, 0x49,
0x3a, 0xc9, 0xf7, 0x78, 0xb1, 0x8f, 0x76, 0xad, 0x5f, 0x19, 0xd4, 0xe4, 0x9e, 0xfd, 0xd8, 0x47,
0xe7, 0x8f, 0x15, 0xa8, 0x9f, 0x52, 0x7e, 0x95, 0x12, 0x02, 0x6b, 0x2c, 0x8e, 0xb9, 0x36, 0x2e,
0xd7, 0x64, 0x00, 0x1b, 0x59, 0x44, 0x33, 0x7e, 0x85, 0x11, 0x0f, 0x3c, 0xca, 0xd1, 0xb7, 0xab,
0x52, 0xbc, 0xc8, 0x26, 0x1f, 0x42, 0x37, 0x8c, 0x3d, 0x1a, 0x8e, 0x53, 0x1e, 0x33, 0x3a, 0x11,
0x76, 0x84, 0x5e, 0x47, 0x32, 0xcf, 0x14, 0x8f, 0x3c, 0x83, 0xad, 0x14, 0x69, 0x38, 0xbe, 0x65,
0x34, 0x29, 0x14, 0xd7, 0xd4, 0x81, 0x42, 0xf0, 0x0d, 0xa3, 0x89, 0xd6, 0x75, 0xfe, 0xd2, 0x80,
0xa6, 0x8b, 0xbf, 0xcf, 0x30, 0xe5, 0x64, 0x1d, 0xaa, 0x81, 0x2f, 0xa3, 0x6d, 0xb9, 0xd5, 0xc0,
0x27, 0x43, 0x20, 0x2e, 0x26, 0xa1, 0x30, 0x1d, 0xc4, 0xd1, 0x7e, 0x98, 0xa5, 0x1c, 0x99, 0x8e,
0x79, 0x85, 0x84, 0x3c, 0x86, 0x56, 0x9c, 0x20, 0x93, 0x3c, 0x99, 0x80, 0x96, 0x3b, 0x67, 0x88,
0xc0, 0x13, 0xca, 0xaf, 0xec, 0x35, 0x29, 0x90, 0x6b, 0xc1, 0xf3, 0x29, 0xa7, 0x76, 0x5d, 0xf1,
0xc4, 0x9a, 0x38, 0xd0, 0x48, 0xd1, 0x63, 0xc8, 0xed, 0x46, 0xbf, 0x32, 0x68, 0x8f, 0x60, 0x98,
0x5c, 0x0c, 0xcf, 0x24, 0xc7, 0xd5, 0x12, 0xf2, 0x18, 0xd6, 0x44, 0x5e, 0xec, 0xa6, 0xd4, 0xb0,
0x84, 0xc6, 0x5e, 0xc6, 0xaf, 0x5c, 0xc9, 0x25, 0x23, 0x68, 0xaa, 0x6f, 0x9a, 0xda, 0x56, 0xbf,
0x36, 0x68, 0x8f, 0x6c, 0xa1, 0xa0, 0xa3, 0x1c, 0xaa, 0x32, 0x48, 0x0f, 0x23, 0xce, 0x66, 0x6e,
0xae, 0x48, 0xbe, 0x07, 0x1d, 0x2f, 0x0c, 0x30, 0xe2, 0x63, 0x1e, 0x5f, 0x63, 0x64, 0xb7, 0xa4,
0x47, 0x6d, 0xc5, 0x3b, 0x17, 0x2c, 0x32, 0x82, 0xfb, 0xa6, 0xca, 0x98, 0x7a, 0x1e, 0xa6, 0x69,
0xcc, 0x6c, 0x90, 0xba, 0xf7, 0x0c, 0xdd, 0x3d, 0x2d, 0x12, 0xc7, 0xfa, 0x41, 0x9a, 0x84, 0x74,
0x36, 0x8e, 0xe8, 0x14, 0xed, 0xb6, 0x3a, 0x56, 0xf3, 0xbe, 0xa2, 0x53, 0x24, 0x3b, 0xd0, 0x9e,
0xc6, 0x59, 0xc4, 0xc7, 0x49, 0x1c, 0x44, 0xdc, 0xee, 0x48, 0x0d, 0x90, 0xac, 0x53, 0xc1, 0x21,
0x1f, 0x80, 0xa2, 0x54, 0x31, 0x76, 0x55, 0x5e, 0x25, 0x47, 0x96, 0xe3, 0x13, 0x58, 0x57, 0xe2,
0xc2, 0x9f, 0x75, 0xa9, 0xd2, 0x95, 0xdc, 0xc2, 0x93, 0x17, 0xd0, 0x92, 0xf5, 0x10, 0x44, 0x97,
0xb1, 0xbd, 0x21, 0xf3, 0x76, 0xcf, 0x48, 0x8b, 0xa8, 0x89, 0xa3, 0xe8, 0x32, 0x76, 0xad, 0x5b,
0xbd, 0x22, 0x9f, 0xc2, 0xa3, 0x52, 0xbc, 0x0c, 0xa7, 0x34, 0x88, 0x82, 0x68, 0x32, 0xce, 0x52,
0x4c, 0xed, 0x4d, 0x59, 0xe1, 0xb6, 0x11, 0xb5, 0x9b, 0x2b, 0xbc, 0x49, 0x31, 0x25, 0x8f, 0xa0,
0x25, 0xea, 0x96, 0xcf, 0xc6, 0x81, 0x6f, 0x6f, 0x49, 0x97, 0x2c, 0xc5, 0x38, 0xf2, 0xc9, 0x47,
0xb0, 0x91, 0xc4, 0x61, 0xe0, 0xcd, 0xc6, 0xf1, 0x0d, 0x32, 0x16, 0xf8, 0x68, 0x93, 0x7e, 0x65,
0x60, 0xb9, 0xeb, 0x8a, 0xfd, 0xb5, 0xe6, 0xae, 0x6a, 0x8d, 0x7b, 0x52, 0x71, 0xa9, 0x35, 0x86,
0x00, 0x5e, 0x1c, 0x45, 0xe8, 0xc9, 0xf2, 0xdb, 0x96, 0x11, 0xae, 0x8b, 0x08, 0xf7, 0x0b, 0xae,
0x6b, 0x68, 0xf4, 0x3e, 0x87, 0x8e, 0x59, 0x0a, 0x64, 0x13, 0x6a, 0xd7, 0x38, 0xd3, 0xe5, 0x2f,
0x96, 0xa4, 0x0f, 0xf5, 0x1b, 0x1a, 0x66, 0x28, 0x4b, 0x5e, 0x17, 0xa2, 0xda, 0xe2, 0x2a, 0xc1,
0x4f, 0xab, 0xaf, 0x2a, 0x0e, 0x85, 0xfa, 0x5e, 0x18, 0xd0, 0x74, 0xe1, 0x3b, 0x55, 0xde, 0xfd,
0x9d, 0xaa, 0xab, 0xbe, 0x13, 0x81, 0x35, 0x59, 0x29, 0xaa, 0x7f, 0xe4, 0xda, 0xf9, 0x57, 0x0d,
0xd6, 0x44, 0x7d, 0x93, 0x1f, 0x41, 0x37, 0x44, 0x9a, 0xe2, 0x38, 0x4e, 0x44, 0x0c, 0xa9, 0xb4,
0xd2, 0x1e, 0x6d, 0x0a, 0xcf, 0x8e, 0x85, 0xe0, 0x6b, 0xc5, 0x77, 0x3b, 0xa1, 0x41, 0x09, 0xd4,
0x08, 0x22, 0x8e, 0x2c, 0xa2, 0xe1, 0x58, 0xf6, 0x9b, 0xb2, 0xdc, 0xc9, 0x99, 0x07, 0xa2, 0xef,
0x16, 0x4b, 0xb5, 0xb6, 0x5c, 0xaa, 0x3d, 0xb0, 0xe4, 0xe7, 0x09, 0x30, 0xd5, 0x78, 0x52, 0xd0,
0x64, 0x04, 0xd6, 0x14, 0x39, 0xd5, 0xed, 0x2c, 0xba, 0xee, 0x41, 0xde, 0x96, 0xc3, 0x13, 0x2d,
0x50, 0x3d, 0x57, 0xe8, 0x2d, 0x35, 0x5d, 0x63, 0xb9, 0xe9, 0x7a, 0x60, 0x15, 0xf9, 0x6a, 0xaa,
0x22, 0xca, 0x69, 0x81, 0xe4, 0x09, 0xb2, 0x20, 0xf6, 0x6d, 0x4b, 0xd6, 0xa2, 0xa6, 0x04, 0x0e,
0x47, 0xd9, 0x54, 0x55, 0x69, 0x4b, 0xe1, 0x70, 0x94, 0x4d, 0x97, 0x8b, 0x12, 0x16, 0x8a, 0x72,
0x07, 0xea, 0x54, 0x7c, 0x49, 0xd9, 0xa5, 0xed, 0x51, 0x4b, 0xfa, 0x2f, 0x18, 0xae, 0xe2, 0x93,
0x21, 0x74, 0x27, 0x2c, 0xce, 0x92, 0xb1, 0x24, 0x31, 0xb5, 0x3b, 0x32, 0x50, 0x43, 0xb1, 0x23,
0xe5, 0x7b, 0x4a, 0xdc, 0xfb, 0x19, 0x74, 0x4b, 0xa1, 0xaf, 0xa8, 0xb1, 0x6d, 0xb3, 0xc6, 0x5a,
0x66, 0x5d, 0xfd, 0xa9, 0x02, 0x1d, 0xf3, 0x9b, 0x8a, 0xcd, 0xe7, 0xe7, 0xc7, 0x72, 0x73, 0xcd,
0x15, 0x4b, 0x01, 0xb8, 0x0c, 0x23, 0xbc, 0xa5, 0x17, 0xa1, 0x3a, 0xc0, 0x72, 0xe7, 0x0c, 0x21,
0x0d, 0x22, 0x8f, 0xe1, 0x14, 0x23, 0xae, 0xe7, 0xd1, 0x9c, 0x41, 0x3e, 0x01, 0x08, 0xd2, 0x34,
0xc3, 0xb1, 0x18, 0x99, 0x12, 0x94, 0xdb, 0xa3, 0xde, 0x50, 0xcd, 0xd3, 0x61, 0x3e, 0x4f, 0x87,
0xe7, 0xf9, 0x3c, 0x75, 0x5b, 0x52, 0x5b, 0xd0, 0x22, 0xef, 0x27, 0xf4, 0xad, 0xf0, 0xa5, 0xae,
0xf2, 0xae, 0x28, 0xe7, 0x0f, 0xd0, 0x50, 0x38, 0xfd, 0x7f, 0xad, 0xd3, 0x87, 0x60, 0xa9, 0xb3,
0x03, 0x5f, 0xd7, 0x68, 0x53, 0xd2, 0x47, 0xbe, 0xf3, 0xb7, 0x0a, 0x58, 0x2e, 0xa6, 0x49, 0x1c,
0xa5, 0x68, 0xcc, 0x91, 0xca, 0x3b, 0xe7, 0x48, 0x75, 0xe5, 0x1c, 0xc9, 0xa7, 0x53, 0xcd, 0x98,
0x4e, 0x3d, 0xb0, 0x18, 0xfa, 0x01, 0x43, 0x8f, 0xeb, 0x49, 0x56, 0xd0, 0x42, 0x76, 0x4b, 0x99,
0x00, 0xc0, 0x54, 0xb6, 0x40, 0xcb, 0x2d, 0x68, 0xf2, 0xd2, 0x84, 0x5f, 0x35, 0xd8, 0xb6, 0x15,
0xfc, 0x2a, 0x77, 0x97, 0xf1, 0xd7, 0xf9, 0x6b, 0x15, 0x36, 0x17, 0xc5, 0x2b, 0x8a, 0x60, 0x1b,
0xea, 0xaa, 0x7b, 0x74, 0x05, 0xf1, 0xa5, 0xbe, 0xa9, 0x2d, 0xf4, 0xcd, 0xcf, 0xa1, 0xeb, 0x31,
0x94, 0x53, 0xf9, 0x7d, 0xbf, 0x7e, 0x27, 0xdf, 0x20, 0x0b, 0xe0, 0x63, 0xd8, 0x14, 0x5e, 0x26,
0xe8, 0xcf, 0xc1, 0x4c, 0x8d, 0xf0, 0x0d, 0xcd, 0x2f, 0xe0, 0xec, 0x19, 0x6c, 0xe5, 0xaa, 0xf3,
0xc6, 0x6b, 0x94, 0x74, 0x0f, 0xf3, 0xfe, 0x7b, 0x00, 0x8d, 0xcb, 0x98, 0x4d, 0x29, 0xd7, 0x9d,
0xae, 0x29, 0x51, 0x16, 0x85, 0xbf, 0xf2, 0x0a, 0x61, 0xa9, 0xb2, 0xc8, 0x99, 0xe2, 0x62, 0x25,
0x3a, 0xbb, 0xb8, 0xf4, 0xc8, 0xae, 0xb7, 0x5c, 0x2b, 0xbf, 0xec, 0x38, 0xbf, 0x86, 0x8d, 0x85,
0x39, 0xb7, 0x22, 0x91, 0x73, 0xf3, 0xd5, 0x92, 0xf9, 0xd2, 0xc9, 0xb5, 0x85, 0x93, 0x7f, 0x03,
0x5b, 0x5f, 0xd0, 0xc8, 0x0f, 0x51, 0x9f, 0xbf, 0xc7, 0x26, 0x72, 0x12, 0xe8, 0x6b, 0xd7, 0x58,
0x5f, 0xa8, 0xba, 0x6e, 0x4b, 0x73, 0x8e, 0x7c, 0xf2, 0x04, 0x9a, 0x4c, 0x69, 0xeb, 0xc2, 0x6b,
0x1b, 0x83, 0xd8, 0xcd, 0x65, 0xce, 0xb7, 0x40, 0x4a, 0x47, 0x8b, 0x1b, 0xd7, 0x8c, 0x0c, 0x44,
0x01, 0xaa, 0xa2, 0xd0, 0x85, 0xdd, 0x31, 0xeb, 0xc8, 0x2d, 0xa4, 0xa4, 0x0f, 0x35, 0x64, 0x4c,
0x9b, 0x90, 0x93, 0x70, 0x7e, 0xbf, 0x75, 0x85, 0xc8, 0xf9, 0x21, 0x6c, 0x9d, 0x25, 0xe8, 0x05,
0x34, 0x94, 0x77, 0x53, 0x65, 0x60, 0x07, 0xea, 0x22, 0xc9, 0x79, 0xcf, 0x4a, 0x70, 0x53, 0x62,
0xc5, 0x77, 0xbe, 0x05, 0x5b, 0xf9, 0x75, 0xf8, 0x36, 0x48, 0x39, 0x46, 0x1e, 0xee, 0x5f, 0xa1,
0x77, 0xfd, 0x3f, 0x8c, 0xfc, 0x06, 0x1e, 0xae, 0xb2, 0x90, 0xfb, 0xd7, 0xf6, 0x04, 0x35, 0xbe,
0x8c, 0xb3, 0x48, 0xd9, 0xb0, 0x5c, 0x90, 0xac, 0xcf, 0x05, 0x47, 0x7c, 0x47, 0x14, 0xfb, 0x52,
0x0d, 0x89, 0x9a, 0xca, 0xf3, 0x51, 0xbb, 0x3b, 0x1f, 0x7f, 0xae, 0x40, 0xeb, 0x0c, 0x79, 0x96,
0xc8, 0x58, 0x1e, 0x41, 0xeb, 0x82, 0xc5, 0xd7, 0xc8, 0xe6, 0xa1, 0x58, 0x8a, 0x71, 0xe4, 0x93,
0x97, 0xd0, 0xd8, 0x8f, 0xa3, 0xcb, 0x60, 0x22, 0x6f, 0xea, 0xed, 0xd1, 0x43, 0x85, 0x2e, 0x7a,
0xef, 0x50, 0xc9, 0xd4, 0xbc, 0xd3, 0x8a, 0xa4, 0x0f, 0x6d, 0xfd, 0x82, 0x79, 0xf3, 0xe6, 0xe8,
0x20, 0x9f, 0xaf, 0x06, 0xab, 0xf7, 0x09, 0xb4, 0x8d, 0x8d, 0xff, 0xd5, 0xb4, 0xf8, 0x2e, 0x80,
0xb4, 0xae, 0x72, 0xb4, 0xa9, 0x42, 0xd5, 0x3b, 0x45, 0x68, 0x3b, 0xd0, 0x12, 0xb7, 0x10, 0x25,
0x26, 0xb0, 0x66, 0x3c, 0x6c, 0xe4, 0xda, 0x79, 0x02, 0x5b, 0x47, 0xd1, 0x0d, 0x0d, 0x03, 0x9f,
0x72, 0xfc, 0x12, 0x67, 0x32, 0x05, 0x4b, 0x1e, 0x38, 0x67, 0xd0, 0xd1, 0x4f, 0x87, 0xf7, 0xf2,
0xb1, 0xa3, 0x7d, 0xfc, 0xcf, 0x4d, 0xf4, 0x31, 0x6c, 0xe8, 0x43, 0x8f, 0x03, 0xdd, 0x42, 0x62,
0xb6, 0x33, 0xbc, 0x0c, 0xde, 0xea, 0xa3, 0x35, 0xe5, 0xbc, 0x82, 0x4d, 0x43, 0xb5, 0x08, 0xe7,
0x1a, 0x67, 0x69, 0xfe, 0xa4, 0x12, 0xeb, 0x3c, 0x03, 0xd5, 0x79, 0x06, 0x1c, 0x58, 0xd7, 0x3b,
0x5f, 0x23, 0xbf, 0x23, 0xba, 0x2f, 0x0b, 0x47, 0x5e, 0xa3, 0x3e, 0xfc, 0x29, 0xd4, 0x51, 0x44,
0x6a, 0x8e, 0x30, 0x33, 0x03, 0xae, 0x12, 0xaf, 0x30, 0xf8, 0xaa, 0x30, 0x78, 0x9a, 0x29, 0x83,
0xef, 0x79, 0x96, 0xf3, 0x61, 0xe1, 0xc6, 0x69, 0xc6, 0xef, 0xfa, 0xa2, 0x4f, 0x60, 0x4b, 0x2b,
0x1d, 0x60, 0x88, 0x1c, 0xef, 0x08, 0xe9, 0x29, 0x90, 0x92, 0xda, 0x5d, 0xc7, 0x3d, 0x06, 0xeb,
0xfc, 0xfc, 0xb8, 0x90, 0x96, 0xb1, 0xd1, 0xf9, 0x14, 0xb6, 0xce, 0x32, 0x3f, 0x3e, 0x65, 0xc1,
0x4d, 0x10, 0xe2, 0x44, 0x19, 0xcb, 0x5f, 0x74, 0x15, 0xe3, 0x45, 0xb7, 0x72, 0x1a, 0x39, 0x03,
0x20, 0xa5, 0xed, 0xc5, 0x77, 0x4b, 0x33, 0x3f, 0xd6, 0x2d, 0x2c, 0xd7, 0xce, 0x00, 0x3a, 0xe7,
0x54, 0xcc, 0x7b, 0x5f, 0xe9, 0xd8, 0xd0, 0xe4, 0x8a, 0xd6, 0x6a, 0x39, 0xe9, 0x8c, 0x60, 0x7b,
0x9f, 0x7a, 0x57, 0x41, 0x34, 0x39, 0x08, 0x52, 0x71, 0xe1, 0xd1, 0x3b, 0x7a, 0x60, 0xf9, 0x9a,
0xa1, 0xb7, 0x14, 0xb4, 0xf3, 0x1c, 0xee, 0x1b, 0xef, 0xd6, 0x33, 0x4e, 0xf3, 0x7c, 0x6c, 0x43,
0x3d, 0x15, 0x94, 0xdc, 0x51, 0x77, 0x15, 0xe1, 0x7c, 0x05, 0xdb, 0xe6, 0x00, 0x16, 0xd7, 0x8f,
0x3c, 0x70, 0x79, 0x31, 0xa8, 0x18, 0x17, 0x03, 0x9d, 0xb3, 0xea, 0x7c, 0x9e, 0x6c, 0x42, 0xed,
0x97, 0xdf, 0x9c, 0xeb, 0x62, 0x17, 0x4b, 0xe7, 0x77, 0xc2, 0x7c, 0xf9, 0x3c, 0x65, 0xbe, 0x74,
0x3b, 0xa8, 0xbc, 0xcf, 0xed, 0x60, 0x45, 0xbd, 0x3d, 0x87, 0xad, 0x93, 0x30, 0xf6, 0xae, 0x0f,
0x23, 0x23, 0x1b, 0x36, 0x34, 0x31, 0x32, 0x93, 0x91, 0x93, 0xce, 0x47, 0xb0, 0x71, 0x1c, 0x7b,
0x34, 0x3c, 0x11, 0xcf, 0x8f, 0x22, 0x0b, 0xf2, 0x47, 0x82, 0x56, 0x55, 0x84, 0xf3, 0x1c, 0x60,
0xfe, 0x84, 0x12, 0xf0, 0xcb, 0x70, 0x1a, 0x73, 0x1c, 0x53, 0xdf, 0xcf, 0x2b, 0x08, 0x14, 0x6b,
0xcf, 0xf7, 0xd9, 0xe8, 0x9f, 0x55, 0x68, 0xfe, 0x42, 0x81, 0x1a, 0xf9, 0x0c, 0xba, 0xa5, 0x11,
0x46, 0xee, 0xcb, 0x37, 0xd4, 0xe2, 0xc0, 0xec, 0x3d, 0x58, 0x62, 0x2b, 0x87, 0x5e, 0x40, 0xc7,
0x1c, 0x50, 0x44, 0x0e, 0x23, 0xf9, 0x43, 0xa7, 0x27, 0x4f, 0x5a, 0x9e, 0x5e, 0x67, 0xb0, 0xbd,
0x6a, 0x74, 0x90, 0xc7, 0x73, 0x0b, 0xcb, 0x63, 0xab, 0xf7, 0xc1, 0x5d, 0xd2, 0x7c, 0xe4, 0x34,
0xf7, 0x43, 0xa4, 0x51, 0x96, 0x98, 0x1e, 0xcc, 0x97, 0xe4, 0x25, 0x74, 0x4b, 0xe0, 0xa9, 0xe2,
0x5c, 0xc2, 0x53, 0x73, 0xcb, 0x53, 0xa8, 0x4b, 0xc0, 0x26, 0xdd, 0xd2, 0xe4, 0xe8, 0xad, 0x17,
0xa4, 0xb2, 0xdd, 0x87, 0x35, 0xf9, 0x7c, 0x34, 0x0c, 0xcb, 0x1d, 0x05, 0x9a, 0x8f, 0xfe, 0x5e,
0x81, 0x66, 0xfe, 0xeb, 0xe7, 0x25, 0xac, 0x09, 0x5c, 0x24, 0xf7, 0x0c, 0x68, 0xc9, 0x31, 0xb5,
0xb7, 0xbd, 0xc0, 0x54, 0x06, 0x86, 0x50, 0x7b, 0x8d, 0x9c, 0x10, 0x43, 0xa8, 0x01, 0xb2, 0x77,
0xaf, 0xcc, 0x2b, 0xf4, 0x4f, 0xb3, 0xb2, 0xbe, 0xc6, 0xb7, 0x92, 0x7e, 0x81, 0x5c, 0x3f, 0x81,
0x86, 0x42, 0x1e, 0x95, 0x94, 0x25, 0xcc, 0x52, 0x1f, 0x7f, 0x19, 0xa3, 0x46, 0xff, 0xa8, 0x01,
0x9c, 0xcd, 0x52, 0x8e, 0xd3, 0x5f, 0x05, 0x78, 0x4b, 0x9e, 0xc1, 0xc6, 0x01, 0x5e, 0xd2, 0x2c,
0xe4, 0xf2, 0x05, 0x21, 0x3a, 0xcc, 0xc8, 0x89, 0xbc, 0x04, 0x15, 0x00, 0xf6, 0x14, 0xda, 0x27,
0xf4, 0xed, 0xbb, 0xf5, 0x3e, 0x83, 0x6e, 0x09, 0x97, 0xb4, 0x8b, 0x8b, 0x48, 0xa7, 0x5d, 0x5c,
0x46, 0xb0, 0xa7, 0xd0, 0xd4, 0x68, 0x65, 0xda, 0x90, 0xb8, 0x5e, 0x42, 0xb1, 0x1f, 0xc3, 0xc6,
0x02, 0x56, 0x99, 0xfa, 0xf2, 0xf7, 0xd4, 0x4a, 0x2c, 0x7b, 0x25, 0x5e, 0x00, 0x65, 0xbc, 0x32,
0x37, 0x3e, 0x54, 0x18, 0xb1, 0x0a, 0xd0, 0x5e, 0x97, 0xdf, 0x0e, 0xf2, 0xe5, 0x64, 0x2f, 0x42,
0x4a, 0x0e, 0x68, 0xf9, 0x41, 0xab, 0xa0, 0xe9, 0x05, 0x74, 0x4c, 0x54, 0x59, 0x6a, 0xc1, 0x65,
0xc8, 0xf9, 0x3e, 0xc0, 0x1c, 0x58, 0x4c, 0x7d, 0x59, 0x1e, 0x0b, 0x98, 0x73, 0xd1, 0x90, 0xaf,
0x8d, 0x1f, 0xfc, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x7b, 0x3e, 0xd0, 0xf0, 0x15, 0x00, 0x00,
}

View File

@@ -212,6 +212,8 @@ message LeaseOptions {
int64 increment = 3;
google.protobuf.Timestamp issue_time = 4;
int64 MaxTTL = 5;
}
message Secret {

View File

@@ -136,6 +136,7 @@ func ProtoLeaseOptionsToLogicalLeaseOptions(l *LeaseOptions) (logical.LeaseOptio
Renewable: l.Renewable,
Increment: time.Duration(l.Increment),
IssueTime: t,
MaxTTL: time.Duration(l.MaxTTL),
}, err
}
@@ -150,6 +151,7 @@ func LogicalLeaseOptionsToProtoLeaseOptions(l logical.LeaseOptions) (*LeaseOptio
Renewable: l.Renewable,
Increment: int64(l.Increment),
IssueTime: t,
MaxTTL: int64(l.MaxTTL),
}, err
}

View File

@@ -92,6 +92,7 @@ func TestTranslation_Request(t *testing.T) {
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Second,
MaxTTL: time.Second,
Renewable: true,
Increment: time.Second,
IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
@@ -104,6 +105,7 @@ func TestTranslation_Request(t *testing.T) {
Auth: &logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: time.Second,
MaxTTL: time.Second,
Renewable: true,
Increment: time.Second,
IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
@@ -192,6 +194,7 @@ func TestTranslation_Response(t *testing.T) {
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
TTL: time.Second,
MaxTTL: time.Second,
Renewable: true,
Increment: time.Second,
IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
@@ -204,6 +207,7 @@ func TestTranslation_Response(t *testing.T) {
Auth: &logical.Auth{
LeaseOptions: logical.LeaseOptions{
TTL: time.Second,
MaxTTL: time.Second,
Renewable: true,
Increment: time.Second,
IssueTime: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),

View File

@@ -381,7 +381,7 @@ func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) {
t.Fatalf("bad: %#v", resp)
}
if resp.Secret.TTL != c.maxLeaseTTL {
t.Fatalf("bad: %#v", resp.Secret)
t.Fatalf("bad: %#v, %d", resp.Secret, c.maxLeaseTTL)
}
if resp.Secret.LeaseID == "" {
t.Fatalf("bad: %#v", resp.Secret)
@@ -422,7 +422,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) {
t.Fatalf("bad: %#v", resp)
}
if resp.Secret.TTL != c.defaultLeaseTTL {
t.Fatalf("bad: %#v", resp.Secret)
t.Fatalf("bad: %#v, %d", resp.Secret, c.defaultLeaseTTL)
}
if resp.Secret.LeaseID == "" {
t.Fatalf("bad: %#v", resp.Secret)

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/helper/wrapping"

View File

@@ -21,6 +21,7 @@ import (
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/helper/locksutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
const (
@@ -640,21 +641,36 @@ func (m *ExpirationManager) Renew(leaseID string, increment time.Duration) (*log
return logical.ErrorResponse("lease does not correspond to a secret"), nil
}
sysView := m.router.MatchingSystemView(le.Path)
if sysView == nil {
return nil, fmt.Errorf("expiration: unable to retrieve system view from router")
}
// Attempt to renew the entry
resp, err := m.renewEntry(le, increment)
if err != nil {
return nil, err
}
// Fast-path if there is no lease
if resp == nil || resp.Secret == nil || !resp.Secret.LeaseEnabled() {
return resp, nil
if resp == nil {
return nil, nil
}
if resp.IsError() {
return &logical.Response{
Data: resp.Data,
}, nil
}
if resp.Secret == nil {
return nil, nil
}
// Validate the lease
if err := resp.Secret.Validate(); err != nil {
ttl, warnings, err := framework.CalculateTTL(sysView, increment, resp.Secret.TTL, 0, resp.Secret.MaxTTL, 0, le.IssueTime)
if err != nil {
return nil, err
}
for _, warning := range warnings {
resp.AddWarning(warning)
}
resp.Secret.TTL = ttl
// Attach the LeaseID
resp.Secret.LeaseID = leaseID
@@ -744,21 +760,16 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke
if err != nil {
return nil, err
}
if resp == nil {
return nil, nil
}
if resp.IsError() {
return &logical.Response{
Data: resp.Data,
}, nil
}
if resp.Auth == nil || !resp.Auth.LeaseEnabled() {
return &logical.Response{
Auth: resp.Auth,
}, nil
if resp.Auth == nil {
return nil, nil
}
sysView := m.router.MatchingSystemView(le.Path)
@@ -766,29 +777,18 @@ func (m *ExpirationManager) RenewToken(req *logical.Request, source string, toke
return nil, fmt.Errorf("expiration: unable to retrieve system view from router")
}
ttl, warnings, err := framework.CalculateTTL(sysView, increment, resp.Auth.TTL, resp.Auth.Period, resp.Auth.MaxTTL, resp.Auth.ExplicitMaxTTL, le.IssueTime)
if err != nil {
return nil, err
}
retResp := &logical.Response{}
switch {
case resp.Auth.Period > time.Duration(0):
// If it resp.Period is non-zero, use that as the TTL and override backend's
// call on TTL modification, such as a TTL value determined by
// framework.LeaseExtend call against the request. Also, cap period value to
// the sys/mount max value.
if resp.Auth.Period > sysView.MaxLeaseTTL() {
retResp.AddWarning(fmt.Sprintf("Period of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(resp.Auth.TTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds())))
resp.Auth.Period = sysView.MaxLeaseTTL()
}
resp.Auth.TTL = resp.Auth.Period
case resp.Auth.TTL > time.Duration(0):
// Cap TTL value to the sys/mount max value
if resp.Auth.TTL > sysView.MaxLeaseTTL() {
retResp.AddWarning(fmt.Sprintf("TTL of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(resp.Auth.TTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds())))
resp.Auth.TTL = sysView.MaxLeaseTTL()
}
for _, warning := range warnings {
retResp.AddWarning(warning)
}
resp.Auth.TTL = ttl
// Attach the ClientToken
resp.Auth.ClientToken = token
resp.Auth.Increment = 0
// Update the lease entry
le.Auth = resp.Auth
@@ -902,12 +902,6 @@ func (m *ExpirationManager) RegisterAuth(source string, auth *logical.Auth) erro
return err
}
// If it resp.Period is non-zero, override the TTL value determined
// by the backend.
if auth.Period > time.Duration(0) {
auth.TTL = auth.Period
}
// Create a lease entry
le := leaseEntry{
LeaseID: path.Join(source, saltedID),
@@ -1069,7 +1063,6 @@ func (m *ExpirationManager) renewEntry(le *leaseEntry, increment time.Duration)
secret.IssueTime = le.IssueTime
secret.Increment = increment
secret.LeaseID = ""
req := logical.RenewRequest(le.Path, &secret, le.Data)
resp, err := m.router.Route(m.quitContext, req)
if err != nil || (resp != nil && resp.IsError()) {

View File

@@ -796,8 +796,14 @@ func TestExpiration_RenewToken(t *testing.T) {
func TestExpiration_RenewToken_period(t *testing.T) {
exp := mockExpiration(t)
root, err := exp.tokenStore.rootToken(context.Background())
if err != nil {
root := &TokenEntry{
Policies: []string{"root"},
Path: "auth/token/root",
DisplayName: "root",
CreationTime: time.Now().Unix(),
Period: time.Minute,
}
if err := exp.tokenStore.create(context.Background(), root); err != nil {
t.Fatalf("err: %v", err)
}
@@ -810,7 +816,7 @@ func TestExpiration_RenewToken_period(t *testing.T) {
},
Period: time.Minute,
}
err = exp.RegisterAuth("auth/token/login", auth)
err := exp.RegisterAuth("auth/token/login", auth)
if err != nil {
t.Fatalf("err: %v", err)
}
@@ -869,7 +875,6 @@ func TestExpiration_RenewToken_period_backend(t *testing.T) {
LeaseOptions: logical.LeaseOptions{
TTL: 10 * time.Second,
Renewable: true,
IssueTime: time.Now(),
},
Period: 5 * time.Second,
}
@@ -888,8 +893,8 @@ func TestExpiration_RenewToken_period_backend(t *testing.T) {
if resp == nil {
t.Fatal("expected a response")
}
if resp.Auth.TTL > 5*time.Second {
t.Fatalf("expected TTL to be less than or equal to period, got: %s", resp.Auth.TTL)
if resp.Auth.TTL == 0 || resp.Auth.TTL > 5*time.Second {
t.Fatalf("expected TTL to be greater than zero and less than or equal to period, got: %s", resp.Auth.TTL)
}
// Wait another 3 seconds. If period works correctly, this should not fail
@@ -1293,7 +1298,7 @@ func TestExpiration_renewEntry(t *testing.T) {
ExpireTime: time.Now(),
}
resp, err := exp.renewEntry(le, time.Second)
resp, err := exp.renewEntry(le)
if err != nil {
t.Fatalf("err: %v", err)
}
@@ -1312,12 +1317,6 @@ func TestExpiration_renewEntry(t *testing.T) {
if !reflect.DeepEqual(req.Data, le.Data) {
t.Fatalf("Bad: %v", req)
}
if req.Secret.Increment != time.Second {
t.Fatalf("Bad: %v", req)
}
if req.Secret.IssueTime.IsZero() {
t.Fatalf("Bad: %v", req)
}
}
func TestExpiration_renewAuthEntry(t *testing.T) {
@@ -1360,7 +1359,7 @@ func TestExpiration_renewAuthEntry(t *testing.T) {
ExpireTime: time.Now().Add(time.Minute),
}
resp, err := exp.renewAuthEntry(&logical.Request{}, le, time.Second)
resp, err := exp.renewAuthEntry(&logical.Request{}, le)
if err != nil {
t.Fatalf("err: %v", err)
}
@@ -1379,12 +1378,6 @@ func TestExpiration_renewAuthEntry(t *testing.T) {
if req.Path != "login" {
t.Fatalf("Bad: %v", req)
}
if req.Auth.Increment != time.Second {
t.Fatalf("Bad: %v", req)
}
if req.Auth.IssueTime.IsZero() {
t.Fatalf("Bad: %v", req)
}
if req.Auth.InternalData["MySecret"] != "secret" {
t.Fatalf("Bad: %v", req)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/helper/wrapping"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
const (
@@ -290,25 +291,6 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
// We exclude renewal of a lease, since it does not need to be re-registered
if resp != nil && resp.Secret != nil && !strings.HasPrefix(req.Path, "sys/renew") &&
!strings.HasPrefix(req.Path, "sys/leases/renew") {
// Get the SystemView for the mount
sysView := c.router.MatchingSystemView(req.Path)
if sysView == nil {
c.logger.Error("unable to retrieve system view from router")
retErr = multierror.Append(retErr, ErrInternalError)
return nil, auth, retErr
}
// Apply the default lease if none given
if resp.Secret.TTL == 0 {
resp.Secret.TTL = sysView.DefaultLeaseTTL()
}
// Limit the lease duration
maxTTL := sysView.MaxLeaseTTL()
if resp.Secret.TTL > maxTTL {
resp.Secret.TTL = maxTTL
}
// KV mounts should return the TTL but not register
// for a lease as this provides a massive slowdown
registerLease := true
@@ -351,6 +333,21 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
}
if registerLease {
sysView := c.router.MatchingSystemView(req.Path)
if sysView == nil {
c.logger.Error("core: unable to look up sys view for login path", "request_path", req.Path)
return nil, nil, ErrInternalError
}
ttl, warnings, err := framework.CalculateTTL(sysView, 0, resp.Secret.TTL, 0, resp.Secret.MaxTTL, 0, time.Time{})
if err != nil {
return nil, nil, err
}
for _, warning := range warnings {
resp.AddWarning(warning)
}
resp.Secret.TTL = ttl
leaseID, err := c.expiration.Register(req, resp)
if err != nil {
c.logger.Error("failed to register lease", "request_path", req.Path, "error", err)
@@ -549,27 +546,12 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re
return nil, nil, ErrInternalError
}
// Start off with the sys default value, and update according to period/TTL
// from resp.Auth
tokenTTL := sysView.DefaultLeaseTTL()
switch {
case auth.Period > time.Duration(0):
// Cap the period value to the sys max_ttl value. The auth backend should
// have checked for it on its login path, but we check here again for
// sanity.
if auth.Period > sysView.MaxLeaseTTL() {
auth.Period = sysView.MaxLeaseTTL()
tokenTTL, warnings, err := framework.CalculateTTL(sysView, 0, auth.TTL, auth.Period, auth.MaxTTL, auth.ExplicitMaxTTL, time.Time{})
if err != nil {
return nil, nil, err
}
tokenTTL = auth.Period
case auth.TTL > time.Duration(0):
// Cap the TTL value. The auth backend should have checked for it on its
// login path (e.g. a call to b.SanitizeTTL), but we check here again for
// sanity.
if auth.TTL > sysView.MaxLeaseTTL() {
auth.TTL = sysView.MaxLeaseTTL()
}
tokenTTL = auth.TTL
for _, warning := range warnings {
resp.AddWarning(warning)
}
// Generate a token

View File

@@ -766,11 +766,11 @@ func (ts *TokenStore) create(ctx context.Context, entry *TokenEntry) error {
entry.ID = entryUUID
}
saltedId, err := ts.SaltID(ctx, entry.ID)
saltedID, err := ts.SaltID(ctx, entry.ID)
if err != nil {
return err
}
exist, _ := ts.lookupSalted(ctx, saltedId, true)
exist, _ := ts.lookupSalted(ctx, saltedID, true)
if exist != nil {
return fmt.Errorf("cannot create a token with a duplicate ID")
}
@@ -795,7 +795,7 @@ func (ts *TokenStore) store(ctx context.Context, entry *TokenEntry) error {
// storeCommon handles the actual storage of an entry, possibly generating
// secondary indexes
func (ts *TokenStore) storeCommon(ctx context.Context, entry *TokenEntry, writeSecondary bool) error {
saltedId, err := ts.SaltID(ctx, entry.ID)
saltedID, err := ts.SaltID(ctx, entry.ID)
if err != nil {
return err
}
@@ -826,7 +826,7 @@ func (ts *TokenStore) storeCommon(ctx context.Context, entry *TokenEntry, writeS
if err != nil {
return err
}
path := parentPrefix + parentSaltedID + "/" + saltedId
path := parentPrefix + parentSaltedID + "/" + saltedID
le := &logical.StorageEntry{Key: path}
if err := ts.view.Put(ctx, le); err != nil {
return fmt.Errorf("failed to persist entry: %v", err)
@@ -835,7 +835,7 @@ func (ts *TokenStore) storeCommon(ctx context.Context, entry *TokenEntry, writeS
}
// Write the primary ID
path := lookupPrefix + saltedId
path := lookupPrefix + saltedID
le := &logical.StorageEntry{Key: path, Value: enc}
if len(entry.Policies) == 1 && entry.Policies[0] == "root" {
le.SealWrap = true
@@ -1060,11 +1060,11 @@ func (ts *TokenStore) Revoke(ctx context.Context, id string) error {
// revokeSalted is used to invalidate a given salted token,
// any child tokens will be orphaned.
func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret error) {
func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string) (ret error) {
// Protect the entry lookup/writing with locks. The rub here is that we
// don't know the ID until we look it up once, so first we look it up, then
// do a locked lookup.
entry, err := ts.lookupSalted(ctx, saltedId, true)
entry, err := ts.lookupSalted(ctx, saltedID, true)
if err != nil {
return err
}
@@ -1076,7 +1076,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
lock.Lock()
// Lookup the token first
entry, err = ts.lookupSalted(ctx, saltedId, true)
entry, err = ts.lookupSalted(ctx, saltedID, true)
if err != nil {
lock.Unlock()
return err
@@ -1116,7 +1116,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
// Lookup the token again to make sure something else didn't
// revoke in the interim
entry, err := ts.lookupSalted(ctx, saltedId, true)
entry, err := ts.lookupSalted(ctx, saltedID, true)
if err != nil {
return
}
@@ -1132,7 +1132,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
// Destroy the token's cubby. This should go first as it's a
// security-sensitive item.
err = ts.cubbyholeDestroyer(ctx, ts, saltedId)
err = ts.cubbyholeDestroyer(ctx, ts, saltedID)
if err != nil {
return err
}
@@ -1150,7 +1150,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
return err
}
path := parentPrefix + parentSaltedID + "/" + saltedId
path := parentPrefix + parentSaltedID + "/" + saltedID
if err = ts.view.Delete(ctx, path); err != nil {
return fmt.Errorf("failed to delete entry: %v", err)
}
@@ -1177,7 +1177,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
// explicit call to orphan the child tokens (the delete occurs at the leaf
// node and uses parent prefix, not entry.Parent, to build the tree for
// traversal).
parentPath := parentPrefix + saltedId + "/"
parentPath := parentPrefix + saltedID + "/"
children, err := ts.view.List(ctx, parentPath)
if err != nil {
return fmt.Errorf("failed to scan for children: %v", err)
@@ -1203,7 +1203,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedId string) (ret er
}
// Now that the entry is not usable for any revocation tasks, nuke it
path := lookupPrefix + saltedId
path := lookupPrefix + saltedID
if err = ts.view.Delete(ctx, path); err != nil {
return fmt.Errorf("failed to delete entry: %v", err)
}
@@ -1221,25 +1221,22 @@ func (ts *TokenStore) RevokeTree(ctx context.Context, id string) error {
}
// Get the salted ID
saltedId, err := ts.SaltID(ctx, id)
saltedID, err := ts.SaltID(ctx, id)
if err != nil {
return err
}
// Nuke the entire tree recursively
if err := ts.revokeTreeSalted(ctx, saltedId); err != nil {
return err
}
return nil
return ts.revokeTreeSalted(ctx, saltedID)
}
// revokeTreeSalted is used to invalidate a given token and all
// child tokens using a saltedID.
// Updated to be non-recursive and revoke child tokens
// before parent tokens(DFS).
func (ts *TokenStore) revokeTreeSalted(ctx context.Context, saltedId string) error {
func (ts *TokenStore) revokeTreeSalted(ctx context.Context, saltedID string) error {
var dfs []string
dfs = append(dfs, saltedId)
dfs = append(dfs, saltedID)
for l := len(dfs); l > 0; l = len(dfs) {
id := dfs[0]
@@ -1467,13 +1464,13 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data
// Look up tainted variants so we only find entries that truly don't
// exist
saltedId, err := ts.SaltID(ctx, accessorEntry.TokenID)
saltedID, err := ts.SaltID(ctx, accessorEntry.TokenID)
if err != nil {
tidyErrors = multierror.Append(tidyErrors, fmt.Errorf("failed to read salt id: %v", err))
lock.RUnlock()
continue
}
te, err := ts.lookupSalted(ctx, saltedId, true)
te, err := ts.lookupSalted(ctx, saltedID, true)
if err != nil {
tidyErrors = multierror.Append(tidyErrors, fmt.Errorf("failed to lookup tainted ID: %v", err))
lock.RUnlock()
@@ -1486,7 +1483,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data
// more and conclude that accessor, leases, and secondary index entries
// for this token should not exist as well.
if te == nil {
ts.logger.Info("deleting token with nil entry", "salted_token", saltedId)
ts.logger.Info("deleting token with nil entry", "salted_token", saltedID)
// RevokeByToken expects a '*TokenEntry'. For the
// purposes of tidying, it is sufficient if the token
@@ -1884,6 +1881,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
te.EntityID = parent.EntityID
}
var explicitMaxTTLToUse time.Duration
if data.ExplicitMaxTTL != "" {
dur, err := parseutil.ParseDurationSecond(data.ExplicitMaxTTL)
if err != nil {
@@ -1893,6 +1891,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
return logical.ErrorResponse("explicit_max_ttl must be positive"), logical.ErrInvalidRequest
}
te.ExplicitMaxTTL = dur
explicitMaxTTLToUse = dur
}
var periodToUse time.Duration
@@ -1942,13 +1941,13 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
if role != nil {
if role.ExplicitMaxTTL != 0 {
switch {
case te.ExplicitMaxTTL == 0:
te.ExplicitMaxTTL = role.ExplicitMaxTTL
case explicitMaxTTLToUse == 0:
explicitMaxTTLToUse = role.ExplicitMaxTTL
default:
if role.ExplicitMaxTTL < te.ExplicitMaxTTL {
te.ExplicitMaxTTL = role.ExplicitMaxTTL
if role.ExplicitMaxTTL < explicitMaxTTLToUse {
explicitMaxTTLToUse = role.ExplicitMaxTTL
}
resp.AddWarning(fmt.Sprintf("Explicit max TTL specified both during creation call and in role; using the lesser value of %d seconds", int64(te.ExplicitMaxTTL.Seconds())))
resp.AddWarning(fmt.Sprintf("Explicit max TTL specified both during creation call and in role; using the lesser value of %d seconds", int64(explicitMaxTTLToUse.Seconds())))
}
}
if role.Period != 0 {
@@ -1966,49 +1965,21 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
sysView := ts.System()
if periodToUse > 0 {
// Cap period value to the sys/mount max value; this matches behavior
// in expiration manager for renewals
if periodToUse > sysView.MaxLeaseTTL() {
resp.AddWarning(fmt.Sprintf("Period of %d seconds is greater than current mount/system default of %d seconds, value will be truncated.", int64(periodToUse.Seconds()), int64(sysView.MaxLeaseTTL().Seconds())))
periodToUse = sysView.MaxLeaseTTL()
// Only calculate a TTL if you are A) periodic, B) have a TTL, C) do not have a TTL and are not a root token
if periodToUse > 0 || te.TTL > 0 || (te.TTL == 0 && !strutil.StrListContains(te.Policies, "root")) {
ttl, warnings, err := framework.CalculateTTL(sysView, 0, te.TTL, periodToUse, 0, explicitMaxTTLToUse, time.Unix(te.CreationTime, 0))
if err != nil {
return nil, err
}
te.TTL = periodToUse
} else {
// Set the default lease if not provided, root tokens are exempt
if te.TTL == 0 && !strutil.StrListContains(te.Policies, "root") {
te.TTL = sysView.DefaultLeaseTTL()
for _, warning := range warnings {
resp.AddWarning(warning)
}
te.TTL = ttl
}
// Limit the lease duration
if te.TTL > sysView.MaxLeaseTTL() && sysView.MaxLeaseTTL() != 0 {
te.TTL = sysView.MaxLeaseTTL()
}
}
// Run some bounding checks if the explicit max TTL is set; we do not check
// period as it's defined to escape the max TTL
if te.ExplicitMaxTTL > 0 {
// Limit the lease duration, except for periodic tokens -- in that case the explicit max limits the period, which itself can escape normal max
if sysView.MaxLeaseTTL() != 0 && te.ExplicitMaxTTL > sysView.MaxLeaseTTL() && periodToUse == 0 {
resp.AddWarning(fmt.Sprintf(
"Explicit max TTL of %d seconds is greater than system/mount allowed value; value is being capped to %d seconds",
int64(te.ExplicitMaxTTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds())))
te.ExplicitMaxTTL = sysView.MaxLeaseTTL()
}
if te.TTL == 0 {
// This won't be the case if it's periodic -- it will be set above
te.TTL = te.ExplicitMaxTTL
} else {
// Limit even in the periodic case
if te.TTL > te.ExplicitMaxTTL {
resp.AddWarning(fmt.Sprintf(
"Requested TTL of %d seconds higher than explicit max TTL; value being capped to %d seconds",
int64(te.TTL.Seconds()), int64(te.ExplicitMaxTTL.Seconds())))
te.TTL = te.ExplicitMaxTTL
}
}
// Root tokens are still bound by explicit max TTL
if te.TTL == 0 && explicitMaxTTLToUse > 0 {
te.TTL = explicitMaxTTLToUse
}
// Don't advertise non-expiring root tokens as renewable, as attempts to renew them are denied
@@ -2037,6 +2008,8 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque
ClientToken: te.ID,
Accessor: te.Accessor,
EntityID: te.EntityID,
Period: periodToUse,
ExplicitMaxTTL: explicitMaxTTLToUse,
}
if ts.policyLookupFunc != nil {
@@ -2166,11 +2139,11 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da
defer lock.RUnlock()
// Lookup the token
saltedId, err := ts.SaltID(ctx, id)
saltedID, err := ts.SaltID(ctx, id)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
out, err := ts.lookupSalted(ctx, saltedId, true)
out, err := ts.lookupSalted(ctx, saltedID, true)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
@@ -2301,68 +2274,23 @@ func (ts *TokenStore) authRenew(ctx context.Context, req *logical.Request, d *fr
return nil, fmt.Errorf("no token entry found during lookup")
}
f := framework.LeaseExtend(req.Auth.Increment, te.ExplicitMaxTTL, ts.System())
// If (te/role).Period is not zero, this is a periodic token. The TTL for a
// periodic token is always the same (the period value). It is not subject
// to normal maximum TTL checks that would come from calling LeaseExtend,
// so we fast path it.
//
// The one wrinkle here is if the token has an explicit max TTL. If both
// are set, we treat it as a regular token and use the periodic value as
// the increment.
// No role? Use normal LeaseExtend semantics, taking into account
// TokenEntry properties
if te.Role == "" {
//Explicit max TTL overrides the period, if both are set
if te.Period != 0 {
if te.ExplicitMaxTTL == 0 {
req.Auth.TTL = te.Period
req.Auth.Period = te.Period
req.Auth.ExplicitMaxTTL = te.ExplicitMaxTTL
return &logical.Response{Auth: req.Auth}, nil
} else {
maxTime := time.Unix(te.CreationTime, 0).Add(te.ExplicitMaxTTL)
if time.Now().Add(te.Period).After(maxTime) {
req.Auth.TTL = maxTime.Sub(time.Now())
} else {
req.Auth.TTL = te.Period
}
return &logical.Response{Auth: req.Auth}, nil
}
}
return f(ctx, req, d)
}
role, err := ts.tokenStoreRole(ctx, te.Role)
if err != nil {
return nil, fmt.Errorf("error looking up role %s: %s", te.Role, err)
}
if role == nil {
return nil, fmt.Errorf("original token role (%s) could not be found, not renewing", te.Role)
}
// Same deal here, but using the role period
if role.Period != 0 {
periodToUse := role.Period
if te.Period > 0 && te.Period < role.Period {
periodToUse = te.Period
}
if te.ExplicitMaxTTL == 0 {
req.Auth.TTL = periodToUse
req.Auth.Period = role.Period
req.Auth.ExplicitMaxTTL = role.ExplicitMaxTTL
return &logical.Response{Auth: req.Auth}, nil
} else {
maxTime := time.Unix(te.CreationTime, 0).Add(te.ExplicitMaxTTL)
if time.Now().Add(periodToUse).After(maxTime) {
req.Auth.TTL = maxTime.Sub(time.Now())
} else {
req.Auth.TTL = periodToUse
}
return &logical.Response{Auth: req.Auth}, nil
}
}
return f(ctx, req, d)
}
func (ts *TokenStore) tokenStoreRole(ctx context.Context, name string) (*tsRoleEntry, error) {

View File

@@ -59,11 +59,11 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) {
t.Fatal(err)
}
saltedId, err := ts.SaltID(context.Background(), entry.ID)
saltedID, err := ts.SaltID(context.Background(), entry.ID)
if err != nil {
t.Fatal(err)
}
path := lookupPrefix + saltedId
path := lookupPrefix + saltedID
le := &logical.StorageEntry{
Key: path,
Value: enc,
@@ -2381,7 +2381,7 @@ func TestTokenStore_RolePeriod(t *testing.T) {
}
ttl = resp.Data["ttl"].(int64)
if ttl > 8 {
t.Fatalf("TTL too large")
t.Fatalf("TTL too large: %d", ttl)
}
// Renewing should not have the increment increase since we've hit the
@@ -2596,9 +2596,10 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) {
if ttl > 10 {
t.Fatalf("TTL too big")
}
// explicit max ttl is stored in the role so not returned here
maxTTL := resp.Data["explicit_max_ttl"].(int64)
if maxTTL != 10 {
t.Fatalf("expected 6 for explicit max TTL, got %d", maxTTL)
if maxTTL != 0 {
t.Fatalf("expected 0 for explicit max TTL, got %d", maxTTL)
}
// Let the TTL go down a bit to ~7 seconds (8 against explicit max)
@@ -2622,7 +2623,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) {
}
ttl = resp.Data["ttl"].(int64)
if ttl > 8 {
t.Fatalf("TTL too big")
t.Fatalf("TTL too big: %d", ttl)
}
// Let the TTL go down a bit more to ~5 seconds (6 against explicit max)
@@ -2755,7 +2756,92 @@ func TestTokenStore_Periodic(t *testing.T) {
}
}
// Do the same with an explicit max TTL
// Now we create a token against the role and also set the te value
// directly. We should use the smaller of the two and be able to renew;
// increment should be ignored as well.
{
req.ClientToken = root
req.Operation = logical.UpdateOperation
req.Path = "auth/token/create/test"
req.Data = map[string]interface{}{
"period": 5,
}
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.Fatalf(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 < 4 || ttl > 5 {
t.Fatalf("TTL bad (expected %d, got %d)", 4, ttl)
}
// Let the TTL go down a bit
time.Sleep(2 * time.Second)
req.Operation = logical.UpdateOperation
req.Path = "auth/token/renew-self"
req.Data = map[string]interface{}{
"increment": 1,
}
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 > 5 {
t.Fatalf("TTL bad (expected less than %d, got %d)", 5, ttl)
}
}
}
func TestTokenStore_Periodic_ExplicitMax(t *testing.T) {
core, _, _, root := TestCoreWithTokenStore(t)
core.defaultLeaseTTL = 10 * time.Second
core.maxLeaseTTL = 10 * time.Second
// Note: these requests are sent to Core since Core handles registration
// with the expiration manager and we need the storage to be consistent
req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/roles/test")
req.ClientToken = root
req.Data = map[string]interface{}{
"period": 5,
}
resp, err := core.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v %v", err, resp)
}
if resp != nil {
t.Fatalf("expected a nil response")
}
// First make one directly and verify on renew it uses the period.
{
req.ClientToken = root
req.Operation = logical.UpdateOperation
@@ -2818,65 +2904,6 @@ func TestTokenStore_Periodic(t *testing.T) {
// Now we create a token against the role and also set the te value
// directly. We should use the smaller of the two and be able to renew;
// increment should be ignored as well.
{
req.ClientToken = root
req.Operation = logical.UpdateOperation
req.Path = "auth/token/create/test"
req.Data = map[string]interface{}{
"period": 5,
}
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.Fatalf(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 < 4 || ttl > 5 {
t.Fatalf("TTL bad (expected %d, got %d)", 4, ttl)
}
// Let the TTL go down a bit
time.Sleep(2 * time.Second)
req.Operation = logical.UpdateOperation
req.Path = "auth/token/renew-self"
req.Data = map[string]interface{}{
"increment": 1,
}
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 > 5 {
t.Fatalf("TTL bad (expected less than %d, got %d)", 5, ttl)
}
}
// Now do the same, also using an explicit max in the role
{
req.Path = "auth/token/roles/test"
req.ClientToken = root