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:
parent
4239f9d243
commit
488d33c70a
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
`
|
||||
|
|
198
vault/mount.go
198
vault/mount.go
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in New Issue