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 }