diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6efc2243..839b0da157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. * **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. FEATURES: diff --git a/api/sys_mounts.go b/api/sys_mounts.go index ac22f03d3b..25a564a310 100644 --- a/api/sys_mounts.go +++ b/api/sys_mounts.go @@ -2,12 +2,11 @@ package api import ( "fmt" - "time" "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") resp, err := c.c.RawRequest(r) if err != nil { @@ -15,12 +14,12 @@ func (c *Sys) ListMounts() (map[string]*Mount, error) { } defer resp.Body.Close() - var result map[string]*Mount + var result map[string]*MountOutput err = resp.DecodeJSON(&result) 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 { return err } @@ -79,7 +78,7 @@ func (c *Sys) Remount(from, to string) error { 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 { return err } @@ -119,13 +118,24 @@ func (c *Sys) checkMountPath(path string) error { return nil } -type Mount struct { - Type string `json:"type" structs:"type"` - Description string `json:"description" structs:"description"` - Config APIMountConfig `json:"config" structs:"config"` +type MountInput struct { + Type string `json:"type" structs:"type"` + Description string `json:"description" structs:"description"` + Config MountConfigInput `json:"config" structs:"config"` } -type APIMountConfig struct { - DefaultLeaseTTL *time.Duration `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"` +type MountConfigInput 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"` +} + +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"` } diff --git a/command/mount.go b/command/mount.go index 5064171e85..535d71a6a7 100644 --- a/command/mount.go +++ b/command/mount.go @@ -3,7 +3,6 @@ package command import ( "fmt" "strings" - "time" "github.com/hashicorp/vault/api" ) @@ -47,29 +46,13 @@ func (c *MountCommand) Run(args []string) int { return 2 } - mountInfo := &api.Mount{ + mountInfo := &api.MountInput{ Type: mountType, Description: description, - Config: api.APIMountConfig{}, - } - - 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 + Config: api.MountConfigInput{ + DefaultLeaseTTL: defaultLeaseTTL, + MaxLeaseTTL: maxLeaseTTL, + }, } if err := client.Sys().Mount(path, mountInfo); err != nil { diff --git a/command/mounts.go b/command/mounts.go index 9c24624954..a504189b0e 100644 --- a/command/mounts.go +++ b/command/mounts.go @@ -3,6 +3,7 @@ package command import ( "fmt" "sort" + "strconv" "strings" "github.com/ryanuber/columnize" @@ -43,9 +44,26 @@ func (c *MountsCommand) Run(args []string) int { columns := []string{"Path | Type | Default TTL | Max TTL | Description"} for _, path := range paths { 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( - "%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)) @@ -64,7 +82,7 @@ Usage: vault mounts [options] This command lists the mounted backends, their mount points, the 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: diff --git a/command/mounttune.go b/command/mounttune.go index 652bed0860..53ae88aaed 100644 --- a/command/mounttune.go +++ b/command/mounttune.go @@ -3,7 +3,6 @@ package command import ( "fmt" "strings" - "time" "github.com/hashicorp/vault/api" ) @@ -34,24 +33,9 @@ func (c *MountTuneCommand) Run(args []string) int { path := args[0] - mountConfig := api.APIMountConfig{} - 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 - } - 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 + mountConfig := api.MountConfigInput{ + DefaultLeaseTTL: defaultLeaseTTL, + MaxLeaseTTL: maxLeaseTTL, } client, err := c.Client() @@ -83,7 +67,7 @@ func (c *MountTuneCommand) Help() string { 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: @@ -92,14 +76,14 @@ General Options: Mount Options: -default-lease-ttl= Default lease time-to-live for this backend. - If not specified, uses the global default, or - the previously set value. Set to '0' to - explicitly set it to use the global default. + If not specified, uses the system default, or + the previously set value. Set to 'system' to + explicitly set it to use the system default. -max-lease-ttl= Max lease time-to-live for this backend. - If not specified, uses the global default, or - the previously set value. Set to '0' to - explicitly set it to use the global default. + If not specified, uses the system default, or + the previously set value. Set to 'system' to + explicitly set it to use the system default. ` return strings.TrimSpace(helpText) diff --git a/http/sys_mount_test.go b/http/sys_mount_test.go index d258d00f36..4789775625 100644 --- a/http/sys_mount_test.go +++ b/http/sys_mount_test.go @@ -3,7 +3,6 @@ package http import ( "reflect" "testing" - "time" "github.com/fatih/structs" "github.com/hashicorp/vault/vault" @@ -293,37 +292,37 @@ func TestSysTuneMount(t *testing.T) { // Shorter than system default 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) // Longer than system max 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) // Longer than system default 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) // Longer than backend max 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) // Shorter than backend default 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) // Shorter than backend max, longer than system max 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) @@ -333,8 +332,8 @@ func TestSysTuneMount(t *testing.T) { "description": "foo", "type": "generic", "config": map[string]interface{}{ - "default_lease_ttl": float64(time.Duration(time.Hour * 71999)), - "max_lease_ttl": float64(time.Duration(time.Hour * 72000)), + "default_lease_ttl": float64(259196400), + "max_lease_ttl": float64(259200000), }, }, "secret/": map[string]interface{}{ @@ -374,8 +373,8 @@ func TestSysTuneMount(t *testing.T) { resp = testHttpGet(t, token, addr+"/v1/sys/mounts/foo/tune") actual = map[string]interface{}{} expected = map[string]interface{}{ - "default_lease_ttl": float64(time.Duration(time.Hour * 71999)), - "max_lease_ttl": float64(time.Duration(time.Hour * 72000)), + "default_lease_ttl": float64(259196400), + "max_lease_ttl": float64(259200000), } testResponseStatus(t, resp, 200) @@ -386,16 +385,16 @@ func TestSysTuneMount(t *testing.T) { // Set a low max resp = testHttpPost(t, token, addr+"/v1/sys/mounts/secret/tune", map[string]interface{}{ - "default_lease_ttl": time.Duration(time.Second * 40), - "max_lease_ttl": time.Duration(time.Second * 80), + "default_lease_ttl": "40s", + "max_lease_ttl": "80s", }) testResponseStatus(t, resp, 204) resp = testHttpGet(t, token, addr+"/v1/sys/mounts/secret/tune") actual = map[string]interface{}{} expected = map[string]interface{}{ - "default_lease_ttl": float64(time.Duration(time.Second * 40)), - "max_lease_ttl": float64(time.Duration(time.Second * 80)), + "default_lease_ttl": float64(40), + "max_lease_ttl": float64(80), } testResponseStatus(t, resp, 200) @@ -421,7 +420,7 @@ func TestSysTuneMount(t *testing.T) { testResponseBody(t, resp, &result) expected = map[string]interface{}{ - "lease_duration": int(time.Duration(time.Second * 80).Seconds()), + "lease_duration": int(80), "lease_id": result.LeaseID, } @@ -441,7 +440,7 @@ func TestSysTuneMount(t *testing.T) { testResponseBody(t, resp, &result) expected = map[string]interface{}{ - "lease_duration": int(time.Duration(time.Second * 40).Seconds()), + "lease_duration": int(40), "lease_id": result.LeaseID, } diff --git a/logical/testing/testing.go b/logical/testing/testing.go index 117ee17a47..d7e85c3f92 100644 --- a/logical/testing/testing.go +++ b/logical/testing/testing.go @@ -167,7 +167,7 @@ func Test(t TestT, c TestCase) { // Mount the backend prefix := "mnt" - mountInfo := &api.Mount{ + mountInfo := &api.MountInput{ Type: "test", Description: "acceptance test", } diff --git a/vault/logical_system.go b/vault/logical_system.go index 4af7ee3d19..041ed0a8b0 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -5,7 +5,6 @@ import ( "strings" "time" - "github.com/fatih/structs" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" "github.com/mitchellh/mapstructure" @@ -54,11 +53,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) logical.Backend Description: strings.TrimSpace(sysHelp["mount_path"][0]), }, "default_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, + Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), }, "max_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, + Type: framework.TypeString, Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), }, }, @@ -378,8 +377,12 @@ func (b *SystemBackend) handleMountTable( info := map[string]interface{}{ "type": entry.Type, "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 } @@ -395,9 +398,14 @@ func (b *SystemBackend) handleMount( description := data.Get("description").(string) 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{}) if configMap != nil && len(configMap) != 0 { - err := mapstructure.Decode(configMap, &config) + err := mapstructure.Decode(configMap, &apiConfig) if err != nil { return logical.ErrorResponse( "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 == "" { return logical.ErrorResponse( "backend type must be specified as a string"), @@ -498,14 +544,10 @@ func (b *SystemBackend) handleMountConfig( return handleError(err) } - def := sysView.DefaultLeaseTTL() - - max := sysView.MaxLeaseTTL() - resp := &logical.Response{ Data: map[string]interface{}{ - "default_lease_ttl": def, - "max_lease_ttl": max, + "default_lease_ttl": int(sysView.DefaultLeaseTTL().Seconds()), + "max_lease_ttl": int(sysView.MaxLeaseTTL().Seconds()), }, } @@ -545,14 +587,34 @@ func (b *SystemBackend) handleMountTune( // Timing configuration parameters { var newDefault, newMax *time.Duration - if defTTLInt, ok := data.GetOk("default_lease_ttl"); ok { - def := time.Duration(defTTLInt.(int)) - newDefault = &def + defTTL := data.Get("default_lease_ttl").(string) + switch defTTL { + 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)) - newMax = &max + + maxTTL := data.Get("max_lease_ttl").(string) + 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 err := b.tuneMountTTLs(path, &mountEntry.Config, newDefault, newMax); err != nil { b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err) diff --git a/vault/logical_system_helpers.go b/vault/logical_system_helpers.go index 626360396f..806981e9aa 100644 --- a/vault/logical_system_helpers.go +++ b/vault/logical_system_helpers.go @@ -26,27 +26,27 @@ func (b *SystemBackend) tuneMountTTLs(path string, meConfig *MountConfig, newDef } 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", - *newMax, *newDefault) + return fmt.Errorf("new backend max lease TTL of %d less than new backend default lease TTL of %d", + int(newMax.Seconds()), int(newDefault.Seconds())) } if newMax != nil && newDefault == nil { 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", - *newMax, meConfig.DefaultLeaseTTL) + return fmt.Errorf("new backend max lease TTL of %d less than backend default lease TTL of %d", + int(newMax.Seconds()), int(meConfig.DefaultLeaseTTL.Seconds())) } } if newDefault != nil { if meConfig.MaxLeaseTTL == 0 { if *newDefault > b.Core.maxLeaseTTL { - return fmt.Errorf("New backend default lease TTL of %d greater than system max lease TTL of %d", - *newDefault, b.Core.maxLeaseTTL) + return fmt.Errorf("new backend default lease TTL of %d greater than system max lease TTL of %d", + int(newDefault.Seconds()), int(b.Core.maxLeaseTTL.Seconds())) } } else { if meConfig.MaxLeaseTTL < *newDefault { - return fmt.Errorf("New backend default lease TTL of %d greater than backend max lease TTL of %d", - *newDefault, meConfig.MaxLeaseTTL) + return fmt.Errorf("new backend default lease TTL of %d greater than backend max lease TTL of %d", + int(newDefault.Seconds()), int(meConfig.MaxLeaseTTL.Seconds())) } } } diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index fc71beefbd..1f19561bc5 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -47,24 +47,24 @@ func TestSystemBackend_mounts(t *testing.T) { "type": "generic", "description": "generic secret storage", "config": map[string]interface{}{ - "default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), - "max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_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"].(int), }, }, "sys/": map[string]interface{}{ "type": "system", "description": "system endpoints used for control, policy and debugging", "config": map[string]interface{}{ - "default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), - "max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_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"].(int), }, }, "cubbyhole/": map[string]interface{}{ "description": "per-token private secret storage", "type": "cubbyhole", "config": map[string]interface{}{ - "default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(time.Duration), - "max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_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"].(int), }, }, } diff --git a/vault/token_store.go b/vault/token_store.go index 8713da822d..d0a2b091c3 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -499,6 +499,7 @@ func (ts *TokenStore) handleCreate( Metadata map[string]string `mapstructure:"meta"` NoParent bool `mapstructure:"no_parent"` Lease string + TTL string DisplayName string `mapstructure:"display_name"` NumUses int `mapstructure:"num_uses"` } @@ -559,8 +560,17 @@ func (ts *TokenStore) handleCreate( te.Parent = "" } - // Parse the lease if any - if data.Lease != "" { + // Parse the TTL/lease if any + 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) if err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest diff --git a/vault/token_store_test.go b/vault/token_store_test.go index c180e04241..19b36c1825 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -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) { _, ts, root := mockTokenStore(t) testMakeToken(t, ts, root, "child", []string{"root", "foo"}) diff --git a/website/source/docs/auth/token.html.md b/website/source/docs/auth/token.html.md index 3a2726a578..02a3546c98 100644 --- a/website/source/docs/auth/token.html.md +++ b/website/source/docs/auth/token.html.md @@ -87,9 +87,14 @@ of the header should be "X-Vault-Token" and the value should be the token.
  • lease optional - The lease period of the token, provided as "1h", where hour is + DEPRECATED; use "ttl" instead. +
  • +
  • + ttl + optional + The TTL period of the token, provided as "1h", where hour is 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.
  • diff --git a/website/source/docs/http/sys-mounts.html.md b/website/source/docs/http/sys-mounts.html.md index 2abd53d4a3..36b2480e7a 100644 --- a/website/source/docs/http/sys-mounts.html.md +++ b/website/source/docs/http/sys-mounts.html.md @@ -60,9 +60,9 @@ description: |-
    Description
    - List the given secret backends configuration. `default_lease_ttl` - or `max_lease_ttl` values of `0` mean that the system - defaults are used by this backend. + List the given mount's configuration. Unlike the `mounts` + endpoint, this will return the current time in seconds for each + TTL, which may be the system default or a mount-specific value.
    Method
    @@ -81,8 +81,8 @@ description: |- ```javascript { - "default_lease_ttl": 0, - "max_lease_ttl": 0 + "default_lease_ttl": 3600, + "max_lease_ttl": 7200 } ``` @@ -153,13 +153,15 @@ description: |- default_lease_ttl optional 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.
  • max_lease_ttl optional 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.