Add delete-version-after to kv CLI subcommands

Adds a new optional parameter "delete-version-after" to the following
CLI subcommands:

- kv metadata put
- kv put
- kv undelete
- kv rollback
This commit is contained in:
Michael Gaffney 2019-06-05 16:20:43 -04:00
parent 62e14c280d
commit 42324c22ff
8 changed files with 357 additions and 155 deletions

View file

@ -26,7 +26,7 @@ Usage: vault kv metadata <subcommand> [options] [args]
Create or update a metadata entry for a key:
$ vault kv metadata put -max-versions=5 secret/foo
$ vault kv metadata put -max-versions=5 -delete-version-after=3h25m19s secret/foo
Get the metadata for a key, this provides information about each existing
version:

View file

@ -4,6 +4,7 @@ import (
"fmt"
"io"
"strings"
"time"
"github.com/mitchellh/cli"
"github.com/posener/complete"
@ -15,9 +16,10 @@ var _ cli.CommandAutocomplete = (*KVMetadataPutCommand)(nil)
type KVMetadataPutCommand struct {
*BaseCommand
flagMaxVersions int
flagCASRequired bool
testStdin io.Reader // for tests
flagMaxVersions int
flagCASRequired bool
flagDeleteVersionAfter time.Duration
testStdin io.Reader // for tests
}
func (c *KVMetadataPutCommand) Synopsis() string {
@ -39,6 +41,10 @@ Usage: vault metadata kv put [options] KEY
$ vault kv metadata put -max-versions=5 secret/foo
Set delete-version-after on the key:
$ vault kv metadata put -delete-version-after=3h25m19s secret/foo
Require Check-and-Set for this key:
$ vault kv metadata put -cas-required secret/foo
@ -69,6 +75,19 @@ func (c *KVMetadataPutCommand) Flags() *FlagSets {
Usage: `If true the key will require the cas parameter to be set on all write requests. If false, the backends configuration will be used.`,
})
f.DurationVar(&DurationVar{
Name: "delete-version-after",
Target: &c.flagDeleteVersionAfter,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: `Specifies the length of time before a version is deleted.
If not set, the backend's configured delete-version-after is used. Cannot be
greater than the backend's delete-version-after. The delete-version-after is
specified as a numeric string with a suffix like "30s" or
"3h25m19s".`,
})
return set
}
@ -122,6 +141,10 @@ func (c *KVMetadataPutCommand) Run(args []string) int {
"cas_required": c.flagCASRequired,
}
if c.flagDeleteVersionAfter > 0 {
data["delete_version_after"] = c.flagDeleteVersionAfter.String()
}
secret, err := client.Logical().Write(path, data)
if err != nil {
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))

View file

@ -5,6 +5,7 @@ import (
"io"
"os"
"strings"
"time"
"github.com/mitchellh/cli"
"github.com/posener/complete"
@ -16,8 +17,9 @@ var _ cli.CommandAutocomplete = (*KVPutCommand)(nil)
type KVPutCommand struct {
*BaseCommand
flagCAS int
testStdin io.Reader // for tests
flagCAS int
flagDeleteVersionAfter time.Duration
testStdin io.Reader // for tests
}
func (c *KVPutCommand) Synopsis() string {
@ -71,6 +73,19 @@ func (c *KVPutCommand) Flags() *FlagSets {
parameter.`,
})
f.DurationVar(&DurationVar{
Name: "delete-version-after",
Target: &c.flagDeleteVersionAfter,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: `Specifies the length of time before this version is
deleted. If not set, the metadata's delete-version-after is used.
Cannot be greater than the metadata's delete-version-after. The
delete-version-after is specified as a numeric string with a suffix
like "30s" or "3h25m19s".`,
})
return set
}
@ -137,6 +152,10 @@ func (c *KVPutCommand) Run(args []string) int {
if c.flagCAS > -1 {
data["options"].(map[string]interface{})["cas"] = c.flagCAS
}
if c.flagDeleteVersionAfter > 0 {
data["options"].(map[string]interface{})["delete_version_after"] = c.flagDeleteVersionAfter.String()
}
}
secret, err := client.Logical().Write(path, data)

View file

@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"strings"
"time"
"github.com/mitchellh/cli"
"github.com/posener/complete"
@ -15,7 +16,8 @@ var _ cli.CommandAutocomplete = (*KVRollbackCommand)(nil)
type KVRollbackCommand struct {
*BaseCommand
flagVersion int
flagVersion int
flagDeleteVersionAfter time.Duration
}
func (c *KVRollbackCommand) Synopsis() string {
@ -53,6 +55,19 @@ func (c *KVRollbackCommand) Flags() *FlagSets {
Usage: `Specifies the version number that should be made current again.`,
})
f.DurationVar(&DurationVar{
Name: "delete-version-after",
Target: &c.flagDeleteVersionAfter,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: `Specifies the length of time before this version is
deleted. If not set, the metadata's delete-version-after is used.
Cannot be greater than the metadata's delete-version-after. The
delete-version-after is specified as a numeric string with a suffix
like "30s" or "3h25m19s".`,
})
return set
}
@ -217,12 +232,18 @@ func (c *KVRollbackCommand) Run(args []string) int {
}
}
secret, err := client.Logical().Write(path, map[string]interface{}{
data = map[string]interface{}{
"data": data,
"options": map[string]interface{}{
"cas": casVersion,
},
})
}
if c.flagDeleteVersionAfter > 0 {
data["options"].(map[string]interface{})["delete_version_after"] = c.flagDeleteVersionAfter.String()
}
secret, err := client.Logical().Write(path, data)
if err != nil {
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
return 2

View file

@ -120,7 +120,7 @@ func TestKVPutCommand(t *testing.T) {
cmd.client = client
code := cmd.Run([]string{
"-cas", "0", "kv/write/cas", "bar=baz",
"-cas", "0", "-delete-version-after", "1h", "kv/write/cas", "bar=baz",
})
if code != 0 {
t.Fatalf("expected 0 to be %d", code)
@ -133,7 +133,7 @@ func TestKVPutCommand(t *testing.T) {
ui, cmd = testKVPutCommand(t)
cmd.client = client
code = cmd.Run([]string{
"-cas", "1", "kv/write/cas", "bar=baz",
"-cas", "1", "-delete-version-after", "1h", "kv/write/cas", "bar=baz",
})
if code != 0 {
t.Fatalf("expected 0 to be %d", code)

View file

@ -3,6 +3,7 @@ package command
import (
"fmt"
"strings"
"time"
"github.com/mitchellh/cli"
"github.com/posener/complete"
@ -14,7 +15,8 @@ var _ cli.CommandAutocomplete = (*KVUndeleteCommand)(nil)
type KVUndeleteCommand struct {
*BaseCommand
flagVersions []string
flagVersions []string
flagDeleteVersionAfter time.Duration
}
func (c *KVUndeleteCommand) Synopsis() string {
@ -51,6 +53,20 @@ func (c *KVUndeleteCommand) Flags() *FlagSets {
Usage: `Specifies the version numbers to undelete.`,
})
f.DurationVar(&DurationVar{
Name: "delete-version-after",
Target: &c.flagDeleteVersionAfter,
Default: 0,
EnvVar: "",
Completion: complete.PredictAnything,
Usage: `Specifies the length of time before these versions will be
deleted. If not set, the metadata's delete-version-after is used.
Cannot be greater than the metadata's delete-version-after. The
delete-version-after is specified as a numeric string with a suffix
like "30s" or
"3h25m19s".`,
})
return set
}
@ -107,6 +123,10 @@ func (c *KVUndeleteCommand) Run(args []string) int {
"versions": kvParseVersionsFlags(c.flagVersions),
}
if c.flagDeleteVersionAfter > 0 {
data["delete_version_after"] = c.flagDeleteVersionAfter.String()
}
secret, err := client.Logical().Write(path, data)
if err != nil {
c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))

View file

@ -39,12 +39,19 @@ key-value store.
- `cas_required` `(bool: false)` If true all keys will require the cas
parameter to be set on all write requests.
- `delete_version_after` `(string:"0s")` If set, specifies the length
of time before a version is deleted.
Accepts [Go duration format string][duration-godoc].
[duration-godoc]: https://golang.org/pkg/time/#ParseDuration
### Sample Payload
```json
{
"max_versions": 5,
"cas_required": false
"cas_required": false,
"delete_version_after": "3h25m19s"
}
```
@ -82,7 +89,8 @@ $ curl \
{
"data": {
"cas_required": false,
"max_versions": 0
"max_versions": 0,
"delete_version_after": "3h25m19s"
}
}
```
@ -143,12 +151,21 @@ have an ACL policy granting the `update` capability.
### Parameters
- `options` `(Map: <optional>)` An object that holds option settings.
- `cas` `(int: <optional>)` - Set the "cas" value to use a Check-And-Set
operation. If not set the write will be allowed. If set to 0 a write will
only be allowed if the key doesnt exist. If the index is non-zero the
write will only be allowed if the keys current version matches the
version specified in the cas parameter.
- `delete_version_after` (`string:"0s"`) Set the `delete_version_after`
value to a duration to specify the `deletion_time` for this
version. If not set, the metadata's `delete_version_after` will be used. If
the metadata's `delete_version_after` is not set, the backend's
`delete_version_after` will be used. If the value is greater than the
metadata's `delete_version_after`, the metadata's `delete_version_after` will be
used. Accepts [Go duration format string][duration-godoc].
- `data` `(Map: <required>)`  The contents of the data map will be stored and
returned on read.
@ -157,7 +174,8 @@ have an ACL policy granting the `update` capability.
```json
{
"options": {
"cas": 0
"cas": 0,
"delete_version_after": "3m"
},
"data": {
"foo": "bar",
@ -182,7 +200,7 @@ $ curl \
{
"data": {
"created_time": "2018-03-22T02:36:43.986212308Z",
"deletion_time": "",
"deletion_time": "2018-03-22T02:39:43.986212308Z",
"destroyed": false,
"version": 1
}
@ -264,14 +282,24 @@ This restores the data, allowing it to be returned on get requests.
- `path` `(string: <required>)` Specifies the path of the secret to undelete.
This is specified as part of the URL.
- `versions` `([]int: <required>)` - The versions to undelete. The versions will
be restored and their data will be returned on normal get requests.
- `delete_version_after` (`string:"0s"`) Set the `delete_version_after` value
to a duration to specify the `deletion_time` for the versions being
undeleted. If not set, the metadata's `delete_version_after` will be used. If
the metadata's `delete_version_after` is not set, the backend's `delete_version_after`
will be used. If the value is greater than the metadata's
`delete_version_after`, the metadata's `delete_version_after` will be used. Accepts
[Go duration format string][duration-godoc].
### Sample Payload
```json
{
"versions": [1, 2]
"versions": [1, 2],
"delete_version_after": "25m"
}
```
@ -298,6 +326,7 @@ numbers from the key-value store.
- `path` `(string: <required>)` Specifies the path of the secret to destroy.
This is specified as part of the URL.
- `versions` `([]int: <required>)` - The versions to destroy. Their data will be
permanently deleted.
@ -433,14 +462,22 @@ have an ACL policy granting the `update` capability.
- `cas_required` `(bool: false)` If true the key will require the cas
parameter to be set on all write requests. If false, the backends
configuration will be used. 
configuration will be used.
- `delete_version_after` `(string:"0s")` Set the `delete_version_after` value
to a duration to specify the `deletion_time` for all new versions
written to this key. If not set, the backend's `delete_version_after` will be
used. If the value is greater than the backend's `delete_version_after`, the
backend's `delete_version_after` will be used. Accepts [Go duration
format string][duration-godoc].
### Sample Payload
```json
{
"max_versions": 5,
"cas_required": false
"cas_required": false,
"delete_version_after": "3h25m19s"
}
```

View file

@ -28,13 +28,13 @@ management tool.
A v2 `kv` secrets engine can be enabled by:
```
```text
$ vault secrets enable -version=2 kv
```
Or, you can pass `kv-v2` as the secrets engine type:
```
```text
$ vault secrets enable kv-v2
```
@ -50,14 +50,14 @@ Once upgraded to version 2, the former paths at which the data was accessible wi
An existing version 1 kv can be upgraded to a version 2 KV store with the CLI command:
```
```text
$ vault kv enable-versioning secret/
Success! Tuned the secrets engine at: secret/
```
or via the API:
```
```text
$ cat payload.json
{
"options": {
@ -163,36 +163,34 @@ allows for writing keys with arbitrary values.
### Writing/Reading arbitrary data
1. Write arbitrary data:
```text
$ vault kv put secret/my-secret my-value=s3cr3t
Key Value
--- -----
created_time 2018-03-30T22:11:48.589157362Z
deletion_time n/a
destroyed false
version 1
Key Value
--- -----
created_time 2019-06-19T17:20:22.985303Z
deletion_time n/a
destroyed false
version 1
```
1. Read arbitrary data:
```text
$ vault kv get secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2018-03-30T22:11:48.589157362Z
deletion_time n/a
destroyed false
version 1
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:20:22.985303Z
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
my-value s3cr3t
====== Data ======
Key Value
--- -----
my-value s3cr3t
```
1. Write another version, the previous version will still be accessible. The
@ -204,48 +202,113 @@ allows for writing keys with arbitrary values.
```text
$ vault kv put -cas=1 secret/my-secret my-value=new-s3cr3t
Key Value
--- -----
created_time 2018-03-30T22:18:37.124228658Z
deletion_time n/a
destroyed false
version 2
Key Value
--- -----
created_time 2019-06-19T17:22:23.369372Z
deletion_time n/a
destroyed false
version 2
```
1. Reading now will return the newest version of the data:
```text
$ vault kv get secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2018-03-30T22:18:37.124228658Z
deletion_time n/a
destroyed false
version 2
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:22:23.369372Z
deletion_time n/a
destroyed false
version 2
====== Data ======
Key Value
--- -----
my-value new-s3cr3t
====== Data ======
Key Value
--- -----
my-value new-s3cr3t
```
1. Previous versions can be accessed with the `-version` flag:
```
```text
$ vault kv get -version=1 secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2018-03-30T22:16:39.808909557Z
deletion_time n/a
destroyed false
version 1
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:20:22.985303Z
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
my-value s3cr3t
====== Data ======
Key Value
--- -----
my-value s3cr3t
```
1. Write another version which will be deleted after a specified
duration. The `-delete-version-after` flag can optionally be passed to specify
a duration of time until the version will be deleted. The previous
versions will still be accessible.
```text
$ vault kv put -delete-version-after=2m secret/my-secret my-value=short-lived-s3cr3t
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time 2019-06-19T17:25:21.834403Z
destroyed false
version 3
```
1. Reading now will return the newest version of the data and show the
`deletion_time`:
```text
$ vault kv get secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time 2019-06-19T17:25:21.834403Z
destroyed false
version 3
====== Data ======
Key Value
--- -----
my-value short-lived-s3cr3t
```
1. Reading after the `deletion_time` will only return metadata:
```text
$ vault kv get secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time 2019-06-19T17:25:21.834403Z
destroyed false
version 3
```
1. Previous versions not deleted can still be accessed with the `-version` flag:
```text
$ vault kv get -version=2 secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:22:23.369372Z
deletion_time n/a
destroyed false
version 2
====== Data ======
Key Value
--- -----
my-value new-s3cr3t
```
### Deleting and Destroying Data
@ -253,7 +316,7 @@ allows for writing keys with arbitrary values.
When deleting data the standard `vault kv delete` command will perform a
soft delete. It will mark the version as deleted and populate a `deletion_time`
timestamp. Soft deletes do not remove the underlying version data from storage,
which allows the version to be undeleted. The `vault kv undelete` commmand
which allows the version to be undeleted. The `vault kv undelete` command
handles undeleting versions.
A version's data is permanently deleted only when the key has more versions than
@ -267,37 +330,37 @@ See the commands below for more information:
1. The latest version of a key can be deleted with the delete command, this also
takes a `-versions` flag to delete prior versions:
```
```text
$ vault kv delete secret/my-secret
Success! Data deleted (if it existed) at: secret/my-secret
Success! Data deleted (if it existed) at: secret/my-secret
```
1. Versions can be undeleted:
```
$ vault kv undelete -versions=2 secret/my-secret
Success! Data written to: secret/undelete/my-secret
```text
$ vault kv undelete -versions=3 secret/my-secret
Success! Data written to: secret/undelete/my-secret
$ vault kv get secret/my-secret
====== Metadata ======
Key Value
--- -----
created_time 2018-03-30T22:18:37.124228658Z
deletion_time n/a
destroyed false
version 2
====== Metadata ======
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time n/a
destroyed false
version 3
====== Data ======
Key Value
--- -----
my-value new-s3cr3t
====== Data ======
Key Value
--- -----
my-value short-lived-s3cr3t
```
1. Destroying a version permanently deletes the underlying data:
```
$ vault kv destroy -versions=2 secret/my-secret
Success! Data written to: secret/destroy/my-secret
```text
$ vault kv destroy -versions=3 secret/my-secret
Success! Data written to: secret/destroy/my-secret
```
### Key Metadata
@ -310,78 +373,97 @@ See the commands below for more information:
1. All metadata and versions for a key can be viewed:
```
```text
$ vault kv metadata get secret/my-secret
======= Metadata =======
Key Value
--- -----
created_time 2018-03-30T22:16:39.808909557Z
current_version 2
max_versions 0
oldest_version 0
updated_time 2018-03-30T22:18:37.124228658Z
========== Metadata ==========
Key Value
--- -----
cas_required false
created_time 2019-06-19T17:20:22.985303Z
current_version 3
delete_version_after 0s
max_versions 0
oldest_version 0
updated_time 2019-06-19T17:23:21.834403Z
====== Version 1 ======
Key Value
--- -----
created_time 2018-03-30T22:16:39.808909557Z
deletion_time n/a
destroyed false
====== Version 1 ======
Key Value
--- -----
created_time 2019-06-19T17:20:22.985303Z
deletion_time n/a
destroyed false
====== Version 2 ======
Key Value
--- -----
created_time 2018-03-30T22:18:37.124228658Z
deletion_time n/a
destroyed true
====== Version 2 ======
Key Value
--- -----
created_time 2019-06-19T17:22:23.369372Z
deletion_time n/a
destroyed false
====== Version 3 ======
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time n/a
destroyed true
```
1. The metadata settings for a key can be configured:
```
$ vault kv metadata put -max-versions 1 secret/my-secret
Success! Data written to: secret/metadata/my-secret
```text
$ vault kv metadata put -max-versions 2 -delete-version-after="3h25m19s" secret/my-secret
Success! Data written to: secret/metadata/my-secret
```
Max versions changes will be applied on next write:
Delete-version-after settings will apply only to new versions. Max versions
changes will be applied on next write:
```
```text
$ vault kv put secret/my-secret my-value=newer-s3cr3t
Key Value
--- -----
created_time 2018-03-30T22:41:09.193643571Z
deletion_time n/a
destroyed false
version 3
Key Value
--- -----
created_time 2019-06-19T17:31:16.662563Z
deletion_time 2019-06-19T20:56:35.662563Z
destroyed false
version 4
```
Once a key has more versions than max versions the oldest versions are cleaned
up:
Once a key has more versions than max versions the oldest versions
are cleaned up:
```
```text
$ vault kv metadata get secret/my-secret
======= Metadata =======
Key Value
--- -----
created_time 2018-03-30T22:16:39.808909557Z
current_version 3
max_versions 1
oldest_version 3
updated_time 2018-03-30T22:41:09.193643571Z
========== Metadata ==========
Key Value
--- -----
cas_required false
created_time 2019-06-19T17:20:22.985303Z
current_version 4
delete_version_after 3h25m19s
max_versions 2
oldest_version 3
updated_time 2019-06-19T17:31:16.662563Z
====== Version 3 ======
Key Value
--- -----
created_time 2018-03-30T22:41:09.193643571Z
deletion_time n/a
destroyed false
====== Version 3 ======
Key Value
--- -----
created_time 2019-06-19T17:23:21.834403Z
deletion_time n/a
destroyed true
====== Version 4 ======
Key Value
--- -----
created_time 2019-06-19T17:31:16.662563Z
deletion_time 2019-06-19T20:56:35.662563Z
destroyed false
```
1. Permanently delete all metadata and versions for a key:
```
```text
$ vault kv metadata delete secret/my-secret
Success! Data deleted (if it existed) at: secret/metadata/my-secret
Success! Data deleted (if it existed) at: secret/metadata/my-secret
```
## API