From a439b483f5d2dfcdd13bb673722bd94b547cd41c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 19 Sep 2015 18:24:53 -0400 Subject: [PATCH] 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. --- vault/core.go | 40 ++++++++++++++++----- vault/core_test.go | 3 ++ vault/logical_passthrough.go | 58 +++++++++++++++++++++++++------ vault/logical_passthrough_test.go | 15 ++++++-- vault/testing.go | 1 + 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/vault/core.go b/vault/core.go index d2315593a1..ddefc7a90e 100644 --- a/vault/core.go +++ b/vault/core.go @@ -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 diff --git a/vault/core_test.go b/vault/core_test.go index c418f07baa..b311723436 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -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 { diff --git a/vault/logical_passthrough.go b/vault/logical_passthrough.go index da39d53a48..2c38879eef 100644 --- a/vault/logical_passthrough.go +++ b/vault/logical_passthrough.go @@ -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 diff --git a/vault/logical_passthrough_test.go b/vault/logical_passthrough_test.go index c10eee516a..ec8e505e8a 100644 --- a/vault/logical_passthrough_test.go +++ b/vault/logical_passthrough_test.go @@ -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 +} diff --git a/vault/testing.go b/vault/testing.go index 4fb1c54b4a..764dbbefa1 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -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 }