logical: put structs here, vault uses them
This commit is contained in:
parent
1837991454
commit
63a9eb321a
|
@ -0,0 +1,24 @@
|
|||
package logical
|
||||
|
||||
// Backend interface must be implemented to be "mountable" at
|
||||
// a given path. Requests flow through a router which has various mount
|
||||
// points that flow to a logical backend. The logic of each backend is flexible,
|
||||
// and this is what allows materialized keys to function. There can be specialized
|
||||
// logical backends for various upstreams (Consul, PostgreSQL, MySQL, etc) that can
|
||||
// interact with remote APIs to generate keys dynamically. This interface also
|
||||
// allows for a "procfs" like interaction, as internal state can be exposed by
|
||||
// acting like a logical backend and being mounted.
|
||||
type Backend interface {
|
||||
// HandleRequest is used to handle a request and generate a response.
|
||||
// The backends must check the operation type and handle appropriately.
|
||||
HandleRequest(*Request) (*Response, error)
|
||||
|
||||
// RootPaths is a list of paths that require root level privileges.
|
||||
// These paths will be enforced by the router so that backends do
|
||||
// not need to handle the authorization. Paths are enforced exactly
|
||||
// or using a prefix match if they end in '*'
|
||||
RootPaths() []string
|
||||
}
|
||||
|
||||
// Factory is the factory function to create a logical backend.
|
||||
type Factory func(map[string]string) (Backend, error)
|
|
@ -0,0 +1,49 @@
|
|||
package logical
|
||||
|
||||
// Request is a struct that stores the parameters and context
|
||||
// of a request being made to Vault. It is used to abstract
|
||||
// the details of the higher level request protocol from the handlers.
|
||||
type Request struct {
|
||||
// Operation is the requested operation type
|
||||
Operation Operation
|
||||
|
||||
// Path is the part of the request path not consumed by the
|
||||
// routing. As an example, if the original request path is "prod/aws/foo"
|
||||
// and the AWS logical backend is mounted at "prod/aws/", then the
|
||||
// final path is "foo" since the mount prefix is trimmed.
|
||||
Path string
|
||||
|
||||
// Request data is an opaque map that must have string keys.
|
||||
Data map[string]interface{}
|
||||
|
||||
// Storage can be used to durably store and retrieve state.
|
||||
Storage Storage
|
||||
}
|
||||
|
||||
// Get returns a data field and guards for nil Data
|
||||
func (r *Request) Get(key string) interface{} {
|
||||
if r.Data == nil {
|
||||
return nil
|
||||
}
|
||||
return r.Data[key]
|
||||
}
|
||||
|
||||
// GetString returns a data field as a string
|
||||
func (r *Request) GetString(key string) string {
|
||||
raw := r.Get(key)
|
||||
s, _ := raw.(string)
|
||||
return s
|
||||
}
|
||||
|
||||
// Operation is an enum that is used to specify the type
|
||||
// of request being made
|
||||
type Operation string
|
||||
|
||||
const (
|
||||
ReadOperation Operation = "read"
|
||||
WriteOperation = "write"
|
||||
DeleteOperation = "delete"
|
||||
ListOperation = "list"
|
||||
RevokeOperation = "revoke"
|
||||
HelpOperation = "help"
|
||||
)
|
|
@ -0,0 +1,68 @@
|
|||
package logical
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Response is a struct that stores the response of a request.
|
||||
// It is used to abstract the details of the higher level request protocol.
|
||||
type Response struct {
|
||||
// IsSecret is used to indicate this is secret material instead of policy or configuration.
|
||||
// Non-secrets never have a VaultID or renewable properties.
|
||||
IsSecret bool
|
||||
|
||||
// The lease settings if applicable.
|
||||
Lease *Lease
|
||||
|
||||
// Response data is an opaque map that must have string keys.
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
// Lease is used to provide more information about the lease
|
||||
type Lease struct {
|
||||
VaultID string // VaultID is the unique identifier used for renewal and revocation
|
||||
Renewable bool // Is the VaultID renewable
|
||||
Revokable bool // Is the secret revokable. Must support 'Revoke' operation.
|
||||
Duration time.Duration // Current lease duration
|
||||
MaxDuration time.Duration // Maximum lease duration
|
||||
MaxIncrement time.Duration // Maximum increment to lease duration
|
||||
}
|
||||
|
||||
// Validate is used to sanity check a lease
|
||||
func (l *Lease) Validate() error {
|
||||
if l.Duration <= 0 {
|
||||
return fmt.Errorf("lease duration must be greater than zero")
|
||||
}
|
||||
if l.MaxDuration <= 0 {
|
||||
return fmt.Errorf("maximum lease duration must be greater than zero")
|
||||
}
|
||||
if l.Duration > l.MaxDuration {
|
||||
return fmt.Errorf("lease duration cannot be greater than maximum lease duration")
|
||||
}
|
||||
if l.MaxIncrement < 0 {
|
||||
return fmt.Errorf("maximum lease increment cannot be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HelpResponse is used to format a help response
|
||||
func HelpResponse(text string, seeAlso []string) *Response {
|
||||
return &Response{
|
||||
IsSecret: false,
|
||||
Data: map[string]interface{}{
|
||||
"help": text,
|
||||
"see_also": seeAlso,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorResponse is used to format an error response
|
||||
func ErrorResponse(text string) *Response {
|
||||
return &Response{
|
||||
IsSecret: false,
|
||||
Data: map[string]interface{}{
|
||||
"error": text,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package logical
|
||||
|
||||
// Storage is the way that logical backends are able read/write data.
|
||||
type Storage interface {
|
||||
List(prefix string) ([]string, error)
|
||||
Get(string) (*StorageEntry, error)
|
||||
Put(*StorageEntry) error
|
||||
Delete(string) error
|
||||
}
|
||||
|
||||
// StorageEntry is the entry for an item in a Storage implementation.
|
||||
type StorageEntry struct {
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
package vault
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrBarrierSealed is returned if an operation is performed on
|
||||
|
@ -76,3 +80,11 @@ type Entry struct {
|
|||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// Logical turns the Entry into a logical storage entry.
|
||||
func (e *Entry) Logical() *logical.StorageEntry {
|
||||
return &logical.StorageEntry{
|
||||
Key: e.Key,
|
||||
Value: e.Value,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
package vault
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
// BarrierView is used to wrap a barrier and ensure all access is automatically
|
||||
// prefixed. This means that nothing outside of the given prefix can be
|
||||
// accessed through the view, which is an additional layer of security when
|
||||
// interacting with the security barrier. Conceptually this is like a
|
||||
// "chroot" into the barrier.
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// BarrierView wraps a SecurityBarrier and ensures all access is automatically
|
||||
// prefixed. This is used to prevent anyone with access to the view to access
|
||||
// any data in the durable storage outside of their prefix. Conceptually this
|
||||
// is like a "chroot" into the barrier.
|
||||
//
|
||||
// BarrierView implements logical.Storage so it can be passed in as the
|
||||
// durable storage mechanism for logical views.
|
||||
type BarrierView struct {
|
||||
barrier SecurityBarrier
|
||||
prefix string
|
||||
|
@ -21,14 +27,32 @@ func NewBarrierView(barrier SecurityBarrier, prefix string) *BarrierView {
|
|||
}
|
||||
}
|
||||
|
||||
// SubView constructs a nested sub-view using the given prefix
|
||||
func (v *BarrierView) SubView(prefix string) *BarrierView {
|
||||
sub := v.expandKey(prefix)
|
||||
return &BarrierView{barrier: v.barrier, prefix: sub}
|
||||
// logical.Storage impl.
|
||||
func (v *BarrierView) List(prefix string) ([]string, error) {
|
||||
return v.barrier.List(v.expandKey(prefix))
|
||||
}
|
||||
|
||||
// Put is used to insert or update an entry
|
||||
func (v *BarrierView) Put(entry *Entry) error {
|
||||
// logical.Storage impl.
|
||||
func (v *BarrierView) Get(key string) (*logical.StorageEntry, error) {
|
||||
entry, err := v.barrier.Get(v.expandKey(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if entry != nil {
|
||||
entry.Key = v.truncateKey(entry.Key)
|
||||
}
|
||||
|
||||
return &logical.StorageEntry{
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// logical.Storage impl.
|
||||
func (v *BarrierView) Put(entry *logical.StorageEntry) error {
|
||||
nested := &Entry{
|
||||
Key: v.expandKey(entry.Key),
|
||||
Value: entry.Value,
|
||||
|
@ -36,27 +60,15 @@ func (v *BarrierView) Put(entry *Entry) error {
|
|||
return v.barrier.Put(nested)
|
||||
}
|
||||
|
||||
// Get is used to fetch an entry
|
||||
func (v *BarrierView) Get(key string) (*Entry, error) {
|
||||
entry, err := v.barrier.Get(v.expandKey(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry != nil {
|
||||
entry.Key = v.truncateKey(entry.Key)
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// Delete is used to permanently delete an entry
|
||||
// logical.Storage impl.
|
||||
func (v *BarrierView) Delete(key string) error {
|
||||
return v.barrier.Delete(v.expandKey(key))
|
||||
}
|
||||
|
||||
// List is used ot list all the keys under a given
|
||||
// prefix, up to the next prefix.
|
||||
func (v *BarrierView) List(prefix string) ([]string, error) {
|
||||
return v.barrier.List(v.expandKey(prefix))
|
||||
// SubView constructs a nested sub-view using the given prefix
|
||||
func (v *BarrierView) SubView(prefix string) *BarrierView {
|
||||
sub := v.expandKey(prefix)
|
||||
return &BarrierView{barrier: v.barrier, prefix: sub}
|
||||
}
|
||||
|
||||
// expandKey is used to expand to the full key path with the prefix
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package vault
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
func TestBarrierView_impl(t *testing.T) {
|
||||
var _ logical.Storage = new(BarrierView)
|
||||
}
|
||||
|
||||
func TestBarrierView(t *testing.T) {
|
||||
_, barrier, _ := mockBarrier(t)
|
||||
|
@ -31,16 +39,16 @@ func TestBarrierView(t *testing.T) {
|
|||
}
|
||||
|
||||
// Try to put the same entry via the view
|
||||
if err := view.Put(entry); err != nil {
|
||||
if err := view.Put(entry.Logical()); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check it is nested
|
||||
out, err = barrier.Get("foo/test")
|
||||
entry, err = barrier.Get("foo/test")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
if entry == nil {
|
||||
t.Fatalf("missing nested foo/test")
|
||||
}
|
||||
|
||||
|
@ -50,20 +58,20 @@ func TestBarrierView(t *testing.T) {
|
|||
}
|
||||
|
||||
// Check the nested key
|
||||
out, err = barrier.Get("foo/test")
|
||||
entry, err = barrier.Get("foo/test")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out != nil {
|
||||
if entry != nil {
|
||||
t.Fatalf("nested foo/test should be gone")
|
||||
}
|
||||
|
||||
// Check the non-nested key
|
||||
out, err = barrier.Get("test")
|
||||
entry, err = barrier.Get("test")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
if entry == nil {
|
||||
t.Fatalf("root test missing")
|
||||
}
|
||||
}
|
||||
|
@ -92,17 +100,17 @@ func TestBarrierView_SubView(t *testing.T) {
|
|||
}
|
||||
|
||||
// Try to put the same entry via the view
|
||||
entry := &Entry{Key: "test", Value: []byte("test")}
|
||||
entry := &logical.StorageEntry{Key: "test", Value: []byte("test")}
|
||||
if err := view.Put(entry); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check it is nested
|
||||
out, err = barrier.Get("foo/bar/test")
|
||||
bout, err := barrier.Get("foo/bar/test")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out == nil {
|
||||
if bout == nil {
|
||||
t.Fatalf("missing nested foo/bar/test")
|
||||
}
|
||||
|
||||
|
@ -121,11 +129,11 @@ func TestBarrierView_SubView(t *testing.T) {
|
|||
}
|
||||
|
||||
// Check the nested key
|
||||
out, err = barrier.Get("foo/bar/test")
|
||||
bout, err = barrier.Get("foo/bar/test")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out != nil {
|
||||
if bout != nil {
|
||||
t.Fatalf("nested foo/bar/test should be gone")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -173,7 +175,7 @@ func (m *ExpirationManager) Register(req *Request, resp *Response) (string, erro
|
|||
}
|
||||
|
||||
// Write out to the view
|
||||
ent := Entry{
|
||||
ent := logical.StorageEntry{
|
||||
Key: le.VaultID,
|
||||
Value: buf,
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
// GenericBackend is used for the storing generic secrets. These are not
|
||||
|
@ -99,7 +101,7 @@ func (g *GenericBackend) handleWrite(req *Request) (*Response, error) {
|
|||
}
|
||||
|
||||
// Write out a new key
|
||||
entry := &Entry{
|
||||
entry := &logical.StorageEntry{
|
||||
Key: req.Path,
|
||||
Value: buf,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue