open-vault/vault/logical_passthrough.go

164 lines
4.2 KiB
Go

package vault
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
// logical.Factory
func PassthroughBackendFactory(map[string]string) (logical.Backend, error) {
var b PassthroughBackend
b.Backend = &framework.Backend{
Paths: []*framework.Path{
&framework.Path{
Pattern: ".*",
Fields: map[string]*framework.FieldSchema{
"lease": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Lease time for this key when read. Ex: 1h",
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handleRead,
logical.WriteOperation: b.handleWrite,
logical.DeleteOperation: b.handleDelete,
logical.ListOperation: b.handleList,
},
HelpSynopsis: strings.TrimSpace(passthroughHelpSynopsis),
HelpDescription: strings.TrimSpace(passthroughHelpDescription),
},
},
Secrets: []*framework.Secret{
&framework.Secret{
Type: "generic",
Renew: b.handleRead,
Revoke: b.handleRevoke,
},
},
}
return b, nil
}
// PassthroughBackend is used storing secrets directly into the physical
// backend. The secrest are encrypted in the durable storage and custom lease
// information can be specified, but otherwise this backend doesn't do anything
// fancy.
type PassthroughBackend struct {
*framework.Backend
}
func (b *PassthroughBackend) handleRevoke(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// This is a no-op
return nil, nil
}
func (b *PassthroughBackend) handleRead(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Read the path
out, err := req.Storage.Get(req.Path)
if err != nil {
return nil, fmt.Errorf("read failed: %v", err)
}
// Fast-path the no data case
if out == nil {
return nil, nil
}
// Decode the data
var rawData map[string]interface{}
if err := json.Unmarshal(out.Value, &rawData); err != nil {
return nil, fmt.Errorf("json decoding failed: %v", err)
}
// Generate the response
resp := b.Secret("generic").Response(rawData, nil)
resp.Secret.Renewable = false
// Check if there is a lease key
leaseVal, ok := rawData["lease"].(string)
if ok {
leaseDuration, err := time.ParseDuration(leaseVal)
if err == nil {
resp.Secret.Renewable = false
resp.Secret.Lease = leaseDuration
}
}
return resp, nil
}
func (b *PassthroughBackend) handleWrite(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Check that some fields are given
if len(req.Data) == 0 {
return nil, fmt.Errorf("missing data fields")
}
// JSON encode the data
buf, err := json.Marshal(req.Data)
if err != nil {
return nil, fmt.Errorf("json encoding failed: %v", err)
}
// Write out a new key
entry := &logical.StorageEntry{
Key: req.Path,
Value: buf,
}
if err := req.Storage.Put(entry); err != nil {
return nil, fmt.Errorf("failed to write: %v", err)
}
return nil, nil
}
func (b *PassthroughBackend) handleDelete(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// Delete the key at the request path
if err := req.Storage.Delete(req.Path); err != nil {
return nil, err
}
return nil, nil
}
func (b *PassthroughBackend) handleList(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// List the keys at the prefix given by the request
keys, err := req.Storage.List(req.Path)
if err != nil {
return nil, err
}
// Generate the response
return logical.ListResponse(keys), nil
}
const passthroughHelpSynopsis = `
Pass-through secret storage to the physical backend, allowing you to
read/write arbitrary data into secret storage.
`
const passthroughHelpDescription = `
The pass-through backend reads and writes arbitrary data into secret storage,
encrypting it along the way.
A lease can be specified when writing with the "lease" field. If given, then
when the secret is read, Vault will report a lease with that duration. It
is expected that the consumer of this backend properly writes renewed keys
before the lease is up. In addition, revocation must be handled by the
user of this backend.
`