Switch per-mount values to strings going in and seconds coming out, like other commands. Indicate deprecation of 'lease' in the token backend.

This commit is contained in:
Jeff Mitchell
2015-09-25 09:46:20 -04:00
parent ae8192f990
commit 70ce824267
14 changed files with 220 additions and 124 deletions

View File

@@ -5,7 +5,7 @@ DEPRECATIONS/BREAKING CHANGES:
Note: deprecations and breaking changes in upcoming releases are announced ahead of time on the "vault-tool" mailing list. Note: deprecations and breaking changes in upcoming releases are announced ahead of time on the "vault-tool" mailing list.
* **Cookie Authentication Removed**: As of 0.3 the only way to authenticate is via the X-Vault-Token header. Cookie authentication was hard to properly test, could result in browsers/tools/applications saving tokens in plaintext on disk, and other issues. [GH-564] * **Cookie Authentication Removed**: As of 0.3 the only way to authenticate is via the X-Vault-Token header. Cookie authentication was hard to properly test, could result in browsers/tools/applications saving tokens in plaintext on disk, and other issues. [GH-564]
* **Terminology/Field Names**: Vault is transitioning from overloading the term "lease" to mean both "a set of metadata" and "the amount of time the metadata is valid". The latter is now being referred to as TTL (or "lease_duration" for backwards-compatibility); some parts of Vault have already switched to using "ttl" and others will follow in upcoming releases. In particular, both the "generic" and "pki" backend accept both "ttl" and "lease" but in 0.4 only "ttl" will be accepted. [GH-528] * **Terminology/Field Names**: Vault is transitioning from overloading the term "lease" to mean both "a set of metadata" and "the amount of time the metadata is valid". The latter is now being referred to as TTL (or "lease_duration" for backwards-compatibility); some parts of Vault have already switched to using "ttl" and others will follow in upcoming releases. In particular, the "token", "generic", and "pki" backends accept both "ttl" and "lease" but in 0.4 only "ttl" will be accepted. [GH-528]
* **Downgrade Not Supported**: Due to enhancements in the storage subsytem, values written by Vault 0.3+ will not be able to be read by prior versions of Vault. There are no expected upgrade issues, however, as with all critical infrastructure it is recommended to back up Vault's physical storage before upgrading. * **Downgrade Not Supported**: Due to enhancements in the storage subsytem, values written by Vault 0.3+ will not be able to be read by prior versions of Vault. There are no expected upgrade issues, however, as with all critical infrastructure it is recommended to back up Vault's physical storage before upgrading.
FEATURES: FEATURES:

View File

@@ -2,12 +2,11 @@ package api
import ( import (
"fmt" "fmt"
"time"
"github.com/fatih/structs" "github.com/fatih/structs"
) )
func (c *Sys) ListMounts() (map[string]*Mount, error) { func (c *Sys) ListMounts() (map[string]*MountOutput, error) {
r := c.c.NewRequest("GET", "/v1/sys/mounts") r := c.c.NewRequest("GET", "/v1/sys/mounts")
resp, err := c.c.RawRequest(r) resp, err := c.c.RawRequest(r)
if err != nil { if err != nil {
@@ -15,12 +14,12 @@ func (c *Sys) ListMounts() (map[string]*Mount, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
var result map[string]*Mount var result map[string]*MountOutput
err = resp.DecodeJSON(&result) err = resp.DecodeJSON(&result)
return result, err return result, err
} }
func (c *Sys) Mount(path string, mountInfo *Mount) error { func (c *Sys) Mount(path string, mountInfo *MountInput) error {
if err := c.checkMountPath(path); err != nil { if err := c.checkMountPath(path); err != nil {
return err return err
} }
@@ -79,7 +78,7 @@ func (c *Sys) Remount(from, to string) error {
return err return err
} }
func (c *Sys) TuneMount(path string, config APIMountConfig) error { func (c *Sys) TuneMount(path string, config MountConfigInput) error {
if err := c.checkMountPath(path); err != nil { if err := c.checkMountPath(path); err != nil {
return err return err
} }
@@ -119,13 +118,24 @@ func (c *Sys) checkMountPath(path string) error {
return nil return nil
} }
type Mount struct { type MountInput struct {
Type string `json:"type" structs:"type"` Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"` Description string `json:"description" structs:"description"`
Config APIMountConfig `json:"config" structs:"config"` Config MountConfigInput `json:"config" structs:"config"`
} }
type APIMountConfig struct { type MountConfigInput struct {
DefaultLeaseTTL *time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL *time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
}
type MountOutput struct {
Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"`
Config MountConfigOutput `json:"config" structs:"config"`
}
type MountConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
} }

View File

@@ -3,7 +3,6 @@ package command
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/hashicorp/vault/api" "github.com/hashicorp/vault/api"
) )
@@ -47,29 +46,13 @@ func (c *MountCommand) Run(args []string) int {
return 2 return 2
} }
mountInfo := &api.Mount{ mountInfo := &api.MountInput{
Type: mountType, Type: mountType,
Description: description, Description: description,
Config: api.APIMountConfig{}, Config: api.MountConfigInput{
} DefaultLeaseTTL: defaultLeaseTTL,
MaxLeaseTTL: maxLeaseTTL,
if defaultLeaseTTL != "" { },
defTTL, err := time.ParseDuration(defaultLeaseTTL)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error parsing default lease TTL duration: %s", err))
return 2
}
mountInfo.Config.DefaultLeaseTTL = &defTTL
}
if maxLeaseTTL != "" {
maxTTL, err := time.ParseDuration(maxLeaseTTL)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error parsing max lease TTL duration: %s", err))
return 2
}
mountInfo.Config.MaxLeaseTTL = &maxTTL
} }
if err := client.Sys().Mount(path, mountInfo); err != nil { if err := client.Sys().Mount(path, mountInfo); err != nil {

View File

@@ -3,6 +3,7 @@ package command
import ( import (
"fmt" "fmt"
"sort" "sort"
"strconv"
"strings" "strings"
"github.com/ryanuber/columnize" "github.com/ryanuber/columnize"
@@ -43,9 +44,26 @@ func (c *MountsCommand) Run(args []string) int {
columns := []string{"Path | Type | Default TTL | Max TTL | Description"} columns := []string{"Path | Type | Default TTL | Max TTL | Description"}
for _, path := range paths { for _, path := range paths {
mount := mounts[path] mount := mounts[path]
defTTL := "system"
switch {
case mount.Type == "system":
defTTL = "n/a"
case mount.Type == "cubbyhole":
defTTL = "n/a"
case mount.Config.DefaultLeaseTTL != 0:
defTTL = strconv.Itoa(mount.Config.DefaultLeaseTTL)
}
maxTTL := "system"
switch {
case mount.Type == "system":
maxTTL = "n/a"
case mount.Type == "cubbyhole":
maxTTL = "n/a"
case mount.Config.MaxLeaseTTL != 0:
maxTTL = strconv.Itoa(mount.Config.MaxLeaseTTL)
}
columns = append(columns, fmt.Sprintf( columns = append(columns, fmt.Sprintf(
"%s | %s | %s | %s | %s", path, mount.Type, mount.Config.DefaultLeaseTTL, mount.Config.MaxLeaseTTL, mount.Description)) "%s | %s | %s | %s | %s", path, mount.Type, defTTL, maxTTL, mount.Description))
} }
c.Ui.Output(columnize.SimpleFormat(columns)) c.Ui.Output(columnize.SimpleFormat(columns))
@@ -64,7 +82,7 @@ Usage: vault mounts [options]
This command lists the mounted backends, their mount points, the This command lists the mounted backends, their mount points, the
configured TTLs, and a human-friendly description of the mount point. configured TTLs, and a human-friendly description of the mount point.
A TTL of '0' indicates that the system default is being used. A TTL of 'system' indicates that the system default is being used.
General Options: General Options:

View File

@@ -3,7 +3,6 @@ package command
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/hashicorp/vault/api" "github.com/hashicorp/vault/api"
) )
@@ -34,24 +33,9 @@ func (c *MountTuneCommand) Run(args []string) int {
path := args[0] path := args[0]
mountConfig := api.APIMountConfig{} mountConfig := api.MountConfigInput{
if defaultLeaseTTL != "" { DefaultLeaseTTL: defaultLeaseTTL,
defTTL, err := time.ParseDuration(defaultLeaseTTL) MaxLeaseTTL: maxLeaseTTL,
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error parsing default lease TTL duration: %s", err))
return 2
}
mountConfig.DefaultLeaseTTL = &defTTL
}
if maxLeaseTTL != "" {
maxTTL, err := time.ParseDuration(maxLeaseTTL)
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error parsing max lease TTL duration: %s", err))
return 2
}
mountConfig.MaxLeaseTTL = &maxTTL
} }
client, err := c.Client() client, err := c.Client()
@@ -83,7 +67,7 @@ func (c *MountTuneCommand) Help() string {
Tune configuration options for a mounted secret backend. Tune configuration options for a mounted secret backend.
Example: vault tune-mount -default-lease-ttl="24h" secret/ Example: vault tune-mount -default-lease-ttl="24h" secret
General Options: General Options:
@@ -92,14 +76,14 @@ General Options:
Mount Options: Mount Options:
-default-lease-ttl=<duration> Default lease time-to-live for this backend. -default-lease-ttl=<duration> Default lease time-to-live for this backend.
If not specified, uses the global default, or If not specified, uses the system default, or
the previously set value. Set to '0' to the previously set value. Set to 'system' to
explicitly set it to use the global default. explicitly set it to use the system default.
-max-lease-ttl=<duration> Max lease time-to-live for this backend. -max-lease-ttl=<duration> Max lease time-to-live for this backend.
If not specified, uses the global default, or If not specified, uses the system default, or
the previously set value. Set to '0' to the previously set value. Set to 'system' to
explicitly set it to use the global default. explicitly set it to use the system default.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)

View File

@@ -3,7 +3,6 @@ package http
import ( import (
"reflect" "reflect"
"testing" "testing"
"time"
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/hashicorp/vault/vault" "github.com/hashicorp/vault/vault"
@@ -293,37 +292,37 @@ func TestSysTuneMount(t *testing.T) {
// Shorter than system default // Shorter than system default
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"default_lease_ttl": time.Duration(time.Hour * 72), "default_lease_ttl": "72h",
}) })
testResponseStatus(t, resp, 204) testResponseStatus(t, resp, 204)
// Longer than system max // Longer than system max
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"default_lease_ttl": time.Duration(time.Hour * 72000), "default_lease_ttl": "72000h",
}) })
testResponseStatus(t, resp, 400) testResponseStatus(t, resp, 400)
// Longer than system default // Longer than system default
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"max_lease_ttl": time.Duration(time.Hour * 72000), "max_lease_ttl": "72000h",
}) })
testResponseStatus(t, resp, 204) testResponseStatus(t, resp, 204)
// Longer than backend max // Longer than backend max
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"default_lease_ttl": time.Duration(time.Hour * 72001), "default_lease_ttl": "72001h",
}) })
testResponseStatus(t, resp, 400) testResponseStatus(t, resp, 400)
// Shorter than backend default // Shorter than backend default
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"max_lease_ttl": time.Duration(time.Hour * 1), "max_lease_ttl": "1h",
}) })
testResponseStatus(t, resp, 400) testResponseStatus(t, resp, 400)
// Shorter than backend max, longer than system max // Shorter than backend max, longer than system max
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/foo/tune", map[string]interface{}{
"default_lease_ttl": time.Duration(time.Hour * 71999), "default_lease_ttl": "71999h",
}) })
testResponseStatus(t, resp, 204) testResponseStatus(t, resp, 204)
@@ -333,8 +332,8 @@ func TestSysTuneMount(t *testing.T) {
"description": "foo", "description": "foo",
"type": "generic", "type": "generic",
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": float64(time.Duration(time.Hour * 71999)), "default_lease_ttl": float64(259196400),
"max_lease_ttl": float64(time.Duration(time.Hour * 72000)), "max_lease_ttl": float64(259200000),
}, },
}, },
"secret/": map[string]interface{}{ "secret/": map[string]interface{}{
@@ -374,8 +373,8 @@ func TestSysTuneMount(t *testing.T) {
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/foo/tune") resp = testHttpGet(t, token, addr+"/v1/sys/mounts/foo/tune")
actual = map[string]interface{}{} actual = map[string]interface{}{}
expected = map[string]interface{}{ expected = map[string]interface{}{
"default_lease_ttl": float64(time.Duration(time.Hour * 71999)), "default_lease_ttl": float64(259196400),
"max_lease_ttl": float64(time.Duration(time.Hour * 72000)), "max_lease_ttl": float64(259200000),
} }
testResponseStatus(t, resp, 200) testResponseStatus(t, resp, 200)
@@ -386,16 +385,16 @@ func TestSysTuneMount(t *testing.T) {
// Set a low max // Set a low max
resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{ resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{
"default_lease_ttl": time.Duration(time.Second * 40), "default_lease_ttl": "40s",
"max_lease_ttl": time.Duration(time.Second * 80), "max_lease_ttl": "80s",
}) })
testResponseStatus(t, resp, 204) testResponseStatus(t, resp, 204)
resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune") resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune")
actual = map[string]interface{}{} actual = map[string]interface{}{}
expected = map[string]interface{}{ expected = map[string]interface{}{
"default_lease_ttl": float64(time.Duration(time.Second * 40)), "default_lease_ttl": float64(40),
"max_lease_ttl": float64(time.Duration(time.Second * 80)), "max_lease_ttl": float64(80),
} }
testResponseStatus(t, resp, 200) testResponseStatus(t, resp, 200)
@@ -421,7 +420,7 @@ func TestSysTuneMount(t *testing.T) {
testResponseBody(t, resp, &result) testResponseBody(t, resp, &result)
expected = map[string]interface{}{ expected = map[string]interface{}{
"lease_duration": int(time.Duration(time.Second * 80).Seconds()), "lease_duration": int(80),
"lease_id": result.LeaseID, "lease_id": result.LeaseID,
} }
@@ -441,7 +440,7 @@ func TestSysTuneMount(t *testing.T) {
testResponseBody(t, resp, &result) testResponseBody(t, resp, &result)
expected = map[string]interface{}{ expected = map[string]interface{}{
"lease_duration": int(time.Duration(time.Second * 40).Seconds()), "lease_duration": int(40),
"lease_id": result.LeaseID, "lease_id": result.LeaseID,
} }

View File

@@ -167,7 +167,7 @@ func Test(t TestT, c TestCase) {
// Mount the backend // Mount the backend
prefix := "mnt" prefix := "mnt"
mountInfo := &api.Mount{ mountInfo := &api.MountInput{
Type: "test", Type: "test",
Description: "acceptance test", Description: "acceptance test",
} }

View File

@@ -5,7 +5,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/fatih/structs"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework" "github.com/hashicorp/vault/logical/framework"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@@ -54,11 +53,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) logical.Backend
Description: strings.TrimSpace(sysHelp["mount_path"][0]), Description: strings.TrimSpace(sysHelp["mount_path"][0]),
}, },
"default_lease_ttl": &framework.FieldSchema{ "default_lease_ttl": &framework.FieldSchema{
Type: framework.TypeDurationSecond, Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]),
}, },
"max_lease_ttl": &framework.FieldSchema{ "max_lease_ttl": &framework.FieldSchema{
Type: framework.TypeDurationSecond, Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]),
}, },
}, },
@@ -378,8 +377,12 @@ func (b *SystemBackend) handleMountTable(
info := map[string]interface{}{ info := map[string]interface{}{
"type": entry.Type, "type": entry.Type,
"description": entry.Description, "description": entry.Description,
"config": structs.Map(entry.Config), "config": map[string]interface{}{
"default_lease_ttl": int(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int(entry.Config.MaxLeaseTTL.Seconds()),
},
} }
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
} }
@@ -395,9 +398,14 @@ func (b *SystemBackend) handleMount(
description := data.Get("description").(string) description := data.Get("description").(string)
var config MountConfig var config MountConfig
var apiConfig struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
}
configMap := data.Get("config").(map[string]interface{}) configMap := data.Get("config").(map[string]interface{})
if configMap != nil && len(configMap) != 0 { if configMap != nil && len(configMap) != 0 {
err := mapstructure.Decode(configMap, &config) err := mapstructure.Decode(configMap, &apiConfig)
if err != nil { if err != nil {
return logical.ErrorResponse( return logical.ErrorResponse(
"unable to convert given mount config information"), "unable to convert given mount config information"),
@@ -405,6 +413,44 @@ func (b *SystemBackend) handleMount(
} }
} }
switch apiConfig.DefaultLeaseTTL {
case "":
case "system":
default:
tmpDef, err := time.ParseDuration(apiConfig.DefaultLeaseTTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)),
logical.ErrInvalidRequest
}
config.DefaultLeaseTTL = tmpDef
}
switch apiConfig.MaxLeaseTTL {
case "":
case "system":
default:
tmpMax, err := time.ParseDuration(apiConfig.MaxLeaseTTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)),
logical.ErrInvalidRequest
}
config.MaxLeaseTTL = tmpMax
}
if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL {
return logical.ErrorResponse(
"given default lease TTL greater than given max lease TTL"),
logical.ErrInvalidRequest
}
if config.DefaultLeaseTTL > b.Core.maxLeaseTTL {
return logical.ErrorResponse(fmt.Sprintf(
"given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))),
logical.ErrInvalidRequest
}
if logicalType == "" { if logicalType == "" {
return logical.ErrorResponse( return logical.ErrorResponse(
"backend type must be specified as a string"), "backend type must be specified as a string"),
@@ -498,14 +544,10 @@ func (b *SystemBackend) handleMountConfig(
return handleError(err) return handleError(err)
} }
def := sysView.DefaultLeaseTTL()
max := sysView.MaxLeaseTTL()
resp := &logical.Response{ resp := &logical.Response{
Data: map[string]interface{}{ Data: map[string]interface{}{
"default_lease_ttl": def, "default_lease_ttl": int(sysView.DefaultLeaseTTL().Seconds()),
"max_lease_ttl": max, "max_lease_ttl": int(sysView.MaxLeaseTTL().Seconds()),
}, },
} }
@@ -545,14 +587,34 @@ func (b *SystemBackend) handleMountTune(
// Timing configuration parameters // Timing configuration parameters
{ {
var newDefault, newMax *time.Duration var newDefault, newMax *time.Duration
if defTTLInt, ok := data.GetOk("default_lease_ttl"); ok { defTTL := data.Get("default_lease_ttl").(string)
def := time.Duration(defTTLInt.(int)) switch defTTL {
newDefault = &def case "":
case "system":
tmpDef := time.Duration(0)
newDefault = &tmpDef
default:
tmpDef, err := time.ParseDuration(defTTL)
if err != nil {
return handleError(err)
}
newDefault = &tmpDef
} }
if maxTTLInt, ok := data.GetOk("max_lease_ttl"); ok {
max := time.Duration(maxTTLInt.(int)) maxTTL := data.Get("max_lease_ttl").(string)
newMax = &max switch maxTTL {
case "":
case "system":
tmpMax := time.Duration(0)
newMax = &tmpMax
default:
tmpMax, err := time.ParseDuration(maxTTL)
if err != nil {
return handleError(err)
}
newMax = &tmpMax
} }
if newDefault != nil || newMax != nil { if newDefault != nil || newMax != nil {
if err := b.tuneMountTTLs(path, &mountEntry.Config, newDefault, newMax); err != nil { if err := b.tuneMountTTLs(path, &mountEntry.Config, newDefault, newMax); err != nil {
b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err) b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err)

View File

@@ -26,27 +26,27 @@ func (b *SystemBackend) tuneMountTTLs(path string, meConfig *MountConfig, newDef
} }
if newMax != nil && newDefault != nil && *newMax < *newDefault { if newMax != nil && newDefault != nil && *newMax < *newDefault {
return fmt.Errorf("New backend max lease TTL of %d less than new backend default lease TTL of %d", return fmt.Errorf("new backend max lease TTL of %d less than new backend default lease TTL of %d",
*newMax, *newDefault) int(newMax.Seconds()), int(newDefault.Seconds()))
} }
if newMax != nil && newDefault == nil { if newMax != nil && newDefault == nil {
if meConfig.DefaultLeaseTTL != 0 && *newMax < meConfig.DefaultLeaseTTL { if meConfig.DefaultLeaseTTL != 0 && *newMax < meConfig.DefaultLeaseTTL {
return fmt.Errorf("New backend max lease TTL of %d less than backend default lease TTL of %d", return fmt.Errorf("new backend max lease TTL of %d less than backend default lease TTL of %d",
*newMax, meConfig.DefaultLeaseTTL) int(newMax.Seconds()), int(meConfig.DefaultLeaseTTL.Seconds()))
} }
} }
if newDefault != nil { if newDefault != nil {
if meConfig.MaxLeaseTTL == 0 { if meConfig.MaxLeaseTTL == 0 {
if *newDefault > b.Core.maxLeaseTTL { if *newDefault > b.Core.maxLeaseTTL {
return fmt.Errorf("New backend default lease TTL of %d greater than system max lease TTL of %d", return fmt.Errorf("new backend default lease TTL of %d greater than system max lease TTL of %d",
*newDefault, b.Core.maxLeaseTTL) int(newDefault.Seconds()), int(b.Core.maxLeaseTTL.Seconds()))
} }
} else { } else {
if meConfig.MaxLeaseTTL < *newDefault { if meConfig.MaxLeaseTTL < *newDefault {
return fmt.Errorf("New backend default lease TTL of %d greater than backend max lease TTL of %d", return fmt.Errorf("new backend default lease TTL of %d greater than backend max lease TTL of %d",
*newDefault, meConfig.MaxLeaseTTL) int(newDefault.Seconds()), int(meConfig.MaxLeaseTTL.Seconds()))
} }
} }
} }

View File

@@ -47,24 +47,24 @@ func TestSystemBackend_mounts(t *testing.T) {
"type": "generic", "type": "generic",
"description": "generic secret storage", "description": "generic secret storage",
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), "default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int),
"max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(time.Duration), "max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int),
}, },
}, },
"sys/": map[string]interface{}{ "sys/": map[string]interface{}{
"type": "system", "type": "system",
"description": "system endpoints used for control, policy and debugging", "description": "system endpoints used for control, policy and debugging",
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), "default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int),
"max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(time.Duration), "max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int),
}, },
}, },
"cubbyhole/": map[string]interface{}{ "cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage", "description": "per-token private secret storage",
"type": "cubbyhole", "type": "cubbyhole",
"config": map[string]interface{}{ "config": map[string]interface{}{
"default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), "default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int),
"max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(time.Duration), "max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int),
}, },
}, },
} }

View File

@@ -499,6 +499,7 @@ func (ts *TokenStore) handleCreate(
Metadata map[string]string `mapstructure:"meta"` Metadata map[string]string `mapstructure:"meta"`
NoParent bool `mapstructure:"no_parent"` NoParent bool `mapstructure:"no_parent"`
Lease string Lease string
TTL string
DisplayName string `mapstructure:"display_name"` DisplayName string `mapstructure:"display_name"`
NumUses int `mapstructure:"num_uses"` NumUses int `mapstructure:"num_uses"`
} }
@@ -559,8 +560,17 @@ func (ts *TokenStore) handleCreate(
te.Parent = "" te.Parent = ""
} }
// Parse the lease if any // Parse the TTL/lease if any
if data.Lease != "" { if data.TTL != "" {
dur, err := time.ParseDuration(data.TTL)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
if dur < 0 {
return logical.ErrorResponse("ttl must be positive"), logical.ErrInvalidRequest
}
te.TTL = dur
} else if data.Lease != "" {
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

View File

@@ -711,6 +711,29 @@ func TestTokenStore_HandleRequest_CreateToken_Lease(t *testing.T) {
} }
} }
func TestTokenStore_HandleRequest_CreateToken_TTL(t *testing.T) {
_, ts, root := mockTokenStore(t)
req := logical.TestRequest(t, logical.WriteOperation, "create")
req.ClientToken = root
req.Data["policies"] = []string{"foo"}
req.Data["ttl"] = "1h"
resp, err := ts.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v %v", err, resp)
}
if resp.Auth.ClientToken == "" {
t.Fatalf("bad: %#v", resp)
}
if resp.Auth.TTL != time.Hour {
t.Fatalf("bad: %#v", resp)
}
if !resp.Auth.Renewable {
t.Fatalf("bad: %#v", resp)
}
}
func TestTokenStore_HandleRequest_Revoke(t *testing.T) { func TestTokenStore_HandleRequest_Revoke(t *testing.T) {
_, ts, root := mockTokenStore(t) _, ts, root := mockTokenStore(t)
testMakeToken(t, ts, root, "child", []string{"root", "foo"}) testMakeToken(t, ts, root, "child", []string{"root", "foo"})

View File

@@ -87,9 +87,14 @@ of the header should be "X-Vault-Token" and the value should be the token.
<li> <li>
<span class="param">lease</span> <span class="param">lease</span>
<span class="param-flags">optional</span> <span class="param-flags">optional</span>
The lease period of the token, provided as "1h", where hour is DEPRECATED; use "ttl" instead.
</li>
<li>
<span class="param">ttl</span>
<span class="param-flags">optional</span>
The TTL period of the token, provided as "1h", where hour is
the largest suffix. If not provided, the token is valid for the the largest suffix. If not provided, the token is valid for the
[default lease duration](/docs/config/index.html), or [default lease TTL](/docs/config/index.html), or
indefinitely if the root policy is used. indefinitely if the root policy is used.
</li> </li>
<li> <li>

View File

@@ -60,9 +60,9 @@ description: |-
<dl> <dl>
<dt>Description</dt> <dt>Description</dt>
<dd> <dd>
List the given secret backends configuration. `default_lease_ttl` List the given mount's configuration. Unlike the `mounts`
or `max_lease_ttl` values of `0` mean that the system endpoint, this will return the current time in seconds for each
defaults are used by this backend. TTL, which may be the system default or a mount-specific value.
</dd> </dd>
<dt>Method</dt> <dt>Method</dt>
@@ -81,8 +81,8 @@ description: |-
```javascript ```javascript
{ {
"default_lease_ttl": 0, "default_lease_ttl": 3600,
"max_lease_ttl": 0 "max_lease_ttl": 7200
} }
``` ```
@@ -153,13 +153,15 @@ description: |-
<span class="param">default_lease_ttl</span> <span class="param">default_lease_ttl</span>
<span class="param-flags">optional</span> <span class="param-flags">optional</span>
The default time-to-live. If set on a specific mount, The default time-to-live. If set on a specific mount,
overrides the global default. overrides the global default. A value of "system" or "0"
are equivalent and set to the system default TTL.
</li> </li>
<li> <li>
<span class="param">max_lease_ttl</span> <span class="param">max_lease_ttl</span>
<span class="param-flags">optional</span> <span class="param-flags">optional</span>
The maximum time-to-live. If set on a specific mount, The maximum time-to-live. If set on a specific mount,
overrides the global default. overrides the global default. A value of "system" or "0"
are equivalent and set to the system max TTL.
</li> </li>
</ul> </ul>
</dd> </dd>