vault: Support sys/revoke-prefix/

This commit is contained in:
Armon Dadgar 2015-03-16 16:33:48 -07:00
parent f08659aaaa
commit a24192b728
2 changed files with 107 additions and 1 deletions

View File

@ -15,6 +15,7 @@ func NewSystemBackend(core *Core) logical.Backend {
PathsRoot: []string{
"mounts/*",
"remount",
"revoke-prefix/*",
},
Paths: []*framework.Path{
@ -107,6 +108,24 @@ func NewSystemBackend(core *Core) logical.Backend {
HelpSynopsis: strings.TrimSpace(sysHelp["revoke"][0]),
HelpDescription: strings.TrimSpace(sysHelp["revoke"][1]),
},
&framework.Path{
Pattern: "revoke-prefix/(?P<prefix>.+)",
Fields: map[string]*framework.FieldSchema{
"prefix": &framework.FieldSchema{
Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["revoke-prefix-path"][0]),
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.WriteOperation: b.handleRevokePrefix,
},
HelpSynopsis: strings.TrimSpace(sysHelp["revoke-prefix"][0]),
HelpDescription: strings.TrimSpace(sysHelp["revoke-prefix"][1]),
},
},
}
}
@ -241,6 +260,19 @@ func (b *SystemBackend) handleRevoke(
return nil, nil
}
// handleRevokePrefix is used to revoke a prefix with many VaultIDs
func (b *SystemBackend) handleRevokePrefix(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Get all the options
prefix := data.Get("prefix").(string)
// Invoke the expiration manager directly
if err := b.Core.expiration.RevokePrefix(prefix); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
return nil, nil
}
// sysHelp is all the help text for the sys backend.
var sysHelp = map[string][2]string{
"mounts": {
@ -312,4 +344,21 @@ you may want to force an immediate revocation. This endpoint can be
used to revoke the secret with the given Vault ID.
`,
},
"revoke-prefix": {
"Revoke all secrets generated in a given prefix",
`
Revokes all the secrets generated under a given mount prefix. As
an example, "prod/aws/" might be the AWS logical backend, and due to
a change in the "ops" policy, we may want to invalidate all the secrets
generated. We can do a revoke prefix at "prod/aws/ops" to revoke all
the ops secrets. This does a prefix match on the Vault IDs and revokes
all matching leases.
`,
},
"revoke-prefix-path": {
`The path to revoke keys under. Example: "prod/aws/ops"`,
"",
},
}

View File

@ -11,6 +11,7 @@ func TestSystemBackend_RootPaths(t *testing.T) {
expected := []string{
"mounts/*",
"remount",
"revoke-prefix/*",
}
b := testSystemBackend(t)
@ -223,7 +224,7 @@ func TestSystemBackend_revoke(t *testing.T) {
t.Fatalf("bad: %#v", resp)
}
// Attempt renew
// Attempt revoke
req2 := logical.TestRequest(t, logical.WriteOperation, "revoke/"+resp.Lease.VaultID)
resp2, err := b.HandleRequest(req2)
if err != nil {
@ -232,6 +233,16 @@ func TestSystemBackend_revoke(t *testing.T) {
if resp2 != nil {
t.Fatalf("bad: %#v", resp)
}
// Attempt renew
req3 := logical.TestRequest(t, logical.WriteOperation, "renew/"+resp.Lease.VaultID)
resp3, err := b.HandleRequest(req3)
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp3.Data["error"] != "lease not found" {
t.Fatalf("bad: %v", resp)
}
}
func TestSystemBackend_revoke_invalidID(t *testing.T) {
@ -248,6 +259,52 @@ func TestSystemBackend_revoke_invalidID(t *testing.T) {
}
}
func TestSystemBackend_revokePrefix(t *testing.T) {
core, b := testCoreSystemBackend(t)
// Create a key with a lease
req := logical.TestRequest(t, logical.WriteOperation, "secret/foo")
req.Data["foo"] = "bar"
req.Data["lease"] = "1h"
resp, err := core.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %#v", resp)
}
// Read a key with a VaultID
req = logical.TestRequest(t, logical.ReadOperation, "secret/foo")
resp, err = core.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp == nil || resp.Lease == nil || resp.Lease.VaultID == "" {
t.Fatalf("bad: %#v", resp)
}
// Attempt revoke
req2 := logical.TestRequest(t, logical.WriteOperation, "revoke-prefix/secret/")
resp2, err := b.HandleRequest(req2)
if err != nil {
t.Fatalf("err: %v %#v", err, resp2)
}
if resp2 != nil {
t.Fatalf("bad: %#v", resp)
}
// Attempt renew
req3 := logical.TestRequest(t, logical.WriteOperation, "renew/"+resp.Lease.VaultID)
resp3, err := b.HandleRequest(req3)
if err != logical.ErrInvalidRequest {
t.Fatalf("err: %v", err)
}
if resp3.Data["error"] != "lease not found" {
t.Fatalf("bad: %v", resp)
}
}
func testSystemBackend(t *testing.T) logical.Backend {
c, _ := TestCoreUnsealed(t)
return NewSystemBackend(c)