Merge pull request #631 from hashicorp/remove-generic-leases

Don't use leases on the generic backend
This commit is contained in:
Jeff Mitchell 2015-09-21 16:57:57 -04:00
commit 8f7e56b81d
6 changed files with 225 additions and 162 deletions

View file

@ -45,12 +45,16 @@ func outputFormatTable(ui cli.Ui, s *api.Secret, whitespace bool) int {
input := make([]string, 0, 5)
input = append(input, fmt.Sprintf("Key %s Value", config.Delim))
if s.LeaseID != "" && s.LeaseDuration > 0 {
input = append(input, fmt.Sprintf("lease_id %s %s", config.Delim, s.LeaseID))
if s.LeaseDuration > 0 {
if s.LeaseID != "" {
input = append(input, fmt.Sprintf("lease_id %s %s", config.Delim, s.LeaseID))
}
input = append(input, fmt.Sprintf(
"lease_duration %s %d", config.Delim, s.LeaseDuration))
input = append(input, fmt.Sprintf(
"lease_renewable %s %s", config.Delim, strconv.FormatBool(s.Renewable)))
if s.LeaseID != "" {
input = append(input, fmt.Sprintf(
"lease_renewable %s %s", config.Delim, strconv.FormatBool(s.Renewable)))
}
}
if s.Auth != nil {

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,31 @@ 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)
// Generic mounts should return the TTL but not register
// for a lease as this provides a massive slowdown
registerLease := true
matchingBackend := c.router.MatchingBackend(req.Path)
if matchingBackend == nil {
c.logger.Println("[ERR] core: unable to retrieve generic backend from router")
return nil, auth, ErrInternalError
}
resp.Secret.LeaseID = leaseID
if ptbe, ok := matchingBackend.(*PassthroughBackend); ok {
if !ptbe.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,19 @@ func (b *PassthroughBackend) handleRead(
ttl, _ = rawData["ttl"].(string)
}
ttlDuration := b.System().DefaultLeaseTTL()
if len(ttl) != 0 {
ttlDuration, err := time.ParseDuration(ttl)
if err == nil {
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
resp.Secret.TTL = ttlDuration
}
}
resp.Secret.TTL = ttlDuration
return resp, nil
}
@ -163,6 +193,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

@ -10,169 +10,160 @@ import (
func TestPassthroughBackend_RootPaths(t *testing.T) {
b := testPassthroughBackend()
root := b.SpecialPaths()
if root != nil {
t.Fatalf("unexpected: %v", root)
test := func(b logical.Backend) {
root := b.SpecialPaths()
if root != nil {
t.Fatalf("unexpected: %v", root)
}
}
test(b)
b = testPassthroughLeasedBackend()
test(b)
}
func TestPassthroughBackend_Write(t *testing.T) {
test := func(b logical.Backend) {
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
out, err := req.Storage.Get("foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("failed to write to view")
}
}
b := testPassthroughBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
out, err := req.Storage.Get("foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if out == nil {
t.Fatalf("failed to write to view")
}
test(b)
b = testPassthroughLeasedBackend()
test(b)
}
func TestPassthroughBackend_Read_Lease(t *testing.T) {
b := testPassthroughBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
req.Data["lease"] = "1h"
storage := req.Storage
func TestPassthroughBackend_Read(t *testing.T) {
test := func(b logical.Backend, ttlType string, leased bool) {
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
req.Data[ttlType] = "1h"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.ReadOperation, "foo")
req.Storage = storage
req = logical.TestRequest(t, logical.ReadOperation, "foo")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Hour,
expected := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Hour,
},
},
},
Data: map[string]interface{}{
"raw": "test",
"lease": "1h",
},
}
resp.Secret.InternalData = nil
resp.Secret.LeaseID = ""
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp)
}
}
func TestPassthroughBackend_Read_TTL(t *testing.T) {
b := testPassthroughBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
req.Data["ttl"] = "1h"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.ReadOperation, "foo")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &logical.Response{
Secret: &logical.Secret{
LeaseOptions: logical.LeaseOptions{
Renewable: true,
TTL: time.Hour,
Data: map[string]interface{}{
"raw": "test",
ttlType: "1h",
},
},
Data: map[string]interface{}{
"raw": "test",
"ttl": "1h",
},
}
}
resp.Secret.InternalData = nil
resp.Secret.LeaseID = ""
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp)
if !leased {
expected.Secret.Renewable = false
}
resp.Secret.InternalData = nil
resp.Secret.LeaseID = ""
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp)
}
}
b := testPassthroughLeasedBackend()
test(b, "lease", true)
test(b, "ttl", true)
b = testPassthroughBackend()
test(b, "lease", false)
test(b, "ttl", false)
}
func TestPassthroughBackend_Delete(t *testing.T) {
test := func(b logical.Backend) {
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.DeleteOperation, "foo")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
req = logical.TestRequest(t, logical.ReadOperation, "foo")
req.Storage = storage
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
}
b := testPassthroughBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.DeleteOperation, "foo")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
req = logical.TestRequest(t, logical.ReadOperation, "foo")
req.Storage = storage
resp, err = b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
test(b)
b = testPassthroughLeasedBackend()
test(b)
}
func TestPassthroughBackend_List(t *testing.T) {
test := func(b logical.Backend) {
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.ListOperation, "")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &logical.Response{
Data: map[string]interface{}{
"keys": []string{"foo"},
},
}
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp)
}
}
b := testPassthroughBackend()
req := logical.TestRequest(t, logical.WriteOperation, "foo")
req.Data["raw"] = "test"
storage := req.Storage
if _, err := b.HandleRequest(req); err != nil {
t.Fatalf("err: %v", err)
}
req = logical.TestRequest(t, logical.ListOperation, "")
req.Storage = storage
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &logical.Response{
Data: map[string]interface{}{
"keys": []string{"foo"},
},
}
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp)
}
test(b)
b = testPassthroughLeasedBackend()
test(b)
}
func testPassthroughBackend() logical.Backend {
@ -185,3 +176,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
}