Rejig how dynamic values are represented in system view and location of some functions in various packages; create mount-tune command and API analogues; update documentation

This commit is contained in:
Jeff Mitchell 2015-09-02 15:56:58 -04:00
parent 4239f9d243
commit 488d33c70a
18 changed files with 521 additions and 200 deletions

View File

@ -54,7 +54,7 @@ func (c *Sys) Unmount(path string) error {
return err
}
func (c *Sys) Remount(from, to string, config vault.MountConfig) error {
func (c *Sys) Remount(from, to string) error {
if err := c.checkMountPath(from); err != nil {
return err
}
@ -63,9 +63,8 @@ func (c *Sys) Remount(from, to string, config vault.MountConfig) error {
}
body := map[string]interface{}{
"from": from,
"to": to,
"config": config,
"from": from,
"to": to,
}
r := c.c.NewRequest("POST", "/v1/sys/remount")
@ -80,6 +79,41 @@ func (c *Sys) Remount(from, to string, config vault.MountConfig) error {
return err
}
func (c *Sys) TuneMount(path string, config vault.MountConfig) error {
if err := c.checkMountPath(path); err != nil {
return err
}
body := map[string]interface{}{
"config": config,
}
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s/tune", path))
if err := r.SetJSONBody(body); err != nil {
return err
}
resp, err := c.c.RawRequest(r)
if err == nil {
defer resp.Body.Close()
}
return err
}
func (c *Sys) MountConfig(path string) error {
if err := c.checkMountPath(path); err != nil {
return err
}
r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/mounts/%s/tune", path))
resp, err := c.c.RawRequest(r)
if err == nil {
defer resp.Body.Close()
}
return err
}
func (c *Sys) checkMountPath(path string) error {
if path[0] == '/' {
return fmt.Errorf("path must not start with /: %s", path)

View File

@ -114,7 +114,10 @@ func (b *backend) pathIssueCert(
var ttl time.Duration
if len(ttlField) == 0 {
ttl = b.System.DefaultLeaseTTL()
ttl, err = b.System().DefaultLeaseTTL()
if err != nil {
return nil, fmt.Errorf("Error fetching default TTL: %s", err)
}
} else {
ttl, err = time.ParseDuration(ttlField)
if err != nil {
@ -125,7 +128,10 @@ func (b *backend) pathIssueCert(
var maxTTL time.Duration
if len(role.MaxTTL) == 0 {
maxTTL = b.System.MaxLeaseTTL()
maxTTL, err = b.System().MaxLeaseTTL()
if err != nil {
return nil, fmt.Errorf("Error fetching max TTL: %s", err)
}
} else {
maxTTL, err = time.ParseDuration(role.MaxTTL)
if err != nil {

View File

@ -256,10 +256,13 @@ func (b *backend) pathRoleCreate(
if len(entry.MaxTTL) == 0 {
entry.MaxTTL = data.Get("lease_max").(string)
}
var maxTTL time.Duration
maxSystemTTL, err := b.System().MaxLeaseTTL()
if err != nil {
return nil, fmt.Errorf("Error fetching max TTL: %s", err)
}
if len(entry.MaxTTL) == 0 {
maxTTL = b.System.MaxLeaseTTL()
maxTTL = maxSystemTTL
} else {
maxTTL, err = time.ParseDuration(entry.MaxTTL)
if err != nil {
@ -267,18 +270,18 @@ func (b *backend) pathRoleCreate(
"Invalid ttl: %s", err)), nil
}
}
if maxTTL > b.System.MaxLeaseTTL() {
if maxTTL > maxSystemTTL {
return logical.ErrorResponse("Requested max TTL is higher than system maximum"), nil
}
var ttl time.Duration
if len(entry.TTL) == 0 {
entry.TTL = data.Get("lease").(string)
}
switch len(entry.TTL) {
case 0:
ttl = b.System.DefaultLeaseTTL()
default:
ttl, err := b.System().DefaultLeaseTTL()
if err != nil {
return nil, fmt.Errorf("Error fetching max TTL: %s", err)
}
if len(entry.TTL) != 0 {
ttl, err = time.ParseDuration(entry.TTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(

View File

@ -226,6 +226,12 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
}, nil
},
"mount-tune": func() (cli.Command, error) {
return &command.MountTuneCommand{
Meta: meta,
}, nil
},
"remount": func() (cli.Command, error) {
return &command.RemountCommand{
Meta: meta,

View File

@ -35,17 +35,17 @@ func (c *MountsCommand) Run(args []string) int {
}
paths := make([]string, 0, len(mounts))
for path, _ := range mounts {
for path := range mounts {
paths = append(paths, path)
}
sort.Strings(paths)
columns := []string{"Path | Type | Description"}
columns := []string{"Path | Type | Default TTL | Max TTL | Description"}
for _, path := range paths {
mount := mounts[path]
columns = append(columns, fmt.Sprintf(
"%s | %s | %s", path, mount.Type, mount.Description))
"%s | %s | %s | %s | %s", path, mount.Type, mount.Config.DefaultLeaseTTL, mount.Config.MaxLeaseTTL, mount.Description))
}
c.Ui.Output(columnize.SimpleFormat(columns))
@ -62,8 +62,9 @@ Usage: vault mounts [options]
Outputs information about the mounted backends.
This command lists the mounted backends, their mount points, and
a human-friendly description of the mount point.
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.
General Options:

106
command/mounttune.go Normal file
View File

@ -0,0 +1,106 @@
package command
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/vault"
)
// RemountCommand is a Command that remounts a mounted secret backend
// to a new endpoint.
type MountTuneCommand struct {
Meta
}
func (c *MountTuneCommand) Run(args []string) int {
var defaultLeaseTTL, maxLeaseTTL string
flags := c.Meta.FlagSet("mount-tune", FlagSetDefault)
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
}
args = flags.Args()
if len(args) != 1 {
flags.Usage()
c.Ui.Error(fmt.Sprintf(
"\n'mount-tune' expects one arguments: the mount path"))
return 1
}
path := args[0]
mountConfig := vault.MountConfig{}
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
}
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing client: %s", err))
return 2
}
if err := client.Sys().TuneMount(path, mountConfig); err != nil {
c.Ui.Error(fmt.Sprintf(
"Mount tune error: %s", err))
return 2
}
c.Ui.Output(fmt.Sprintf(
"Successfully tuned mount '%s'!", path))
return 0
}
func (c *MountTuneCommand) Synopsis() string {
return "Tune mount configuration parameters"
}
func (c *MountTuneCommand) Help() string {
helpText := `
Usage: vault mount-tune [options] path
Tune configuration options fro a mounted secret backend.
Example: vault tune-mount -default-lease-ttl="24h" secret/
General Options:
` + generalOptionsUsage() + `
Mount Options:
-default-lease-ttl=<duration> 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.
-max-lease-ttl=<duration> 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.
`
return strings.TrimSpace(helpText)
}

View File

@ -3,9 +3,6 @@ package command
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/vault"
)
// RemountCommand is a Command that remounts a mounted secret backend
@ -15,10 +12,7 @@ type RemountCommand struct {
}
func (c *RemountCommand) Run(args []string) int {
var defaultLeaseTTL, maxLeaseTTL string
flags := c.Meta.FlagSet("remount", FlagSetDefault)
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
@ -35,26 +29,6 @@ func (c *RemountCommand) Run(args []string) int {
from := args[0]
to := args[1]
mountConfig := vault.MountConfig{}
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
}
client, err := c.Client()
if err != nil {
c.Ui.Error(fmt.Sprintf(
@ -62,7 +36,7 @@ func (c *RemountCommand) Run(args []string) int {
return 2
}
if err := client.Sys().Remount(from, to, mountConfig); err != nil {
if err := client.Sys().Remount(from, to); err != nil {
c.Ui.Error(fmt.Sprintf(
"Unmount error: %s", err))
return 2
@ -89,27 +63,11 @@ Usage: vault remount [options] from to
the Vault data associated with the backend will be preserved (such
as configuration data).
If the 'from' and 'to' values of the same, performs an in-place
remount. This allows you to change mount options.
Example: vault remount secret/ generic/
General Options:
` + generalOptionsUsage() + `
` + generalOptionsUsage()
Mount Options:
-default-lease-ttl=<duration> 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.
-max-lease-ttl=<duration> 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.
`
return strings.TrimSpace(helpText)
}

View File

@ -56,11 +56,8 @@ type Backend struct {
// See the built-in AuthRenew helpers in lease.go for common callbacks.
AuthRenew OperationFunc
// System provides an interface to access certain system configuration
// information, such as globally configured default and max lease TTLs.
System logical.SystemView
logger *log.Logger
system logical.SystemView
once sync.Once
pathsRe []*regexp.Regexp
}
@ -146,7 +143,7 @@ func (b *Backend) SpecialPaths() *logical.Paths {
// Setup is used to initialize the backend with the initial backend configuration
func (b *Backend) Setup(config *logical.BackendConfig) (logical.Backend, error) {
b.logger = config.Logger
b.System = config.System
b.system = config.System
return b, nil
}
@ -160,6 +157,10 @@ func (b *Backend) Logger() *log.Logger {
return log.New(ioutil.Discard, "", 0)
}
func (b *Backend) System() logical.SystemView {
return b.system
}
// Route looks up the path that would be used for a given path string.
func (b *Backend) Route(path string) *Path {
result, _ := b.route(path)

View File

@ -22,6 +22,10 @@ type Backend interface {
// ends in '*' then it is a prefix-based match. The '*' can only appear
// at the end.
SpecialPaths() *Paths
// System provides an interface to access certain system configuration
// information, such as globally configured default and max lease TTLs.
System() SystemView
}
// BackendConfig is provided to the factory to initialize the backend

View File

@ -6,12 +6,12 @@ import "time"
// for logical backends to consume
type SystemView interface {
// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
DefaultLeaseTTL() time.Duration
DefaultLeaseTTL() (time.Duration, error)
// MaxLeaseTTL returns the max lease TTL set in Vault configuration; backend
// authors should take care not to issue credentials that last longer than
// this value, as Vault will revoke them
MaxLeaseTTL() time.Duration
MaxLeaseTTL() (time.Duration, error)
}
type StaticSystemView struct {
@ -19,29 +19,10 @@ type StaticSystemView struct {
MaxLeaseTTLVal time.Duration
}
func (d *StaticSystemView) DefaultLeaseTTL() time.Duration {
return d.DefaultLeaseTTLVal
func (d StaticSystemView) DefaultLeaseTTL() (time.Duration, error) {
return d.DefaultLeaseTTLVal, nil
}
func (d *StaticSystemView) MaxLeaseTTL() time.Duration {
return d.MaxLeaseTTLVal
}
type DynamicSystemView struct {
DefaultLeaseTTLVal **time.Duration
MaxLeaseTTLVal **time.Duration
}
func (d *DynamicSystemView) DefaultLeaseTTL() time.Duration {
if *d.DefaultLeaseTTLVal == nil {
return 0
}
return **d.DefaultLeaseTTLVal
}
func (d *DynamicSystemView) MaxLeaseTTL() time.Duration {
if *d.MaxLeaseTTLVal == nil {
return 0
}
return **d.MaxLeaseTTLVal
func (d StaticSystemView) MaxLeaseTTL() (time.Duration, error) {
return d.MaxLeaseTTLVal, nil
}

View File

@ -478,7 +478,7 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r
// 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/") {
// Get the SystemView for the mount
sysView, err := c.PathSysView(req.Path)
sysView, err := c.sysViewByPath(req.Path)
if err != nil {
c.logger.Println(err)
return nil, auth, ErrInternalError
@ -486,12 +486,22 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r
// Apply the default lease if none given
if resp.Secret.TTL == 0 {
resp.Secret.TTL = sysView.DefaultLeaseTTL()
ttl, err := sysView.DefaultLeaseTTL()
if err != nil {
c.logger.Println(err)
return nil, auth, ErrInternalError
}
resp.Secret.TTL = ttl
}
// Limit the lease duration
if resp.Secret.TTL > sysView.MaxLeaseTTL() {
resp.Secret.TTL = sysView.MaxLeaseTTL()
maxTTL, err := sysView.MaxLeaseTTL()
if err != nil {
c.logger.Println(err)
return nil, auth, ErrInternalError
}
if resp.Secret.TTL > maxTTL {
resp.Secret.TTL = maxTTL
}
// Register the lease

View File

@ -56,14 +56,36 @@ func NewSystemBackend(core *Core) logical.Backend {
},
&framework.Path{
Pattern: "mounts/(?P<path>.+)",
Pattern: "mounts/(?P<path>.+?)/tune$",
Fields: map[string]*framework.FieldSchema{
"path": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_path"][0]),
},
"config": &framework.FieldSchema{
Type: framework.TypeMap,
Description: strings.TrimSpace(sysHelp["mount_config"][0]),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handleMountConfig,
logical.WriteOperation: b.handleMountTune,
},
HelpSynopsis: strings.TrimSpace(sysHelp["mount_tune"][0]),
HelpDescription: strings.TrimSpace(sysHelp["mount_tune"][1]),
},
&framework.Path{
Pattern: "mounts/(?P<path>.+?)",
Fields: map[string]*framework.FieldSchema{
"path": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_path"][0]),
},
"type": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["mount_type"][0]),
@ -99,10 +121,6 @@ func NewSystemBackend(core *Core) logical.Backend {
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["remount_to"][0]),
},
"config": &framework.FieldSchema{
Type: framework.TypeMap,
Description: strings.TrimSpace(sysHelp["mount_config"][0]),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
@ -449,20 +467,73 @@ func (b *SystemBackend) handleRemount(
logical.ErrInvalidRequest
}
var config MountConfig
configMap := data.Get("config").(map[string]interface{})
if configMap != nil && len(configMap) != 0 {
err := mapstructure.Decode(configMap, &config)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to convert given mount config information: %s", err)),
logical.ErrInvalidRequest
}
// Attempt remount
if err := b.Core.remount(fromPath, toPath); err != nil {
b.Backend.Logger().Printf("[ERR] sys: remount '%s' to '%s' failed: %v", fromPath, toPath, err)
return handleError(err)
}
// Attempt remount
if err := b.Core.remount(fromPath, toPath, config); err != nil {
b.Backend.Logger().Printf("[ERR] sys: remount '%s' to '%s' failed: %v", fromPath, toPath, err)
return nil, nil
}
// handleMountConfig is used to get config settings on a backend
func (b *SystemBackend) handleMountConfig(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
path := data.Get("path").(string)
if path == "" {
return logical.ErrorResponse(
"path must be specified as a string"),
logical.ErrInvalidRequest
}
def, max, err := b.Core.TTLsByPath(path)
if err != nil {
b.Backend.Logger().Printf("[ERR] sys: fetching config of path '%s' failed: %v", path, err)
return handleError(err)
}
config := MountConfig{
DefaultLeaseTTL: &def,
MaxLeaseTTL: &max,
}
resp := &logical.Response{
Data: map[string]interface{}{
"config": config,
},
}
return resp, nil
}
// handleMountTune is used to set config settings on a backend
func (b *SystemBackend) handleMountTune(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
path := data.Get("path").(string)
if path == "" {
return logical.ErrorResponse(
"path must be specified as a string"),
logical.ErrInvalidRequest
}
var config MountConfig
configMap := data.Get("config").(map[string]interface{})
if configMap == nil || len(configMap) == 0 {
return logical.ErrorResponse(
"invalid parameters; 'config' empty or not supplied"),
logical.ErrInvalidRequest
}
err := mapstructure.Decode(configMap, &config)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to convert given mount config information: %s", err)),
logical.ErrInvalidRequest
}
// Attempt tune
if err := b.Core.tuneMount(path, config); err != nil {
b.Backend.Logger().Printf("[ERR] sys: tune of path '%s' failed: %v", path, err)
return handleError(err)
}
@ -893,6 +964,10 @@ Change the mount point of an already-mounted backend.
"",
},
"mount_tune": {
"Tune backend configuration parameters for this mount.",
},
"renew": {
"Renew a lease on a secret",
`

View File

@ -40,6 +40,27 @@ var (
}
)
type dynamicSystemView struct {
core *Core
path string
}
func (d dynamicSystemView) DefaultLeaseTTL() (time.Duration, error) {
def, _, err := d.core.TTLsByPath(d.path)
if err != nil {
return 0, err
}
return def, nil
}
func (d dynamicSystemView) MaxLeaseTTL() (time.Duration, error) {
def, _, err := d.core.TTLsByPath(d.path)
if err != nil {
return 0, err
}
return def, nil
}
// MountTable is used to represent the internal mount table
type MountTable struct {
// This lock should be held whenever modifying the Entries field.
@ -118,8 +139,8 @@ type MountEntry struct {
// MountConfig is used to hold settable options
type MountConfig struct {
DefaultLeaseTTL *time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl"` // Override for global default
MaxLeaseTTL *time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl"` // Override for global default
DefaultLeaseTTL *time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
MaxLeaseTTL *time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
}
// Returns a deep copy of the mount entry
@ -165,7 +186,7 @@ func (c *Core) mount(me *MountEntry) error {
view := NewBarrierView(c.barrier, backendBarrierPrefix+me.UUID+"/")
// Create the new backend
sysView, err := c.MountEntrySysView(me)
sysView, err := c.mountEntrySysView(me)
if err != nil {
return err
}
@ -283,7 +304,7 @@ func (c *Core) taintMountEntry(path string) error {
}
// Remount is used to remount a path at a new mount point.
func (c *Core) remount(src, dst string, config MountConfig) error {
func (c *Core) remount(src, dst string) error {
c.mounts.Lock()
defer c.mounts.Unlock()
@ -302,48 +323,12 @@ func (c *Core) remount(src, dst string, config MountConfig) error {
}
}
inPlace := false
if src == dst {
inPlace = true
}
// Verify exact match of the route
match := c.router.MatchingMount(src)
if match == "" || src != match {
return fmt.Errorf("no matching mount at '%s'", src)
}
// Verify there is no conflicting mount
if inPlace {
for _, ent := range c.mounts.Entries {
if ent.Path == src {
if config.DefaultLeaseTTL != nil {
if *config.DefaultLeaseTTL == 0 {
ent.Config.DefaultLeaseTTL = &c.defaultLeaseTTL
} else {
ent.Config.DefaultLeaseTTL = config.DefaultLeaseTTL
}
}
if config.MaxLeaseTTL != nil {
if *config.MaxLeaseTTL == 0 {
ent.Config.MaxLeaseTTL = &c.maxLeaseTTL
} else {
ent.Config.MaxLeaseTTL = config.MaxLeaseTTL
}
}
break
}
}
// Update the mount table
if err := c.persistMounts(c.mounts); err != nil {
return errors.New("failed to update mount table")
}
c.logger.Printf("[INFO] core: remounted '%s' in-place", src)
return nil
}
if match := c.router.MatchingMount(dst); match != "" {
return fmt.Errorf("existing mount at '%s'", match)
}
@ -371,23 +356,10 @@ func (c *Core) remount(src, dst string, config MountConfig) error {
// Update the entry in the mount table
newTable := c.mounts.Clone()
for _, ent := range newTable.Entries {
//FIXME: Update systemview in each logical backend
if ent.Path == src {
ent.Path = dst
ent.Tainted = false
if config.DefaultLeaseTTL != nil {
if *config.DefaultLeaseTTL == 0 {
ent.Config.DefaultLeaseTTL = &c.defaultLeaseTTL
} else {
ent.Config.DefaultLeaseTTL = config.DefaultLeaseTTL
}
}
if config.MaxLeaseTTL != nil {
if *config.MaxLeaseTTL == 0 {
ent.Config.MaxLeaseTTL = &c.maxLeaseTTL
} else {
ent.Config.MaxLeaseTTL = config.MaxLeaseTTL
}
}
break
}
}
@ -412,6 +384,67 @@ func (c *Core) remount(src, dst string, config MountConfig) error {
return nil
}
// tuneMount is used to set config on a mount point
func (c *Core) tuneMount(path string, config MountConfig) error {
// Ensure we end the path in a slash
if !strings.HasSuffix(path, "/") {
path += "/"
}
// Prevent protected paths from being changed
for _, p := range protectedMounts {
if strings.HasPrefix(path, p) {
return fmt.Errorf("cannot tune '%s'", path)
}
}
// Verify exact match of the route
match := c.router.MatchingMount(path)
if match == "" || path != match {
return fmt.Errorf("no matching mount at '%s'", path)
}
// Find and modify mount
for _, ent := range c.mounts.Entries {
if ent.Path == path {
if config.MaxLeaseTTL != nil {
if *config.MaxLeaseTTL == 0 {
*ent.Config.MaxLeaseTTL = 0
} else {
ent.Config.MaxLeaseTTL = config.MaxLeaseTTL
}
}
if config.DefaultLeaseTTL != nil {
if *ent.Config.MaxLeaseTTL == 0 {
if *config.DefaultLeaseTTL > c.maxLeaseTTL {
return fmt.Errorf("Given default lease TTL of %d greater than system default lease TTL of %d",
*config.DefaultLeaseTTL, c.maxLeaseTTL)
}
} else {
if *ent.Config.MaxLeaseTTL != 0 && *ent.Config.MaxLeaseTTL < *config.DefaultLeaseTTL {
return fmt.Errorf("Given default lease TTL of %d greater than backend max lease TTL of %d",
*config.DefaultLeaseTTL, *ent.Config.MaxLeaseTTL)
}
}
if *config.DefaultLeaseTTL == 0 {
*ent.Config.DefaultLeaseTTL = 0
} else {
ent.Config.DefaultLeaseTTL = config.DefaultLeaseTTL
}
}
break
}
}
// Update the mount table
if err := c.persistMounts(c.mounts); err != nil {
return errors.New("failed to update mount table")
}
c.logger.Printf("[INFO] core: tuned '%s'", path)
return nil
}
// loadMounts is invoked as part of postUnseal to load the mount table
func (c *Core) loadMounts() error {
// Load the existing mount table
@ -481,7 +514,7 @@ func (c *Core) setupMounts() error {
// Initialize the backend
// Create the new backend
sysView, err := c.MountEntrySysView(entry)
sysView, err := c.mountEntrySysView(entry)
if err != nil {
return err
}
@ -542,32 +575,32 @@ func (c *Core) newLogicalBackend(t string, sysView logical.SystemView, view logi
return b, nil
}
// MountEntrySysView creates a logical.SystemView from global and
// mountEntrySysView creates a logical.SystemView from global and
// mount-specific entries
func (c *Core) MountEntrySysView(me *MountEntry) (logical.SystemView, error) {
func (c *Core) mountEntrySysView(me *MountEntry) (logical.SystemView, error) {
if me == nil {
return nil, fmt.Errorf("[ERR] core: nil MountEntry when generating SystemView")
}
sysDefaultLeaseTTLPtr := &c.defaultLeaseTTL
sysMaxLeaseTTLPtr := &c.maxLeaseTTL
sysView := &logical.DynamicSystemView{
DefaultLeaseTTLVal: &sysDefaultLeaseTTLPtr,
MaxLeaseTTLVal: &sysMaxLeaseTTLPtr,
}
if me.Config.DefaultLeaseTTL != nil && *me.Config.DefaultLeaseTTL != 0 {
sysView.DefaultLeaseTTLVal = &me.Config.DefaultLeaseTTL
}
if me.Config.MaxLeaseTTL != nil && *me.Config.MaxLeaseTTL != 0 {
sysView.MaxLeaseTTLVal = &me.Config.MaxLeaseTTL
sysView := dynamicSystemView{
core: c,
path: me.Path,
}
return sysView, nil
}
// PathSysView is a simple helper for MountEntrySysView
func (c *Core) PathSysView(path string) (logical.SystemView, error) {
// sysViewByPath is a simple helper for MountEntrySysView
func (c *Core) sysViewByPath(path string) (logical.SystemView, error) {
me, err := c.mountEntryByPath(path)
if err != nil {
return nil, err
}
return c.mountEntrySysView(me)
}
// mountEntryByPath searches across all tables to find the MountEntry
func (c *Core) mountEntryByPath(path string) (*MountEntry, error) {
pathSep := strings.IndexRune(path, '/')
if pathSep == -1 {
return nil, fmt.Errorf("[ERR] core: failed to find separator for path %s", path)
@ -582,7 +615,28 @@ func (c *Core) PathSysView(path string) (logical.SystemView, error) {
if me == nil {
return nil, fmt.Errorf("[ERR] core: failed to find mount entry for path %s", path)
}
return c.MountEntrySysView(me)
return me, nil
}
// TTLsByPath returns the default and max TTLs corresponding to a particular
// mount point, or the system default
func (c *Core) TTLsByPath(path string) (def, max time.Duration, retErr error) {
me, err := c.mountEntryByPath(path)
if err != nil {
return 0, 0, err
}
def = c.defaultLeaseTTL
max = c.maxLeaseTTL
if me.Config.DefaultLeaseTTL != nil && *me.Config.DefaultLeaseTTL != 0 {
def = *me.Config.DefaultLeaseTTL
}
if me.Config.MaxLeaseTTL != nil && *me.Config.MaxLeaseTTL != 0 {
max = *me.Config.MaxLeaseTTL
}
return
}
// defaultMountTable creates a default mount table

View File

@ -91,6 +91,15 @@ func (r *Router) Remount(src, dst string) error {
// Update the mount point
r.root.Delete(src)
mountEntry, ok := raw.(*mountEntry)
if !ok {
return fmt.Errorf("Unable to retrieve mount entry at '%s'", src)
}
sysView := mountEntry.backend.System()
dynSysView, ok := sysView.(dynamicSystemView)
if ok {
dynSysView.path = dst
}
r.root.Insert(dst, raw)
return nil
}

View File

@ -5,6 +5,7 @@ import (
"strings"
"sync"
"testing"
"time"
"github.com/hashicorp/vault/helper/uuid"
"github.com/hashicorp/vault/logical"
@ -41,6 +42,13 @@ func (n *NoopBackend) SpecialPaths() *logical.Paths {
}
}
func (n *NoopBackend) System() logical.SystemView {
return logical.StaticSystemView{
DefaultLeaseTTLVal: time.Hour * 24,
MaxLeaseTTLVal: time.Hour * 24 * 30,
}
}
func TestRouter_Mount(t *testing.T) {
r := NewRouter()
_, barrier, _ := mockBarrier(t)

View File

@ -6,6 +6,7 @@ import (
"net"
"os/exec"
"testing"
"time"
"golang.org/x/crypto/ssh"
@ -262,3 +263,10 @@ func (n *rawHTTP) HandleRequest(req *logical.Request) (*logical.Response, error)
func (n *rawHTTP) SpecialPaths() *logical.Paths {
return &logical.Paths{Unauthenticated: []string{"*"}}
}
func (n *rawHTTP) System() logical.SystemView {
return logical.StaticSystemView{
DefaultLeaseTTLVal: time.Hour * 24,
MaxLeaseTTLVal: time.Hour * 24 * 30,
}
}

View File

@ -21,6 +21,9 @@ description: |-
<dt>Method</dt>
<dd>GET</dd>
<dt>URL</dt>
<dd>`/sys/mounts`</dd>
<dt>Parameters</dt>
<dd>
None
@ -54,6 +57,40 @@ description: |-
</dd>
</dl>
<dl>
<dt>Description</dt>
<dd>
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.
</dd>
<dt>Method</dt>
<dd>GET</dd>
<dt>URL</dt>
<dd>`/sys/mounts/<mount point>/tune`</dd>
<dt>Parameters</dt>
<dd>
None
</dd>
<dt>Returns</dt>
<dd>
```javascript
{
"config": {
"default_lease_ttl": 0,
"max_lease_ttl": 0
}
}
```
</dd>
</dl>
## POST
<dl>
@ -99,6 +136,39 @@ description: |-
</dd>
</dl>
<dl>
<dt>Description</dt>
<dd>
Tune configuration parameters for a given mount point.
</dd>
<dt>Method</dt>
<dd>POST</dd>
<dt>URL</dt>
<dd>`/sys/mounts/<mount point>/tune`</dd>
<dt>Parameters</dt>
<dd>
<ul>
<li>
<span class="param">config</span>
<span class="param-flags">required</span>
Config options for this mount. This is an object with
two possible values: `default_lease_ttl` and
`max_lease_ttl`. These control the default and
maximum lease time-to-live, respectively. If set
on a specific mount, this overrides the global
defaults.
</li>
</ul>
</dd>
<dt>Returns</dt>
<dd>`204` response code.
</dd>
</dl>
## DELETE
<dl>

View File

@ -28,21 +28,8 @@ description: |-
<li>
<span class="param">to</span>
<span class="param-flags">required</span>
The new mount point. This can be the same
as `from` if you simply want to change
backend configuration with the `config`
parameter.
The new mount point.
</li>
<li>
<span class="param">config</span>
<span class="param-flags">optional</span>
Config options for this mount. This is an object with
two possible values: `default_lease_ttl` and
`max_lease_ttl`. These control the default and
maximum lease time-to-live, respectively. If set
on a specific mount, this overrides the global
defaults.
</li>
</ul>
</dd>