Don't use leases on the generic backend...with a caveat.

You can now turn on and off the lease behavior in the generic backend by
using one of two factories. Core uses the normal one if it's not already
set, so unit tests can use the custom one and all stay working.

This also adds logic into core to check, when the response is coming
from a generic backend, whether that backend has leases enabled. This
adds some slight overhead.
This commit is contained in:
Jeff Mitchell 2015-09-19 18:24:53 -04:00
parent a5f52f43b1
commit 47e8c0070a
5 changed files with 96 additions and 21 deletions

View File

@ -350,7 +350,10 @@ func NewCore(conf *CoreConfig) (*Core, error) {
for k, f := range conf.LogicalBackends {
logicalBackends[k] = f
}
logicalBackends["generic"] = PassthroughBackendFactory
_, ok := logicalBackends["generic"]
if !ok {
logicalBackends["generic"] = PassthroughBackendFactory
}
logicalBackends["cubbyhole"] = CubbyholeBackendFactory
logicalBackends["system"] = func(config *logical.BackendConfig) (logical.Backend, error) {
return NewSystemBackend(c, config), nil
@ -503,15 +506,36 @@ func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, r
resp.Secret.TTL = maxTTL
}
// Register the lease
leaseID, err := c.expiration.Register(req, resp)
if err != nil {
c.logger.Printf(
"[ERR] core: failed to register lease "+
"(request: %#v, response: %#v): %v", req, resp, err)
mountEntry := c.router.MatchingMountEntry(req.Path)
if mountEntry == nil {
c.logger.Println("[ERR] core: unable to retrieve generic mount entry from router")
return nil, auth, ErrInternalError
}
resp.Secret.LeaseID = leaseID
// Generic mounts should return the TTL but not register
// for a lease as this provides a massive slowdown
registerLease := true
if mountEntry.Type == "generic" {
backend := c.router.MatchingBackend(req.Path)
if backend == nil {
c.logger.Println("[ERR] core: unable to retrieve generic backend from router")
return nil, auth, ErrInternalError
}
if !backend.(*PassthroughBackend).GeneratesLeases() {
registerLease = false
resp.Secret.Renewable = false
}
}
if registerLease {
leaseID, err := c.expiration.Register(req, resp)
if err != nil {
c.logger.Printf(
"[ERR] core: failed to register lease "+
"(request: %#v, response: %#v): %v", req, resp, err)
return nil, auth, ErrInternalError
}
resp.Secret.LeaseID = leaseID
}
}
// Only the token store is allowed to return an auth block, for any

View File

@ -43,6 +43,9 @@ func TestCore_Init(t *testing.T) {
conf := &CoreConfig{
Physical: inm,
DisableMlock: true,
LogicalBackends: map[string]logical.Factory{
"generic": LeasedPassthroughBackendFactory,
},
}
c, err := NewCore(conf)
if err != nil {

View File

@ -10,9 +10,23 @@ import (
"github.com/hashicorp/vault/logical/framework"
)
// logical.Factory
// PassthroughBackendFactory returns a PassthroughBackend
// with leases switched off
func PassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, error) {
return LeaseSwitchedPassthroughBackend(conf, false)
}
// PassthroughBackendWithLeasesFactory returns a PassthroughBackend
// with leases switched on
func LeasedPassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, error) {
return LeaseSwitchedPassthroughBackend(conf, true)
}
// LeaseSwitchedPassthroughBackendFactory returns a PassthroughBackend
// with leases switched on or off
func LeaseSwitchedPassthroughBackend(conf *logical.BackendConfig, leases bool) (logical.Backend, error) {
var b PassthroughBackend
b.generateLeases = leases
b.Backend = &framework.Backend{
Help: strings.TrimSpace(passthroughHelp),
@ -42,15 +56,17 @@ func PassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, er
HelpDescription: strings.TrimSpace(passthroughHelpDescription),
},
},
}
Secrets: []*framework.Secret{
if b.generateLeases {
b.Backend.Secrets = []*framework.Secret{
&framework.Secret{
Type: "generic",
Renew: b.handleRead,
Revoke: b.handleRevoke,
},
},
}
}
if conf == nil {
@ -58,7 +74,7 @@ func PassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, er
}
b.Backend.Setup(conf)
return b, nil
return &b, nil
}
// PassthroughBackend is used storing secrets directly into the physical
@ -67,6 +83,7 @@ func PassthroughBackendFactory(conf *logical.BackendConfig) (logical.Backend, er
// fancy.
type PassthroughBackend struct {
*framework.Backend
generateLeases bool
}
func (b *PassthroughBackend) handleRevoke(
@ -94,9 +111,17 @@ func (b *PassthroughBackend) handleRead(
return nil, fmt.Errorf("json decoding failed: %v", err)
}
// Generate the response
resp := b.Secret("generic").Response(rawData, nil)
resp.Secret.Renewable = false
var resp *logical.Response
if b.generateLeases {
// Generate the response
resp = b.Secret("generic").Response(rawData, nil)
resp.Secret.Renewable = false
} else {
resp = &logical.Response{
Secret: &logical.Secret{},
Data: rawData,
}
}
// Check if there is a ttl key
var ttl string
@ -105,14 +130,21 @@ func (b *PassthroughBackend) handleRead(
ttl, _ = rawData["ttl"].(string)
}
var ttlDuration time.Duration
if len(ttl) != 0 {
ttlDuration, err := time.ParseDuration(ttl)
if err == nil {
resp.Secret.Renewable = true
resp.Secret.TTL = ttlDuration
ttlDuration, err = time.ParseDuration(ttl)
if err != nil {
return logical.ErrorResponse("failed to parse ttl for entry"), nil
}
if b.generateLeases {
resp.Secret.Renewable = true
}
} else {
ttlDuration = b.System().DefaultLeaseTTL()
}
resp.Secret.TTL = ttlDuration
return resp, nil
}
@ -163,6 +195,10 @@ func (b *PassthroughBackend) handleList(
return logical.ListResponse(keys), nil
}
func (b *PassthroughBackend) GeneratesLeases() bool {
return b.generateLeases
}
const passthroughHelp = `
The generic backend reads and writes arbitrary secrets to the backend.
The secrets are encrypted/decrypted by Vault: they are never stored

View File

@ -39,7 +39,7 @@ func TestPassthroughBackend_Write(t *testing.T) {
}
func TestPassthroughBackend_Read_Lease(t *testing.T) {
b := testPassthroughBackend()
b := testPassthroughLeasedBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
req.Data["lease"] = "1h"
@ -78,7 +78,7 @@ func TestPassthroughBackend_Read_Lease(t *testing.T) {
}
func TestPassthroughBackend_Read_TTL(t *testing.T) {
b := testPassthroughBackend()
b := testPassthroughLeasedBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
req.Data["ttl"] = "1h"
@ -185,3 +185,14 @@ func testPassthroughBackend() logical.Backend {
})
return b
}
func testPassthroughLeasedBackend() logical.Backend {
b, _ := LeasedPassthroughBackendFactory(&logical.BackendConfig{
Logger: nil,
System: logical.StaticSystemView{
DefaultLeaseTTLVal: time.Hour * 24,
MaxLeaseTTLVal: time.Hour * 24 * 30,
},
})
return b
}

View File

@ -76,6 +76,7 @@ func TestCore(t *testing.T) *Core {
for backendName, backendFactory := range noopBackends {
logicalBackends[backendName] = backendFactory
}
logicalBackends["generic"] = LeasedPassthroughBackendFactory
for backendName, backendFactory := range testLogicalBackends {
logicalBackends[backendName] = backendFactory
}