Sync over
This commit is contained in:
parent
aaa1094240
commit
c144f95be0
|
@ -33,6 +33,28 @@ func (r ReplicationState) String() string {
|
|||
return "disabled"
|
||||
}
|
||||
|
||||
func (r ReplicationState) GetDRString() string {
|
||||
switch {
|
||||
case r.HasState(ReplicationDRPrimary):
|
||||
return ReplicationDRPrimary.String()
|
||||
case r.HasState(ReplicationDRSecondary):
|
||||
return ReplicationDRSecondary.String()
|
||||
default:
|
||||
return ReplicationDisabled.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (r ReplicationState) GetPerformanceString() string {
|
||||
switch {
|
||||
case r.HasState(ReplicationPerformancePrimary):
|
||||
return ReplicationPerformancePrimary.String()
|
||||
case r.HasState(ReplicationPerformanceSecondary):
|
||||
return ReplicationPerformanceSecondary.String()
|
||||
default:
|
||||
return ReplicationDisabled.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (r ReplicationState) HasState(flag ReplicationState) bool { return r&flag != 0 }
|
||||
func (r *ReplicationState) AddState(flag ReplicationState) { *r |= flag }
|
||||
func (r *ReplicationState) ClearState(flag ReplicationState) { *r &= ^flag }
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package identity
|
||||
|
||||
import "github.com/golang/protobuf/ptypes"
|
||||
|
||||
func (e *Entity) SentinelGet(key string) (interface{}, error) {
|
||||
if e == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch key {
|
||||
case "aliases":
|
||||
return e.Aliases, nil
|
||||
case "id":
|
||||
return e.ID, nil
|
||||
case "meta", "metadata":
|
||||
return e.Metadata, nil
|
||||
case "name":
|
||||
return e.Name, nil
|
||||
case "creation_time":
|
||||
return ptypes.TimestampString(e.CreationTime), nil
|
||||
case "last_update_time":
|
||||
return ptypes.TimestampString(e.LastUpdateTime), nil
|
||||
case "merged_entity_ids":
|
||||
return e.MergedEntityIDs, nil
|
||||
case "policies":
|
||||
return e.Policies, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Alias) SentinelGet(key string) (interface{}, error) {
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch key {
|
||||
case "id":
|
||||
return p.ID, nil
|
||||
case "mount_type":
|
||||
return p.MountType, nil
|
||||
case "mount_accessor":
|
||||
return p.MountAccessor, nil
|
||||
case "mount_path":
|
||||
return p.MountPath, nil
|
||||
case "meta", "metadata":
|
||||
return p.Metadata, nil
|
||||
case "name":
|
||||
return p.Name, nil
|
||||
case "creation_time":
|
||||
return ptypes.TimestampString(p.CreationTime), nil
|
||||
case "last_update_time":
|
||||
return ptypes.TimestampString(p.LastUpdateTime), nil
|
||||
case "merged_from_entity_ids":
|
||||
return p.MergedFromEntityIDs, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *Group) SentinelGet(key string) (interface{}, error) {
|
||||
if g == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch key {
|
||||
case "id":
|
||||
return g.ID, nil
|
||||
case "name":
|
||||
return g.Name, nil
|
||||
case "policies":
|
||||
return g.Policies, nil
|
||||
case "parent_group_ids":
|
||||
return g.ParentGroupIDs, nil
|
||||
case "member_entity_ids":
|
||||
return g.MemberEntityIDs, nil
|
||||
case "meta", "metadata":
|
||||
return g.Metadata, nil
|
||||
case "creation_time":
|
||||
return ptypes.TimestampString(g.CreationTime), nil
|
||||
case "last_update_time":
|
||||
return ptypes.TimestampString(g.LastUpdateTime), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -22,7 +22,7 @@ func pathDuoConfig() *framework.Path {
|
|||
},
|
||||
"push_info": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "A string of URL-encoded key/value pairs that provides additional context about the authentication attemmpt in the Duo Mobile app",
|
||||
Description: "A string of URL-encoded key/value pairs that provides additional context about the authentication attempt in the Duo Mobile app",
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ var _ = math.Inf
|
|||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Item struct {
|
||||
ID string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Message *google_protobuf.Any `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
||||
ID string `sentinel:"" protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Message *google_protobuf.Any `sentinel:"" protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Item) Reset() { *m = Item{} }
|
||||
|
@ -54,8 +54,8 @@ func (m *Item) GetMessage() *google_protobuf.Any {
|
|||
}
|
||||
|
||||
type Bucket struct {
|
||||
Key string `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
|
||||
Items []*Item `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"`
|
||||
Key string `sentinel:"" protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
|
||||
Items []*Item `sentinel:"" protobuf:"bytes,2,rep,name=items" json:"items,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Bucket) Reset() { *m = Bucket{} }
|
||||
|
|
|
@ -11,5 +11,5 @@ type Connection struct {
|
|||
RemoteAddr string `json:"remote_addr"`
|
||||
|
||||
// ConnState is the TLS connection state if applicable.
|
||||
ConnState *tls.ConnectionState
|
||||
ConnState *tls.ConnectionState `sentinel:""`
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@ const (
|
|||
|
||||
// TypeSlice represents a slice of any type
|
||||
TypeSlice
|
||||
|
||||
// TypeStringSlice is a helper for TypeSlice that returns a sanitized
|
||||
// slice of strings
|
||||
TypeStringSlice
|
||||
|
||||
// TypeCommaStringSlice is a helper for TypeSlice that returns a sanitized
|
||||
// slice of strings and also supports parsing a comma-separated list in
|
||||
// a string field
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package logical
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// LeaseOptions is an embeddable struct to capture common lease
|
||||
// settings between a Secret and Auth
|
||||
|
|
|
@ -117,4 +117,9 @@ type Paths struct {
|
|||
// LocalStorage are paths (prefixes) that are local to this instance; this
|
||||
// indicates that these paths should not be replicated
|
||||
LocalStorage []string
|
||||
|
||||
// SealWrapStorage are storage paths that, when using a capable seal,
|
||||
// should be seal wrapped with extra encryption. It is exact matching
|
||||
// unless it ends with '/' in which case it will be treated as a prefix.
|
||||
SealWrapStorage []string
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ func (b *backend) pathInternalUpdate(
|
|||
b.internal = value
|
||||
// Return the secret
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
func (b *backend) pathInternalRead(
|
||||
|
@ -37,5 +36,4 @@ func (b *backend) pathInternalRead(
|
|||
"value": b.internal,
|
||||
},
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package logical
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -11,23 +12,41 @@ import (
|
|||
type RequestWrapInfo struct {
|
||||
// Setting to non-zero specifies that the response should be wrapped.
|
||||
// Specifies the desired TTL of the wrapping token.
|
||||
TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"`
|
||||
TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl" sentinel:""`
|
||||
|
||||
// The format to use for the wrapped response; if not specified it's a bare
|
||||
// token
|
||||
Format string `json:"format" structs:"format" mapstructure:"format"`
|
||||
Format string `json:"format" structs:"format" mapstructure:"format" sentinel:""`
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (r *RequestWrapInfo) SentinelGet(key string) (interface{}, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch key {
|
||||
case "ttl":
|
||||
return r.TTL, nil
|
||||
case "ttl_seconds":
|
||||
return int64(r.TTL.Seconds()), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Note: Many of these have Sentinel disabled because they are values populated
|
||||
// by the router after policy checks; the token namespace would be the right
|
||||
// place to access them via Sentinel
|
||||
type Request struct {
|
||||
// Id is the uuid associated with each request
|
||||
ID string `json:"id" structs:"id" mapstructure:"id"`
|
||||
ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""`
|
||||
|
||||
// If set, the name given to the replication secondary where this request
|
||||
// originated
|
||||
ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster"`
|
||||
ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster" sentinel:""`
|
||||
|
||||
// Operation is the requested operation type
|
||||
Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"`
|
||||
|
@ -36,26 +55,26 @@ type Request struct {
|
|||
// 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 `json:"path" structs:"path" mapstructure:"path"`
|
||||
Path string `json:"path" structs:"path" mapstructure:"path" sentinel:""`
|
||||
|
||||
// Request data is an opaque map that must have string keys.
|
||||
Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"`
|
||||
|
||||
// Storage can be used to durably store and retrieve state.
|
||||
Storage Storage `json:"-"`
|
||||
Storage Storage `json:"-" sentinel:""`
|
||||
|
||||
// Secret will be non-nil only for Revoke and Renew operations
|
||||
// to represent the secret that was returned prior.
|
||||
Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"`
|
||||
Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret" sentinel:""`
|
||||
|
||||
// Auth will be non-nil only for Renew operations
|
||||
// to represent the auth that was returned prior.
|
||||
Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"`
|
||||
Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth" sentinel:""`
|
||||
|
||||
// Headers will contain the http headers from the request. This value will
|
||||
// be used in the audit broker to ensure we are auditing only the allowed
|
||||
// headers.
|
||||
Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers"`
|
||||
Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers" sentinel:""`
|
||||
|
||||
// Connection will be non-nil only for credential providers to
|
||||
// inspect the connection information and potentially use it for
|
||||
|
@ -66,45 +85,58 @@ type Request struct {
|
|||
// can be verified and ACLs applied. This value is passed
|
||||
// through to the logical backends but after being salted and
|
||||
// hashed.
|
||||
ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token"`
|
||||
ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""`
|
||||
|
||||
// ClientTokenAccessor is provided to the core so that the it can get
|
||||
// logged as part of request audit logging.
|
||||
ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor"`
|
||||
ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""`
|
||||
|
||||
// DisplayName is provided to the logical backend to help associate
|
||||
// dynamic secrets with the source entity. This is not a sensitive
|
||||
// name, but is useful for operators.
|
||||
DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name"`
|
||||
DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name" sentinel:""`
|
||||
|
||||
// MountPoint is provided so that a logical backend can generate
|
||||
// paths relative to itself. The `Path` is effectively the client
|
||||
// request path with the MountPoint trimmed off.
|
||||
MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point"`
|
||||
MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point" sentinel:""`
|
||||
|
||||
// MountType is provided so that a logical backend can make decisions
|
||||
// based on the specific mount type (e.g., if a mount type has different
|
||||
// aliases, generating different defaults depending on the alias)
|
||||
MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type"`
|
||||
MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type" sentinel:""`
|
||||
|
||||
// MountAccessor is provided so that identities returned by the authentication
|
||||
// backends can be tied to the mount it belongs to.
|
||||
MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor"`
|
||||
MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" sentinel:""`
|
||||
|
||||
// WrapInfo contains requested response wrapping parameters
|
||||
WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"`
|
||||
WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info" sentinel:""`
|
||||
|
||||
// ClientTokenRemainingUses represents the allowed number of uses left on the
|
||||
// token supplied
|
||||
ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"`
|
||||
|
||||
// MFACreds holds the parsed MFA information supplied over the API as part of
|
||||
// X-Vault-MFA header
|
||||
MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""`
|
||||
|
||||
// EntityID is the identity of the caller extracted out of the token used
|
||||
// to make this request
|
||||
EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id"`
|
||||
EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id" sentinel:""`
|
||||
|
||||
// PolicyOverride indicates that the requestor wishes to override
|
||||
// soft-mandatory Sentinel policies
|
||||
PolicyOverride bool `json:"policy_override" structs:"policy_override" mapstructure:"policy_override"`
|
||||
|
||||
// Whether the request is unauthenticated, as in, had no client token
|
||||
// attached. Useful in some situations where the client token is not made
|
||||
// accessible.
|
||||
Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"`
|
||||
|
||||
// For replication, contains the last WAL on the remote side after handling
|
||||
// the request, used for best-effort avoidance of stale read-after-write
|
||||
lastRemoteWAL uint64
|
||||
lastRemoteWAL uint64 `sentinel:""`
|
||||
}
|
||||
|
||||
// Get returns a data field and guards for nil Data
|
||||
|
@ -126,6 +158,24 @@ func (r *Request) GoString() string {
|
|||
return fmt.Sprintf("*%#v", *r)
|
||||
}
|
||||
|
||||
func (r *Request) SentinelGet(key string) (interface{}, error) {
|
||||
switch key {
|
||||
case "path":
|
||||
// Sanitize it here so that it's consistent in policies
|
||||
return strings.TrimPrefix(r.Path, "/"), nil
|
||||
|
||||
case "wrapping", "wrap_info":
|
||||
// If the pointer is nil accessing the wrap info is considered
|
||||
// "undefined" so this allows us to instead discover a TTL of zero
|
||||
if r.WrapInfo == nil {
|
||||
return &RequestWrapInfo{}, nil
|
||||
}
|
||||
return r.WrapInfo, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Request) LastRemoteWAL() uint64 {
|
||||
return r.lastRemoteWAL
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ type Secret struct {
|
|||
// InternalData is JSON-encodable data that is stored with the secret.
|
||||
// This will be sent back during a Renew/Revoke for storing internal data
|
||||
// used for those operations.
|
||||
InternalData map[string]interface{} `json:"internal_data"`
|
||||
InternalData map[string]interface{} `json:"internal_data" sentinel:""`
|
||||
|
||||
// LeaseID is the ID returned to the user to manage this secret.
|
||||
// This is generated by Vault core. Any set value will be ignored.
|
||||
// For requests, this will always be blank.
|
||||
LeaseID string
|
||||
LeaseID string `sentinel:""`
|
||||
}
|
||||
|
||||
func (s *Secret) Validate() error {
|
||||
|
|
|
@ -23,8 +23,9 @@ type Storage interface {
|
|||
|
||||
// StorageEntry is the entry for an item in a Storage implementation.
|
||||
type StorageEntry struct {
|
||||
Key string
|
||||
Value []byte
|
||||
Key string
|
||||
Value []byte
|
||||
SealWrap bool
|
||||
}
|
||||
|
||||
// DecodeJSON decodes the 'Value' present in StorageEntry.
|
||||
|
@ -94,6 +95,10 @@ func CollectKeys(view ClearableView) ([]string, error) {
|
|||
|
||||
// ClearView is used to delete all the keys in a view
|
||||
func ClearView(view ClearableView) error {
|
||||
if view == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect all the keys
|
||||
keys, err := CollectKeys(view)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
)
|
||||
|
||||
// This logic was pulled from the http package so that it can be used for
|
||||
// encoding wrapped responses as well. It simply translates the logical request
|
||||
// to an http response, with the values we want and omitting the values we
|
||||
// don't.
|
||||
// encoding wrapped responses as well. It simply translates the logical
|
||||
// response to an http response, with the values we want and omitting the
|
||||
// values we don't.
|
||||
func LogicalResponseToHTTPResponse(input *Response) *HTTPResponse {
|
||||
httpResp := &HTTPResponse{
|
||||
Data: input.Data,
|
||||
|
|
39
meta/meta.go
39
meta/meta.go
|
@ -4,10 +4,12 @@ import (
|
|||
"bufio"
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/command/token"
|
||||
"github.com/hashicorp/vault/helper/flag-slice"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
|
@ -34,6 +36,9 @@ var (
|
|||
"s", "m", or "h"; if no suffix is specified it will
|
||||
be parsed as seconds. May also be specified via
|
||||
VAULT_WRAP_TTL.
|
||||
|
||||
-policy-override Indicates that any soft-mandatory Sentinel policies
|
||||
be overridden.
|
||||
`
|
||||
}
|
||||
)
|
||||
|
@ -48,13 +53,15 @@ type Meta struct {
|
|||
ForceAddress string // Address to force for API clients
|
||||
|
||||
// These are set by the command line flags.
|
||||
flagAddress string
|
||||
flagCACert string
|
||||
flagCAPath string
|
||||
flagClientCert string
|
||||
flagClientKey string
|
||||
flagWrapTTL string
|
||||
flagInsecure bool
|
||||
flagAddress string
|
||||
flagCACert string
|
||||
flagCAPath string
|
||||
flagClientCert string
|
||||
flagClientKey string
|
||||
flagWrapTTL string
|
||||
flagInsecure bool
|
||||
flagMFA []string
|
||||
flagPolicyOverride bool
|
||||
|
||||
// Queried if no token can be found
|
||||
TokenHelper TokenHelperFunc
|
||||
|
@ -105,6 +112,22 @@ func (m *Meta) Client() (*api.Client, error) {
|
|||
|
||||
client.SetWrappingLookupFunc(m.DefaultWrappingLookupFunc)
|
||||
|
||||
var mfaCreds []string
|
||||
|
||||
// Extract the MFA credentials from environment variable first
|
||||
if os.Getenv(api.EnvVaultMFA) != "" {
|
||||
mfaCreds = []string{os.Getenv(api.EnvVaultMFA)}
|
||||
}
|
||||
|
||||
// If CLI MFA flags were supplied, prefer that over environment variable
|
||||
if len(m.flagMFA) != 0 {
|
||||
mfaCreds = m.flagMFA
|
||||
}
|
||||
|
||||
client.SetMFACreds(mfaCreds)
|
||||
|
||||
client.SetPolicyOverride(m.flagPolicyOverride)
|
||||
|
||||
// If we have a token directly, then set that
|
||||
token := m.ClientToken
|
||||
|
||||
|
@ -154,6 +177,8 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
|
|||
f.StringVar(&m.flagWrapTTL, "wrap-ttl", "", "")
|
||||
f.BoolVar(&m.flagInsecure, "insecure", false, "")
|
||||
f.BoolVar(&m.flagInsecure, "tls-skip-verify", false, "")
|
||||
f.BoolVar(&m.flagPolicyOverride, "policy-override", false, "")
|
||||
f.Var((*sliceflag.StringFlag)(&m.flagMFA), "mfa", "")
|
||||
}
|
||||
|
||||
// Create an io.Writer that writes to our Ui properly for errors.
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestFlagSet(t *testing.T) {
|
|||
},
|
||||
{
|
||||
FlagSetServer,
|
||||
[]string{"address", "ca-cert", "ca-path", "client-cert", "client-key", "insecure", "tls-skip-verify", "wrap-ttl"},
|
||||
[]string{"address", "ca-cert", "ca-path", "client-cert", "client-key", "insecure", "mfa", "policy-override", "tls-skip-verify", "wrap-ttl"},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ func (c *Cache) List(prefix string) ([]string, error) {
|
|||
return c.backend.List(prefix)
|
||||
}
|
||||
|
||||
func (c *TransactionalCache) Transaction(txns []TxnEntry) error {
|
||||
func (c *TransactionalCache) Transaction(txns []*TxnEntry) error {
|
||||
// Lock the world
|
||||
for _, lock := range c.locks {
|
||||
lock.Lock()
|
||||
|
|
|
@ -196,7 +196,7 @@ func (c *CockroachDBBackend) List(prefix string) ([]string, error) {
|
|||
}
|
||||
|
||||
// Transaction is used to run multiple entries via a transaction
|
||||
func (c *CockroachDBBackend) Transaction(txns []physical.TxnEntry) error {
|
||||
func (c *CockroachDBBackend) Transaction(txns []*physical.TxnEntry) error {
|
||||
defer metrics.MeasureSince([]string{"cockroachdb", "transaction"}, time.Now())
|
||||
if len(txns) == 0 {
|
||||
return nil
|
||||
|
@ -210,7 +210,7 @@ func (c *CockroachDBBackend) Transaction(txns []physical.TxnEntry) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (c *CockroachDBBackend) transaction(tx *sql.Tx, txns []physical.TxnEntry) error {
|
||||
func (c *CockroachDBBackend) transaction(tx *sql.Tx, txns []*physical.TxnEntry) error {
|
||||
deleteStmt, err := tx.Prepare(c.rawStatements["delete"])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -303,7 +303,7 @@ func setupTLSConfig(conf map[string]string) (*tls.Config, error) {
|
|||
}
|
||||
|
||||
// Used to run multiple entries via a transaction
|
||||
func (c *ConsulBackend) Transaction(txns []physical.TxnEntry) error {
|
||||
func (c *ConsulBackend) Transaction(txns []*physical.TxnEntry) error {
|
||||
if len(txns) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -334,7 +334,7 @@ func (c *ConsulBackend) Transaction(txns []physical.TxnEntry) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
if ok && len(resp.Errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -273,7 +273,7 @@ func (b *FileBackend) validatePath(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *TransactionalFileBackend) Transaction(txns []physical.TxnEntry) error {
|
||||
func (b *TransactionalFileBackend) Transaction(txns []*physical.TxnEntry) error {
|
||||
b.permitPool.Acquire()
|
||||
defer b.permitPool.Release()
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ func (i *InmemBackend) ListInternal(prefix string) ([]string, error) {
|
|||
}
|
||||
|
||||
// Implements the transaction interface
|
||||
func (t *TransactionalInmemBackend) Transaction(txns []physical.TxnEntry) error {
|
||||
func (t *TransactionalInmemBackend) Transaction(txns []*physical.TxnEntry) error {
|
||||
t.permitPool.Acquire()
|
||||
defer t.permitPool.Release()
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ func (f *faultyPseudo) List(prefix string) ([]string, error) {
|
|||
return f.underlying.List(prefix)
|
||||
}
|
||||
|
||||
func (f *faultyPseudo) Transaction(txns []physical.TxnEntry) error {
|
||||
func (f *faultyPseudo) Transaction(txns []*physical.TxnEntry) error {
|
||||
f.underlying.permitPool.Acquire()
|
||||
defer f.underlying.permitPool.Release()
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ func (l *LatencyInjector) List(prefix string) ([]string, error) {
|
|||
}
|
||||
|
||||
// Transaction is a latent transaction request
|
||||
func (l *TransactionalLatencyInjector) Transaction(txns []TxnEntry) error {
|
||||
func (l *TransactionalLatencyInjector) Transaction(txns []*TxnEntry) error {
|
||||
l.addLatency()
|
||||
return l.Transactional.Transaction(txns)
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ type Lock interface {
|
|||
type Entry struct {
|
||||
Key string
|
||||
Value []byte
|
||||
SealWrap bool `json:"seal_wrap,omitempty"`
|
||||
}
|
||||
|
||||
// Factory is the factory function to create a physical backend.
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package physical
|
||||
|
||||
// PhysicalAccess is a wrapper around physical.Backend that allows Core to
|
||||
// expose its physical storage operations through PhysicalAccess() while
|
||||
// restricting the ability to modify Core.physical itself.
|
||||
type PhysicalAccess struct {
|
||||
physical Backend
|
||||
}
|
||||
|
||||
var _ Backend = (*PhysicalAccess)(nil)
|
||||
|
||||
func NewPhysicalAccess(physical Backend) *PhysicalAccess {
|
||||
return &PhysicalAccess{physical: physical}
|
||||
}
|
||||
|
||||
func (p *PhysicalAccess) Put(entry *Entry) error {
|
||||
return p.physical.Put(entry)
|
||||
}
|
||||
|
||||
func (p *PhysicalAccess) Get(key string) (*Entry, error) {
|
||||
return p.physical.Get(key)
|
||||
}
|
||||
|
||||
func (p *PhysicalAccess) Delete(key string) error {
|
||||
return p.physical.Delete(key)
|
||||
}
|
||||
|
||||
func (p *PhysicalAccess) List(prefix string) ([]string, error) {
|
||||
return p.physical.List(prefix)
|
||||
}
|
||||
|
||||
func (p *PhysicalAccess) Purge() {
|
||||
if purgeable, ok := p.physical.(Purgable); ok {
|
||||
purgeable.Purge()
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func ExerciseBackend(t *testing.T, b Backend) {
|
||||
t.Helper()
|
||||
// Should be empty
|
||||
keys, err := b.List("")
|
||||
if err != nil {
|
||||
|
@ -196,6 +197,7 @@ func ExerciseBackend(t *testing.T, b Backend) {
|
|||
}
|
||||
|
||||
func ExerciseBackend_ListPrefix(t *testing.T, b Backend) {
|
||||
t.Helper()
|
||||
e1 := &Entry{Key: "foo", Value: []byte("test")}
|
||||
e2 := &Entry{Key: "foo/bar", Value: []byte("test")}
|
||||
e3 := &Entry{Key: "foo/bar/baz", Value: []byte("test")}
|
||||
|
@ -266,6 +268,7 @@ func ExerciseBackend_ListPrefix(t *testing.T, b Backend) {
|
|||
}
|
||||
|
||||
func ExerciseHABackend(t *testing.T, b HABackend, b2 HABackend) {
|
||||
t.Helper()
|
||||
// Get the lock
|
||||
lock, err := b.LockWith("foo", "bar")
|
||||
if err != nil {
|
||||
|
@ -342,6 +345,7 @@ func ExerciseHABackend(t *testing.T, b HABackend, b2 HABackend) {
|
|||
}
|
||||
|
||||
func ExerciseTransactionalBackend(t *testing.T, b Backend) {
|
||||
t.Helper()
|
||||
tb, ok := b.(Transactional)
|
||||
if !ok {
|
||||
t.Fatal("Not a transactional backend")
|
||||
|
@ -395,7 +399,8 @@ func ExerciseTransactionalBackend(t *testing.T, b Backend) {
|
|||
}
|
||||
}
|
||||
|
||||
func SetupTestingTransactions(t *testing.T, b Backend) []TxnEntry {
|
||||
func SetupTestingTransactions(t *testing.T, b Backend) []*TxnEntry {
|
||||
t.Helper()
|
||||
// Add a few keys so that we test rollback with deletion
|
||||
if err := b.Put(&Entry{
|
||||
Key: "foo",
|
||||
|
@ -420,34 +425,34 @@ func SetupTestingTransactions(t *testing.T, b Backend) []TxnEntry {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
txns := []TxnEntry{
|
||||
TxnEntry{
|
||||
txns := []*TxnEntry{
|
||||
&TxnEntry{
|
||||
Operation: PutOperation,
|
||||
Entry: &Entry{
|
||||
Key: "foo",
|
||||
Value: []byte("bar2"),
|
||||
},
|
||||
},
|
||||
TxnEntry{
|
||||
&TxnEntry{
|
||||
Operation: DeleteOperation,
|
||||
Entry: &Entry{
|
||||
Key: "deleteme",
|
||||
},
|
||||
},
|
||||
TxnEntry{
|
||||
&TxnEntry{
|
||||
Operation: PutOperation,
|
||||
Entry: &Entry{
|
||||
Key: "foo",
|
||||
Value: []byte("bar3"),
|
||||
},
|
||||
},
|
||||
TxnEntry{
|
||||
&TxnEntry{
|
||||
Operation: DeleteOperation,
|
||||
Entry: &Entry{
|
||||
Key: "deleteme2",
|
||||
},
|
||||
},
|
||||
TxnEntry{
|
||||
&TxnEntry{
|
||||
Operation: PutOperation,
|
||||
Entry: &Entry{
|
||||
Key: "zip",
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package physical
|
||||
|
||||
import multierror "github.com/hashicorp/go-multierror"
|
||||
import (
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
// TxnEntry is an operation that takes atomically as part of
|
||||
// a transactional update. Only supported by Transactional backends.
|
||||
|
@ -14,7 +16,7 @@ type TxnEntry struct {
|
|||
// required for some features such as replication.
|
||||
type Transactional interface {
|
||||
// The function to run a transaction
|
||||
Transaction([]TxnEntry) error
|
||||
Transaction([]*TxnEntry) error
|
||||
}
|
||||
|
||||
type PseudoTransactional interface {
|
||||
|
@ -27,8 +29,8 @@ type PseudoTransactional interface {
|
|||
}
|
||||
|
||||
// Implements the transaction interface
|
||||
func GenericTransactionHandler(t PseudoTransactional, txns []TxnEntry) (retErr error) {
|
||||
rollbackStack := make([]TxnEntry, 0, len(txns))
|
||||
func GenericTransactionHandler(t PseudoTransactional, txns []*TxnEntry) (retErr error) {
|
||||
rollbackStack := make([]*TxnEntry, 0, len(txns))
|
||||
var dirty bool
|
||||
|
||||
// We walk the transactions in order; each successful operation goes into a
|
||||
|
@ -47,11 +49,12 @@ TxnWalk:
|
|||
// Nothing to delete or roll back
|
||||
continue
|
||||
}
|
||||
rollbackEntry := TxnEntry{
|
||||
rollbackEntry := &TxnEntry{
|
||||
Operation: PutOperation,
|
||||
Entry: &Entry{
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
SealWrap: entry.SealWrap,
|
||||
},
|
||||
}
|
||||
err = t.DeleteInternal(txn.Entry.Key)
|
||||
|
@ -60,7 +63,7 @@ TxnWalk:
|
|||
dirty = true
|
||||
break TxnWalk
|
||||
}
|
||||
rollbackStack = append([]TxnEntry{rollbackEntry}, rollbackStack...)
|
||||
rollbackStack = append([]*TxnEntry{rollbackEntry}, rollbackStack...)
|
||||
|
||||
case PutOperation:
|
||||
entry, err := t.GetInternal(txn.Entry.Key)
|
||||
|
@ -70,30 +73,32 @@ TxnWalk:
|
|||
break TxnWalk
|
||||
}
|
||||
// Nothing existed so in fact rolling back requires a delete
|
||||
var rollbackEntry TxnEntry
|
||||
var rollbackEntry *TxnEntry
|
||||
if entry == nil {
|
||||
rollbackEntry = TxnEntry{
|
||||
rollbackEntry = &TxnEntry{
|
||||
Operation: DeleteOperation,
|
||||
Entry: &Entry{
|
||||
Key: txn.Entry.Key,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
rollbackEntry = TxnEntry{
|
||||
rollbackEntry = &TxnEntry{
|
||||
Operation: PutOperation,
|
||||
Entry: &Entry{
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
SealWrap: entry.SealWrap,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
err = t.PutInternal(txn.Entry)
|
||||
if err != nil {
|
||||
retErr = multierror.Append(retErr, err)
|
||||
dirty = true
|
||||
break TxnWalk
|
||||
}
|
||||
rollbackStack = append([]TxnEntry{rollbackEntry}, rollbackStack...)
|
||||
rollbackStack = append([]*TxnEntry{rollbackEntry}, rollbackStack...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
116
vault/acl.go
116
vault/acl.go
|
@ -23,6 +23,25 @@ type ACL struct {
|
|||
root bool
|
||||
}
|
||||
|
||||
type PolicyCheckOpts struct {
|
||||
RootPrivsRequired bool
|
||||
Unauth bool
|
||||
}
|
||||
|
||||
type AuthResults struct {
|
||||
ACLResults *ACLResults
|
||||
Allowed bool
|
||||
RootPrivs bool
|
||||
Error *multierror.Error
|
||||
}
|
||||
|
||||
type ACLResults struct {
|
||||
Allowed bool
|
||||
RootPrivs bool
|
||||
IsRoot bool
|
||||
MFAMethods []string
|
||||
}
|
||||
|
||||
// New is used to construct a policy based ACL from a set of policies.
|
||||
func NewACL(policies []*Policy) (*ACL, error) {
|
||||
// Initialize
|
||||
|
@ -38,6 +57,13 @@ func NewACL(policies []*Policy) (*ACL, error) {
|
|||
if policy == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch policy.Type {
|
||||
case PolicyTypeACL:
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to parse policy (wrong type)")
|
||||
}
|
||||
|
||||
// Check if this is root
|
||||
if policy.Name == "root" {
|
||||
a.root = true
|
||||
|
@ -61,7 +87,7 @@ func NewACL(policies []*Policy) (*ACL, error) {
|
|||
}
|
||||
|
||||
// these are the ones already in the tree
|
||||
existingPerms := raw.(*Permissions)
|
||||
existingPerms := raw.(*ACLPermissions)
|
||||
|
||||
switch {
|
||||
case existingPerms.CapabilitiesBitmap&DenyCapabilityInt > 0:
|
||||
|
@ -142,7 +168,6 @@ func NewACL(policies []*Policy) (*ACL, error) {
|
|||
|
||||
INSERT:
|
||||
tree.Insert(pc.Prefix, existingPerms)
|
||||
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
|
@ -159,7 +184,7 @@ func (a *ACL) Capabilities(path string) (pathCapabilities []string) {
|
|||
raw, ok := a.exactRules.Get(path)
|
||||
|
||||
if ok {
|
||||
perm := raw.(*Permissions)
|
||||
perm := raw.(*ACLPermissions)
|
||||
capabilities = perm.CapabilitiesBitmap
|
||||
goto CHECK
|
||||
}
|
||||
|
@ -169,7 +194,7 @@ func (a *ACL) Capabilities(path string) (pathCapabilities []string) {
|
|||
if !ok {
|
||||
return []string{DenyCapability}
|
||||
} else {
|
||||
perm := raw.(*Permissions)
|
||||
perm := raw.(*ACLPermissions)
|
||||
capabilities = perm.CapabilitiesBitmap
|
||||
}
|
||||
|
||||
|
@ -201,29 +226,33 @@ CHECK:
|
|||
return
|
||||
}
|
||||
|
||||
// AllowOperation is used to check if the given operation is permitted. The
|
||||
// first bool indicates if an op is allowed, the second whether sudo priviliges
|
||||
// exist for that op and path.
|
||||
func (a *ACL) AllowOperation(req *logical.Request) (bool, bool) {
|
||||
// AllowOperation is used to check if the given operation is permitted.
|
||||
func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) {
|
||||
ret = new(ACLResults)
|
||||
|
||||
// Fast-path root
|
||||
if a.root {
|
||||
return true, true
|
||||
ret.Allowed = true
|
||||
ret.RootPrivs = true
|
||||
ret.IsRoot = true
|
||||
return
|
||||
}
|
||||
op := req.Operation
|
||||
path := req.Path
|
||||
|
||||
// Help is always allowed
|
||||
if op == logical.HelpOperation {
|
||||
return true, false
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
|
||||
var permissions *Permissions
|
||||
var permissions *ACLPermissions
|
||||
|
||||
// Find an exact matching rule, look for glob if no match
|
||||
var capabilities uint32
|
||||
raw, ok := a.exactRules.Get(path)
|
||||
if ok {
|
||||
permissions = raw.(*Permissions)
|
||||
permissions = raw.(*ACLPermissions)
|
||||
capabilities = permissions.CapabilitiesBitmap
|
||||
goto CHECK
|
||||
}
|
||||
|
@ -231,9 +260,9 @@ func (a *ACL) AllowOperation(req *logical.Request) (bool, bool) {
|
|||
// Find a glob rule, default deny if no match
|
||||
_, raw, ok = a.globRules.LongestPrefix(path)
|
||||
if !ok {
|
||||
return false, false
|
||||
return
|
||||
} else {
|
||||
permissions = raw.(*Permissions)
|
||||
permissions = raw.(*ACLPermissions)
|
||||
capabilities = permissions.CapabilitiesBitmap
|
||||
}
|
||||
|
||||
|
@ -241,7 +270,8 @@ CHECK:
|
|||
// Check if the minimum permissions are met
|
||||
// If "deny" has been explicitly set, only deny will be in the map, so we
|
||||
// only need to check for the existence of other values
|
||||
sudo := capabilities&SudoCapabilityInt > 0
|
||||
ret.RootPrivs = capabilities&SudoCapabilityInt > 0
|
||||
|
||||
operationAllowed := false
|
||||
switch op {
|
||||
case logical.ReadOperation:
|
||||
|
@ -261,21 +291,21 @@ CHECK:
|
|||
operationAllowed = capabilities&UpdateCapabilityInt > 0
|
||||
|
||||
default:
|
||||
return false, false
|
||||
return
|
||||
}
|
||||
|
||||
if !operationAllowed {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
|
||||
if permissions.MaxWrappingTTL > 0 {
|
||||
if req.WrapInfo == nil || req.WrapInfo.TTL > permissions.MaxWrappingTTL {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
}
|
||||
if permissions.MinWrappingTTL > 0 {
|
||||
if req.WrapInfo == nil || req.WrapInfo.TTL < permissions.MinWrappingTTL {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
}
|
||||
// This situation can happen because of merging, even though in a single
|
||||
|
@ -283,7 +313,7 @@ CHECK:
|
|||
if permissions.MinWrappingTTL != 0 &&
|
||||
permissions.MaxWrappingTTL != 0 &&
|
||||
permissions.MaxWrappingTTL < permissions.MinWrappingTTL {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
|
||||
// Only check parameter permissions for operations that can modify
|
||||
|
@ -291,7 +321,8 @@ CHECK:
|
|||
if op == logical.UpdateOperation || op == logical.CreateOperation {
|
||||
// If there are no data fields, allow
|
||||
if len(req.Data) == 0 {
|
||||
return true, sudo
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
|
||||
if len(permissions.DeniedParameters) == 0 {
|
||||
|
@ -300,7 +331,7 @@ CHECK:
|
|||
|
||||
// Check if all parameters have been denied
|
||||
if _, ok := permissions.DeniedParameters["*"]; ok {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
|
||||
for parameter, value := range req.Data {
|
||||
|
@ -308,7 +339,7 @@ CHECK:
|
|||
if valueSlice, ok := permissions.DeniedParameters[strings.ToLower(parameter)]; ok {
|
||||
// If the value exists in denied values slice, deny
|
||||
if valueInParameterList(value, valueSlice) {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,30 +347,59 @@ CHECK:
|
|||
ALLOWED_PARAMETERS:
|
||||
// If we don't have any allowed parameters set, allow
|
||||
if len(permissions.AllowedParameters) == 0 {
|
||||
return true, sudo
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
|
||||
_, allowedAll := permissions.AllowedParameters["*"]
|
||||
if len(permissions.AllowedParameters) == 1 && allowedAll {
|
||||
return true, sudo
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
|
||||
for parameter, value := range req.Data {
|
||||
valueSlice, ok := permissions.AllowedParameters[strings.ToLower(parameter)]
|
||||
// Requested parameter is not in allowed list
|
||||
if !ok && !allowedAll {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
|
||||
// If the value doesn't exists in the allowed values slice,
|
||||
// deny
|
||||
if ok && !valueInParameterList(value, valueSlice) {
|
||||
return false, sudo
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, sudo
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
func (c *Core) performPolicyChecks(acl *ACL, te *TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts) (ret *AuthResults) {
|
||||
ret = new(AuthResults)
|
||||
|
||||
// First, perform normal ACL checks if requested. The only time no ACL
|
||||
// should be applied is if we are only processing EGPs against a login
|
||||
// path in which case opts.Unauth will be set.
|
||||
if acl != nil && !opts.Unauth {
|
||||
ret.ACLResults = acl.AllowOperation(req)
|
||||
ret.RootPrivs = ret.ACLResults.RootPrivs
|
||||
// Root is always allowed; skip Sentinel/MFA checks
|
||||
if ret.ACLResults.IsRoot {
|
||||
//c.logger.Warn("policy: token is root, skipping checks")
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
if !ret.ACLResults.Allowed {
|
||||
return
|
||||
}
|
||||
if !ret.RootPrivs && opts.RootPrivsRequired {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ret.Allowed = true
|
||||
return
|
||||
}
|
||||
|
||||
func valueInParameterList(v interface{}, list []interface{}) bool {
|
||||
|
|
|
@ -23,7 +23,7 @@ func TestACL_Capabilities(t *testing.T) {
|
|||
t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
|
||||
}
|
||||
|
||||
policies, err := Parse(aclPolicy)
|
||||
policies, err := ParseACLPolicy(aclPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -64,17 +64,17 @@ func TestACL_Root(t *testing.T) {
|
|||
request := new(logical.Request)
|
||||
request.Operation = logical.UpdateOperation
|
||||
request.Path = "sys/mount/foo"
|
||||
allowed, rootPrivs := acl.AllowOperation(request)
|
||||
if !rootPrivs {
|
||||
authResults := acl.AllowOperation(request)
|
||||
if !authResults.RootPrivs {
|
||||
t.Fatalf("expected root")
|
||||
}
|
||||
if !allowed {
|
||||
if !authResults.Allowed {
|
||||
t.Fatalf("expected permissions")
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_Single(t *testing.T) {
|
||||
policy, err := Parse(aclPolicy)
|
||||
policy, err := ParseACLPolicy(aclPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -89,8 +89,8 @@ func TestACL_Single(t *testing.T) {
|
|||
request := new(logical.Request)
|
||||
request.Operation = logical.ReadOperation
|
||||
request.Path = "sys/mount/foo"
|
||||
_, rootPrivs := acl.AllowOperation(request)
|
||||
if rootPrivs {
|
||||
authResults := acl.AllowOperation(request)
|
||||
if authResults.RootPrivs {
|
||||
t.Fatalf("unexpected root")
|
||||
}
|
||||
|
||||
|
@ -128,23 +128,23 @@ func TestACL_Single(t *testing.T) {
|
|||
request := new(logical.Request)
|
||||
request.Operation = tc.op
|
||||
request.Path = tc.path
|
||||
allowed, rootPrivs := acl.AllowOperation(request)
|
||||
if allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, allowed, rootPrivs)
|
||||
authResults := acl.AllowOperation(request)
|
||||
if authResults.Allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
|
||||
}
|
||||
if rootPrivs != tc.rootPrivs {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, allowed, rootPrivs)
|
||||
if authResults.RootPrivs != tc.rootPrivs {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_Layered(t *testing.T) {
|
||||
policy1, err := Parse(aclPolicy)
|
||||
policy1, err := ParseACLPolicy(aclPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
policy2, err := Parse(aclPolicy2)
|
||||
policy2, err := ParseACLPolicy(aclPolicy2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -162,8 +162,8 @@ func testLayeredACL(t *testing.T, acl *ACL) {
|
|||
request := new(logical.Request)
|
||||
request.Operation = logical.ReadOperation
|
||||
request.Path = "sys/mount/foo"
|
||||
_, rootPrivs := acl.AllowOperation(request)
|
||||
if rootPrivs {
|
||||
authResults := acl.AllowOperation(request)
|
||||
if authResults.RootPrivs {
|
||||
t.Fatalf("unexpected root")
|
||||
}
|
||||
|
||||
|
@ -206,18 +206,18 @@ func testLayeredACL(t *testing.T, acl *ACL) {
|
|||
request := new(logical.Request)
|
||||
request.Operation = tc.op
|
||||
request.Path = tc.path
|
||||
allowed, rootPrivs := acl.AllowOperation(request)
|
||||
if allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, allowed, rootPrivs)
|
||||
authResults := acl.AllowOperation(request)
|
||||
if authResults.Allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
|
||||
}
|
||||
if rootPrivs != tc.rootPrivs {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, allowed, rootPrivs)
|
||||
if authResults.RootPrivs != tc.rootPrivs {
|
||||
t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_PolicyMerge(t *testing.T) {
|
||||
policy, err := Parse(mergingPolicies)
|
||||
policy, err := ParseACLPolicy(mergingPolicies)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ func TestACL_PolicyMerge(t *testing.T) {
|
|||
t.Fatalf("Could not find acl entry for path %s", tc.path)
|
||||
}
|
||||
|
||||
p := raw.(*Permissions)
|
||||
p := raw.(*ACLPermissions)
|
||||
if !reflect.DeepEqual(tc.allowed, p.AllowedParameters) {
|
||||
t.Fatalf("Allowed paramaters did not match, Expected: %#v, Got: %#v", tc.allowed, p.AllowedParameters)
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ func TestACL_PolicyMerge(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestACL_AllowOperation(t *testing.T) {
|
||||
policy, err := Parse(permissionsPolicy)
|
||||
policy, err := ParseACLPolicy(permissionsPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -333,16 +333,16 @@ func TestACL_AllowOperation(t *testing.T) {
|
|||
}
|
||||
for _, op := range toperations {
|
||||
request.Operation = op
|
||||
allowed, _ := acl.AllowOperation(&request)
|
||||
if allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v", tc, allowed)
|
||||
authResults := acl.AllowOperation(&request)
|
||||
if authResults.Allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestACL_ValuePermissions(t *testing.T) {
|
||||
policy, err := Parse(valuePermissionsPolicy)
|
||||
policy, err := ParseACLPolicy(valuePermissionsPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -408,9 +408,9 @@ func TestACL_ValuePermissions(t *testing.T) {
|
|||
}
|
||||
for _, op := range toperations {
|
||||
request.Operation = op
|
||||
allowed, _ := acl.AllowOperation(&request)
|
||||
if allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v", tc, allowed)
|
||||
authResults := acl.AllowOperation(&request)
|
||||
if authResults.Allowed != tc.allowed {
|
||||
t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -418,7 +418,7 @@ func TestACL_ValuePermissions(t *testing.T) {
|
|||
|
||||
// NOTE: this test doesn't catch any races ATM
|
||||
func TestACL_CreationRace(t *testing.T) {
|
||||
policy, err := Parse(valuePermissionsPolicy)
|
||||
policy, err := ParseACLPolicy(valuePermissionsPolicy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
|
|
@ -95,6 +95,8 @@ func (c *Core) enableCredential(entry *MountEntry) error {
|
|||
}
|
||||
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
||||
view := NewBarrierView(c.barrier, viewPath)
|
||||
var err error
|
||||
var backend logical.Backend
|
||||
sysView := c.mountEntrySysView(entry)
|
||||
conf := make(map[string]string)
|
||||
if entry.Config.PluginName != "" {
|
||||
|
@ -102,7 +104,7 @@ func (c *Core) enableCredential(entry *MountEntry) error {
|
|||
}
|
||||
|
||||
// Create the new backend
|
||||
backend, err := c.newCredentialBackend(entry.Type, sysView, view, conf)
|
||||
backend, err = c.newCredentialBackend(entry.Type, sysView, view, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -155,7 +157,7 @@ func (c *Core) disableCredential(path string) error {
|
|||
|
||||
// Store the view for this backend
|
||||
fullPath := credentialRoutePrefix + path
|
||||
view := c.router.MatchingStorageView(fullPath)
|
||||
view := c.router.MatchingStorageByAPIPath(fullPath)
|
||||
if view == nil {
|
||||
return fmt.Errorf("no matching backend %s", fullPath)
|
||||
}
|
||||
|
@ -170,14 +172,13 @@ func (c *Core) disableCredential(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Revoke credentials from this path
|
||||
if err := c.expiration.RevokePrefix(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Call cleanup function if it exists
|
||||
backend := c.router.MatchingBackend(fullPath)
|
||||
if backend != nil {
|
||||
// Revoke credentials from this path
|
||||
if err := c.expiration.RevokePrefix(fullPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Call cleanup function if it exists
|
||||
backend.Cleanup()
|
||||
}
|
||||
|
||||
|
@ -186,11 +187,14 @@ func (c *Core) disableCredential(path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Clear the data in the view
|
||||
if view != nil {
|
||||
switch {
|
||||
case entry.Local, !c.replicationState.HasState(consts.ReplicationPerformanceSecondary):
|
||||
// Have writable storage, remove the whole thing
|
||||
if err := logical.ClearView(view); err != nil {
|
||||
c.logger.Error("core: failed to clear view for path being unmounted", "error", err, "path", path)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remove the mount table entry
|
||||
|
@ -226,6 +230,27 @@ func (c *Core) removeCredEntry(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// remountCredEntryForce takes a copy of the mount entry for the path and fully
|
||||
// unmounts and remounts the backend to pick up any changes, such as filtered
|
||||
// paths
|
||||
func (c *Core) remountCredEntryForce(path string) error {
|
||||
fullPath := credentialRoutePrefix + path
|
||||
me := c.router.MatchingMountEntry(fullPath)
|
||||
if me == nil {
|
||||
return fmt.Errorf("cannot find mount for path '%s'", path)
|
||||
}
|
||||
|
||||
me, err := me.Clone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.disableCredential(path); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.enableCredential(me)
|
||||
}
|
||||
|
||||
// taintCredEntry is used to mark an entry in the auth table as tainted
|
||||
func (c *Core) taintCredEntry(path string) error {
|
||||
c.authLock.Lock()
|
||||
|
@ -398,9 +423,9 @@ func (c *Core) persistAuth(table *MountTable, localOnly bool) error {
|
|||
// setupCredentials is invoked after we've loaded the auth table to
|
||||
// initialize the credential backends and setup the router
|
||||
func (c *Core) setupCredentials() error {
|
||||
var view *BarrierView
|
||||
var err error
|
||||
var persistNeeded bool
|
||||
var view *BarrierView
|
||||
|
||||
c.authLock.Lock()
|
||||
defer c.authLock.Unlock()
|
||||
|
@ -416,13 +441,13 @@ func (c *Core) setupCredentials() error {
|
|||
// Create a barrier view using the UUID
|
||||
viewPath := credentialBarrierPrefix + entry.UUID + "/"
|
||||
view = NewBarrierView(c.barrier, viewPath)
|
||||
// Initialize the backend
|
||||
sysView := c.mountEntrySysView(entry)
|
||||
conf := make(map[string]string)
|
||||
if entry.Config.PluginName != "" {
|
||||
conf["plugin_name"] = entry.Config.PluginName
|
||||
}
|
||||
|
||||
// Initialize the backend
|
||||
backend, err = c.newCredentialBackend(entry.Type, sysView, view, conf)
|
||||
if err != nil {
|
||||
c.logger.Error("core: failed to create credential entry", "path", entry.Path, "error", err)
|
||||
|
@ -439,8 +464,9 @@ func (c *Core) setupCredentials() error {
|
|||
}
|
||||
|
||||
// Check for the correct backend type
|
||||
if entry.Type == "plugin" && backend.Type() != logical.TypeCredential {
|
||||
return fmt.Errorf("cannot mount '%s' of type '%s' as an auth backend", entry.Config.PluginName, backend.Type())
|
||||
backendType := backend.Type()
|
||||
if entry.Type == "plugin" && backendType != logical.TypeCredential {
|
||||
return fmt.Errorf("cannot mount '%s' of type '%s' as an auth backend", entry.Config.PluginName, backendType)
|
||||
}
|
||||
|
||||
if err := backend.Initialize(); err != nil {
|
||||
|
|
|
@ -294,7 +294,7 @@ func TestCore_DisableCredential_Cleanup(t *testing.T) {
|
|||
}
|
||||
|
||||
// Store the view
|
||||
view := c.router.MatchingStorageView("auth/foo/")
|
||||
view := c.router.MatchingStorageByAPIPath("auth/foo/")
|
||||
|
||||
// Inject data
|
||||
se := &logical.StorageEntry{
|
||||
|
|
|
@ -160,15 +160,17 @@ type BarrierEncryptor interface {
|
|||
|
||||
// Entry is used to represent data stored by the security barrier
|
||||
type Entry struct {
|
||||
Key string
|
||||
Value []byte
|
||||
Key string
|
||||
Value []byte
|
||||
SealWrap bool
|
||||
}
|
||||
|
||||
// Logical turns the Entry into a logical storage entry.
|
||||
func (e *Entry) Logical() *logical.StorageEntry {
|
||||
return &logical.StorageEntry{
|
||||
Key: e.Key,
|
||||
Value: e.Value,
|
||||
Key: e.Key,
|
||||
Value: e.Value,
|
||||
SealWrap: e.SealWrap,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package vault
|
||||
|
||||
// BarrierEncryptorAccess is a wrapper around BarrierEncryptor that allows Core
|
||||
// to expose its barrier encrypt/decrypt operations through BarrierEncryptorAccess()
|
||||
// while restricting the ability to modify Core.barrier itself.
|
||||
type BarrierEncryptorAccess struct {
|
||||
barrierEncryptor BarrierEncryptor
|
||||
}
|
||||
|
||||
var _ BarrierEncryptor = (*BarrierEncryptorAccess)(nil)
|
||||
|
||||
func NewBarrierEncryptorAccess(barrierEncryptor BarrierEncryptor) *BarrierEncryptorAccess {
|
||||
return &BarrierEncryptorAccess{barrierEncryptor: barrierEncryptor}
|
||||
}
|
||||
|
||||
func (b *BarrierEncryptorAccess) Encrypt(key string, plaintext []byte) ([]byte, error) {
|
||||
return b.barrierEncryptor.Encrypt(key, plaintext)
|
||||
}
|
||||
|
||||
func (b *BarrierEncryptorAccess) Decrypt(key string, ciphertext []byte) ([]byte, error) {
|
||||
return b.barrierEncryptor.Decrypt(key, ciphertext)
|
||||
}
|
|
@ -490,8 +490,9 @@ func (b *AESGCMBarrier) CreateUpgrade(term uint32) error {
|
|||
value := b.encrypt(key, prevTerm, primary, buf)
|
||||
// Create upgrade key
|
||||
pe := &physical.Entry{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Key: key,
|
||||
Value: value,
|
||||
SealWrap: true,
|
||||
}
|
||||
return b.backend.Put(pe)
|
||||
}
|
||||
|
@ -642,8 +643,9 @@ func (b *AESGCMBarrier) Put(entry *Entry) error {
|
|||
}
|
||||
|
||||
pe := &physical.Entry{
|
||||
Key: entry.Key,
|
||||
Value: b.encrypt(entry.Key, term, primary, entry.Value),
|
||||
Key: entry.Key,
|
||||
Value: b.encrypt(entry.Key, term, primary, entry.Value),
|
||||
SealWrap: entry.SealWrap,
|
||||
}
|
||||
return b.backend.Put(pe)
|
||||
}
|
||||
|
@ -673,8 +675,9 @@ func (b *AESGCMBarrier) Get(key string) (*Entry, error) {
|
|||
|
||||
// Wrap in a logical entry
|
||||
entry := &Entry{
|
||||
Key: key,
|
||||
Value: plain,
|
||||
Key: key,
|
||||
Value: plain,
|
||||
SealWrap: pe.SealWrap,
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
|
|
@ -66,8 +66,9 @@ func (v *BarrierView) Get(key string) (*logical.StorageEntry, error) {
|
|||
}
|
||||
|
||||
return &logical.StorageEntry{
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
Key: entry.Key,
|
||||
Value: entry.Value,
|
||||
SealWrap: entry.SealWrap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -84,8 +85,9 @@ func (v *BarrierView) Put(entry *logical.StorageEntry) error {
|
|||
}
|
||||
|
||||
nested := &Entry{
|
||||
Key: expandedKey,
|
||||
Value: entry.Value,
|
||||
Key: expandedKey,
|
||||
Value: entry.Value,
|
||||
SealWrap: entry.SealWrap,
|
||||
}
|
||||
return v.barrier.Put(nested)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func (c *Core) Capabilities(token, path string) ([]string, error) {
|
|||
|
||||
var policies []*Policy
|
||||
for _, tePolicy := range te.Policies {
|
||||
policy, err := c.policyStore.GetPolicy(tePolicy)
|
||||
policy, err := c.policyStore.GetPolicy(tePolicy, PolicyTypeToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestCapabilities(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create a policy
|
||||
policy, _ := Parse(aclPolicy)
|
||||
policy, _ := ParseACLPolicy(aclPolicy)
|
||||
err = c.policyStore.SetPolicy(policy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
|
Loading…
Reference in New Issue