More porting from rep (#2388)

* More porting from rep

* Address review feedback
This commit is contained in:
Jeff Mitchell 2017-02-16 16:29:30 -05:00 committed by GitHub
parent 98c7bd6c03
commit 8acbdefdf2
29 changed files with 405 additions and 67 deletions

View File

@ -17,20 +17,10 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
} }
func Backend(conf *logical.BackendConfig) (*framework.Backend, error) { func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
// Initialize the salt
salt, err := salt.NewSalt(conf.StorageView, &salt.Config{
HashFunc: salt.SHA1Hash,
})
if err != nil {
return nil, err
}
var b backend var b backend
b.Salt = salt
b.MapAppId = &framework.PolicyMap{ b.MapAppId = &framework.PolicyMap{
PathMap: framework.PathMap{ PathMap: framework.PathMap{
Name: "app-id", Name: "app-id",
Salt: salt,
Schema: map[string]*framework.FieldSchema{ Schema: map[string]*framework.FieldSchema{
"display_name": &framework.FieldSchema{ "display_name": &framework.FieldSchema{
Type: framework.TypeString, Type: framework.TypeString,
@ -48,7 +38,6 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
b.MapUserId = &framework.PathMap{ b.MapUserId = &framework.PathMap{
Name: "user-id", Name: "user-id",
Salt: salt,
Schema: map[string]*framework.FieldSchema{ Schema: map[string]*framework.FieldSchema{
"cidr_block": &framework.FieldSchema{ "cidr_block": &framework.FieldSchema{
Type: framework.TypeString, Type: framework.TypeString,
@ -81,17 +70,11 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
), ),
AuthRenew: b.pathLoginRenew, AuthRenew: b.pathLoginRenew,
Init: b.initialize,
} }
// Since the salt is new in 0.2, we need to handle this by migrating b.view = conf.StorageView
// any existing keys to use the salt. We can deprecate this eventually,
// but for now we want a smooth upgrade experience by automatically
// upgrading to use salting.
if salt.DidGenerate() {
if err := b.upgradeToSalted(conf.StorageView); err != nil {
return nil, err
}
}
return b.Backend, nil return b.Backend, nil
} }
@ -100,10 +83,36 @@ type backend struct {
*framework.Backend *framework.Backend
Salt *salt.Salt Salt *salt.Salt
view logical.Storage
MapAppId *framework.PolicyMap MapAppId *framework.PolicyMap
MapUserId *framework.PathMap MapUserId *framework.PathMap
} }
func (b *backend) initialize() error {
salt, err := salt.NewSalt(b.view, &salt.Config{
HashFunc: salt.SHA1Hash,
})
if err != nil {
return err
}
b.Salt = salt
b.MapAppId.Salt = salt
b.MapUserId.Salt = salt
// Since the salt is new in 0.2, we need to handle this by migrating
// any existing keys to use the salt. We can deprecate this eventually,
// but for now we want a smooth upgrade experience by automatically
// upgrading to use salting.
if salt.DidGenerate() {
if err := b.upgradeToSalted(b.view); err != nil {
return err
}
}
return nil
}
// upgradeToSalted is used to upgrade the non-salted keys prior to // upgradeToSalted is used to upgrade the non-salted keys prior to
// Vault 0.2 to be salted. This is done on mount time and is only // Vault 0.2 to be salted. This is done on mount time and is only
// done once. It can be deprecated eventually, but should be around // done once. It can be deprecated eventually, but should be around

View File

@ -72,6 +72,10 @@ func TestBackend_upgradeToSalted(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
err = backend.Initialize()
if err != nil {
t.Fatalf("err: %v", err)
}
// Check the keys have been upgraded // Check the keys have been upgraded
out, err := inm.Get("struct/map/app-id/foo") out, err := inm.Get("struct/map/app-id/foo")

View File

@ -17,6 +17,9 @@ type backend struct {
// by this backend. // by this backend.
salt *salt.Salt salt *salt.Salt
// The view to use when creating the salt
view logical.Storage
// Guard to clean-up the expired SecretID entries // Guard to clean-up the expired SecretID entries
tidySecretIDCASGuard uint32 tidySecretIDCASGuard uint32
@ -57,18 +60,9 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
} }
func Backend(conf *logical.BackendConfig) (*backend, error) { func Backend(conf *logical.BackendConfig) (*backend, error) {
// Initialize the salt
salt, err := salt.NewSalt(conf.StorageView, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return nil, err
}
// Create a backend object // Create a backend object
b := &backend{ b := &backend{
// Set the salt object for the backend view: conf.StorageView,
salt: salt,
// Create the map of locks to modify the registered roles // Create the map of locks to modify the registered roles
roleLocksMap: make(map[string]*sync.RWMutex, 257), roleLocksMap: make(map[string]*sync.RWMutex, 257),
@ -83,6 +77,8 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
secretIDAccessorLocksMap: make(map[string]*sync.RWMutex, 257), secretIDAccessorLocksMap: make(map[string]*sync.RWMutex, 257),
} }
var err error
// Create 256 locks each for managing RoleID and SecretIDs. This will avoid // Create 256 locks each for managing RoleID and SecretIDs. This will avoid
// a superfluous number of locks directly proportional to the number of RoleID // a superfluous number of locks directly proportional to the number of RoleID
// and SecretIDs. These locks can be accessed by indexing based on the first two // and SecretIDs. These locks can be accessed by indexing based on the first two
@ -129,10 +125,22 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
pathTidySecretID(b), pathTidySecretID(b),
}, },
), ),
Init: b.initialize,
} }
return b, nil return b, nil
} }
func (b *backend) initialize() error {
salt, err := salt.NewSalt(b.view, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return err
}
b.salt = salt
return nil
}
// periodicFunc of the backend will be invoked once a minute by the RollbackManager. // periodicFunc of the backend will be invoked once a minute by the RollbackManager.
// RoleRole backend utilizes this function to delete expired SecretID entries. // RoleRole backend utilizes this function to delete expired SecretID entries.
// This could mean that the SecretID may live in the backend upto 1 min after its // This could mean that the SecretID may live in the backend upto 1 min after its

View File

@ -21,5 +21,9 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = b.Initialize()
if err != nil {
t.Fatal(err)
}
return b, config.StorageView return b, config.StorageView
} }

View File

@ -23,6 +23,9 @@ type backend struct {
*framework.Backend *framework.Backend
Salt *salt.Salt Salt *salt.Salt
// Used during initialization to set the salt
view logical.Storage
// Lock to make changes to any of the backend's configuration endpoints. // Lock to make changes to any of the backend's configuration endpoints.
configMutex sync.RWMutex configMutex sync.RWMutex
@ -59,18 +62,11 @@ type backend struct {
} }
func Backend(conf *logical.BackendConfig) (*backend, error) { func Backend(conf *logical.BackendConfig) (*backend, error) {
salt, err := salt.NewSalt(conf.StorageView, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return nil, err
}
b := &backend{ b := &backend{
// Setting the periodic func to be run once in an hour. // Setting the periodic func to be run once in an hour.
// If there is a real need, this can be made configurable. // If there is a real need, this can be made configurable.
tidyCooldownPeriod: time.Hour, tidyCooldownPeriod: time.Hour,
Salt: salt, view: conf.StorageView,
EC2ClientsMap: make(map[string]map[string]*ec2.EC2), EC2ClientsMap: make(map[string]map[string]*ec2.EC2),
IAMClientsMap: make(map[string]map[string]*iam.IAM), IAMClientsMap: make(map[string]map[string]*iam.IAM),
} }
@ -83,6 +79,9 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
Unauthenticated: []string{ Unauthenticated: []string{
"login", "login",
}, },
LocalStorage: []string{
"whitelist/identity/",
},
}, },
Paths: []*framework.Path{ Paths: []*framework.Path{
pathLogin(b), pathLogin(b),
@ -104,11 +103,26 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
pathIdentityWhitelist(b), pathIdentityWhitelist(b),
pathTidyIdentityWhitelist(b), pathTidyIdentityWhitelist(b),
}, },
Invalidate: b.invalidate,
Init: b.initialize,
} }
return b, nil return b, nil
} }
func (b *backend) initialize() error {
salt, err := salt.NewSalt(b.view, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return err
}
b.Salt = salt
return nil
}
// periodicFunc performs the tasks that the backend wishes to do periodically. // periodicFunc performs the tasks that the backend wishes to do periodically.
// Currently this will be triggered once in a minute by the RollbackManager. // Currently this will be triggered once in a minute by the RollbackManager.
// //
@ -169,6 +183,16 @@ func (b *backend) periodicFunc(req *logical.Request) error {
return nil return nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/client":
b.configMutex.Lock()
defer b.configMutex.Unlock()
b.flushCachedEC2Clients()
b.flushCachedIAMClients()
}
}
const backendHelp = ` const backendHelp = `
aws-ec2 auth backend takes in PKCS#7 signature of an AWS EC2 instance and a client aws-ec2 auth backend takes in PKCS#7 signature of an AWS EC2 instance and a client
created nonce to authenticates the EC2 instance with Vault. created nonce to authenticates the EC2 instance with Vault.

View File

@ -1,6 +1,7 @@
package cert package cert
import ( import (
"strings"
"sync" "sync"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
@ -13,7 +14,7 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
if err != nil { if err != nil {
return b, err return b, err
} }
return b, b.populateCRLs(conf.StorageView) return b, nil
} }
func Backend() *backend { func Backend() *backend {
@ -36,9 +37,10 @@ func Backend() *backend {
}), }),
AuthRenew: b.pathLoginRenew, AuthRenew: b.pathLoginRenew,
Invalidate: b.invalidate,
} }
b.crls = map[string]CRLInfo{}
b.crlUpdateMutex = &sync.RWMutex{} b.crlUpdateMutex = &sync.RWMutex{}
return &b return &b
@ -52,6 +54,15 @@ type backend struct {
crlUpdateMutex *sync.RWMutex crlUpdateMutex *sync.RWMutex
} }
func (b *backend) invalidate(key string) {
switch {
case strings.HasPrefix(key, "crls/"):
b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock()
b.crls = nil
}
}
const backendHelp = ` const backendHelp = `
The "cert" credential provider allows authentication using The "cert" credential provider allows authentication using
TLS client certificates. A client connects to Vault and uses TLS client certificates. A client connects to Vault and uses

View File

@ -45,6 +45,12 @@ func (b *backend) populateCRLs(storage logical.Storage) error {
b.crlUpdateMutex.Lock() b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock() defer b.crlUpdateMutex.Unlock()
if b.crls != nil {
return nil
}
b.crls = map[string]CRLInfo{}
keys, err := storage.List("crls/") keys, err := storage.List("crls/")
if err != nil { if err != nil {
return fmt.Errorf("error listing CRLs: %v", err) return fmt.Errorf("error listing CRLs: %v", err)
@ -56,6 +62,7 @@ func (b *backend) populateCRLs(storage logical.Storage) error {
for _, key := range keys { for _, key := range keys {
entry, err := storage.Get("crls/" + key) entry, err := storage.Get("crls/" + key)
if err != nil { if err != nil {
b.crls = nil
return fmt.Errorf("error loading CRL %s: %v", key, err) return fmt.Errorf("error loading CRL %s: %v", key, err)
} }
if entry == nil { if entry == nil {
@ -64,6 +71,7 @@ func (b *backend) populateCRLs(storage logical.Storage) error {
var crlInfo CRLInfo var crlInfo CRLInfo
err = entry.DecodeJSON(&crlInfo) err = entry.DecodeJSON(&crlInfo)
if err != nil { if err != nil {
b.crls = nil
return fmt.Errorf("error decoding CRL %s: %v", key, err) return fmt.Errorf("error decoding CRL %s: %v", key, err)
} }
b.crls[key] = crlInfo b.crls[key] = crlInfo
@ -121,6 +129,10 @@ func (b *backend) pathCRLDelete(
return logical.ErrorResponse(`"name" parameter cannot be empty`), nil return logical.ErrorResponse(`"name" parameter cannot be empty`), nil
} }
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.Lock() b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock() defer b.crlUpdateMutex.Unlock()
@ -131,8 +143,7 @@ func (b *backend) pathCRLDelete(
)), nil )), nil
} }
err := req.Storage.Delete("crls/" + name) if err := req.Storage.Delete("crls/" + name); err != nil {
if err != nil {
return logical.ErrorResponse(fmt.Sprintf( return logical.ErrorResponse(fmt.Sprintf(
"error deleting crl %s: %v", name, err), "error deleting crl %s: %v", name, err),
), nil ), nil
@ -150,6 +161,10 @@ func (b *backend) pathCRLRead(
return logical.ErrorResponse(`"name" parameter must be set`), nil return logical.ErrorResponse(`"name" parameter must be set`), nil
} }
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.RLock() b.crlUpdateMutex.RLock()
defer b.crlUpdateMutex.RUnlock() defer b.crlUpdateMutex.RUnlock()
@ -185,6 +200,10 @@ func (b *backend) pathCRLWrite(
return logical.ErrorResponse("parsed CRL is nil"), nil return logical.ErrorResponse("parsed CRL is nil"), nil
} }
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.Lock() b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock() defer b.crlUpdateMutex.Unlock()

View File

@ -17,6 +17,12 @@ func Backend() *backend {
b.Backend = &framework.Backend{ b.Backend = &framework.Backend{
Help: strings.TrimSpace(backendHelp), Help: strings.TrimSpace(backendHelp),
PathsSpecial: &logical.Paths{
LocalStorage: []string{
framework.WALPrefix,
},
},
Paths: []*framework.Path{ Paths: []*framework.Path{
pathConfigRoot(), pathConfigRoot(),
pathConfigLease(&b), pathConfigLease(&b),

View File

@ -31,6 +31,8 @@ func Backend() *backend {
secretCreds(&b), secretCreds(&b),
}, },
Invalidate: b.invalidate,
Clean: func() { Clean: func() {
b.ResetDB(nil) b.ResetDB(nil)
}, },
@ -107,6 +109,13 @@ func (b *backend) ResetDB(newSession *gocql.Session) {
b.session = newSession b.session = newSession
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.ResetDB(nil)
}
}
const backendHelp = ` const backendHelp = `
The Cassandra backend dynamically generates database users. The Cassandra backend dynamically generates database users.

View File

@ -33,6 +33,8 @@ func Backend() *framework.Backend {
}, },
Clean: b.ResetSession, Clean: b.ResetSession,
Invalidate: b.invalidate,
} }
return b.Backend return b.Backend
@ -97,6 +99,13 @@ func (b *backend) ResetSession() {
b.session = nil b.session = nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.ResetSession()
}
}
// LeaseConfig returns the lease configuration // LeaseConfig returns the lease configuration
func (b *backend) LeaseConfig(s logical.Storage) (*configLease, error) { func (b *backend) LeaseConfig(s logical.Storage) (*configLease, error) {
entry, err := s.Get("config/lease") entry, err := s.Get("config/lease")

View File

@ -32,6 +32,8 @@ func Backend() *backend {
secretCreds(&b), secretCreds(&b),
}, },
Invalidate: b.invalidate,
Clean: b.ResetDB, Clean: b.ResetDB,
} }
@ -112,6 +114,13 @@ func (b *backend) ResetDB() {
b.db = nil b.db = nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.ResetDB()
}
}
// LeaseConfig returns the lease configuration // LeaseConfig returns the lease configuration
func (b *backend) LeaseConfig(s logical.Storage) (*configLease, error) { func (b *backend) LeaseConfig(s logical.Storage) (*configLease, error) {
entry, err := s.Get("config/lease") entry, err := s.Get("config/lease")

View File

@ -32,6 +32,8 @@ func Backend() *backend {
secretCreds(&b), secretCreds(&b),
}, },
Invalidate: b.invalidate,
Clean: b.ResetDB, Clean: b.ResetDB,
} }
@ -105,6 +107,13 @@ func (b *backend) ResetDB() {
b.db = nil b.db = nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.ResetDB()
}
}
// Lease returns the lease information // Lease returns the lease information
func (b *backend) Lease(s logical.Storage) (*configLease, error) { func (b *backend) Lease(s logical.Storage) (*configLease, error) {
entry, err := s.Get("config/lease") entry, err := s.Get("config/lease")

View File

@ -29,6 +29,12 @@ func Backend() *backend {
"crl/pem", "crl/pem",
"crl", "crl",
}, },
LocalStorage: []string{
"revoked/",
"crl",
"certs/",
},
}, },
Paths: []*framework.Path{ Paths: []*framework.Path{

View File

@ -34,6 +34,8 @@ func Backend(conf *logical.BackendConfig) *backend {
}, },
Clean: b.ResetDB, Clean: b.ResetDB,
Invalidate: b.invalidate,
} }
b.logger = conf.Logger b.logger = conf.Logger
@ -126,6 +128,13 @@ func (b *backend) ResetDB() {
b.db = nil b.db = nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.ResetDB()
}
}
// Lease returns the lease information // Lease returns the lease information
func (b *backend) Lease(s logical.Storage) (*configLease, error) { func (b *backend) Lease(s logical.Storage) (*configLease, error) {
entry, err := s.Get("config/lease") entry, err := s.Get("config/lease")

View File

@ -35,6 +35,8 @@ func Backend() *backend {
}, },
Clean: b.resetClient, Clean: b.resetClient,
Invalidate: b.invalidate,
} }
return &b return &b
@ -99,6 +101,13 @@ func (b *backend) resetClient() {
b.client = nil b.client = nil
} }
func (b *backend) invalidate(key string) {
switch key {
case "config/connection":
b.resetClient()
}
}
// Lease returns the lease information // Lease returns the lease information
func (b *backend) Lease(s logical.Storage) (*configLease, error) { func (b *backend) Lease(s logical.Storage) (*configLease, error) {
entry, err := s.Get("config/lease") entry, err := s.Get("config/lease")

View File

@ -10,6 +10,7 @@ import (
type backend struct { type backend struct {
*framework.Backend *framework.Backend
view logical.Storage
salt *salt.Salt salt *salt.Salt
} }
@ -22,15 +23,8 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
} }
func Backend(conf *logical.BackendConfig) (*backend, error) { func Backend(conf *logical.BackendConfig) (*backend, error) {
salt, err := salt.NewSalt(conf.StorageView, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return nil, err
}
var b backend var b backend
b.salt = salt b.view = conf.StorageView
b.Backend = &framework.Backend{ b.Backend = &framework.Backend{
Help: strings.TrimSpace(backendHelp), Help: strings.TrimSpace(backendHelp),
@ -38,6 +32,10 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
Unauthenticated: []string{ Unauthenticated: []string{
"verify", "verify",
}, },
LocalStorage: []string{
"otp/",
},
}, },
Paths: []*framework.Path{ Paths: []*framework.Path{
@ -54,10 +52,23 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
secretDynamicKey(&b), secretDynamicKey(&b),
secretOTP(&b), secretOTP(&b),
}, },
Init: b.Initialize,
} }
return &b, nil return &b, nil
} }
func (b *backend) Initialize() error {
salt, err := salt.NewSalt(b.view, &salt.Config{
HashFunc: salt.SHA256Hash,
})
if err != nil {
return err
}
b.salt = salt
return nil
}
const backendHelp = ` const backendHelp = `
The SSH backend generates credentials allowing clients to establish SSH The SSH backend generates credentials allowing clients to establish SSH
connections to remote hosts. connections to remote hosts.

View File

@ -73,6 +73,10 @@ func TestBackend_allowed_users(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = b.Initialize()
if err != nil {
t.Fatal(err)
}
roleData := map[string]interface{}{ roleData := map[string]interface{}{
"key_type": "otp", "key_type": "otp",

View File

@ -1,6 +1,8 @@
package transit package transit
import ( import (
"strings"
"github.com/hashicorp/vault/helper/keysutil" "github.com/hashicorp/vault/helper/keysutil"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework" "github.com/hashicorp/vault/logical/framework"
@ -39,6 +41,8 @@ func Backend(conf *logical.BackendConfig) *backend {
}, },
Secrets: []*framework.Secret{}, Secrets: []*framework.Secret{},
Invalidate: b.invalidate,
} }
b.lm = keysutil.NewLockManager(conf.System.CachingDisabled()) b.lm = keysutil.NewLockManager(conf.System.CachingDisabled())
@ -50,3 +54,14 @@ type backend struct {
*framework.Backend *framework.Backend
lm *keysutil.LockManager lm *keysutil.LockManager
} }
func (b *backend) invalidate(key string) {
if b.Logger().IsTrace() {
b.Logger().Trace("transit: invalidating key", "key", key)
}
switch {
case strings.HasPrefix(key, "policy/"):
name := strings.TrimPrefix(key, "policy/")
b.lm.InvalidatePolicy(name)
}
}

View File

@ -15,11 +15,13 @@ type MountCommand struct {
func (c *MountCommand) Run(args []string) int { func (c *MountCommand) Run(args []string) int {
var description, path, defaultLeaseTTL, maxLeaseTTL string var description, path, defaultLeaseTTL, maxLeaseTTL string
var local bool
flags := c.Meta.FlagSet("mount", meta.FlagSetDefault) flags := c.Meta.FlagSet("mount", meta.FlagSetDefault)
flags.StringVar(&description, "description", "", "") flags.StringVar(&description, "description", "", "")
flags.StringVar(&path, "path", "", "") flags.StringVar(&path, "path", "", "")
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "") flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "") flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
flags.BoolVar(&local, "local", false, "")
flags.Usage = func() { c.Ui.Error(c.Help()) } flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
return 1 return 1
@ -54,6 +56,7 @@ func (c *MountCommand) Run(args []string) int {
DefaultLeaseTTL: defaultLeaseTTL, DefaultLeaseTTL: defaultLeaseTTL,
MaxLeaseTTL: maxLeaseTTL, MaxLeaseTTL: maxLeaseTTL,
}, },
Local: local,
} }
if err := client.Sys().Mount(path, mountInfo); err != nil { if err := client.Sys().Mount(path, mountInfo); err != nil {
@ -102,6 +105,10 @@ Mount Options:
the previously set value. Set to '0' to the previously set value. Set to '0' to
explicitly set it to use the global default. explicitly set it to use the global default.
-local Mark the mount as a local mount. Local mounts
are not replicated nor (if a secondary)
removed by replication.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)
} }

View File

@ -42,7 +42,7 @@ func (c *MountsCommand) Run(args []string) int {
} }
sort.Strings(paths) sort.Strings(paths)
columns := []string{"Path | Type | Default TTL | Max TTL | Description"} columns := []string{"Path | Type | Default TTL | Max TTL | Replication Behavior | Description"}
for _, path := range paths { for _, path := range paths {
mount := mounts[path] mount := mounts[path]
defTTL := "system" defTTL := "system"
@ -63,8 +63,12 @@ func (c *MountsCommand) Run(args []string) int {
case mount.Config.MaxLeaseTTL != 0: case mount.Config.MaxLeaseTTL != 0:
maxTTL = strconv.Itoa(mount.Config.MaxLeaseTTL) maxTTL = strconv.Itoa(mount.Config.MaxLeaseTTL)
} }
replicatedBehavior := "replicated"
if mount.Local {
replicatedBehavior = "local"
}
columns = append(columns, fmt.Sprintf( columns = append(columns, fmt.Sprintf(
"%s | %s | %s | %s | %s", path, mount.Type, defTTL, maxTTL, mount.Description)) "%s | %s | %s | %s | %s | %s", path, mount.Type, defTTL, maxTTL, replicatedBehavior, mount.Description))
} }
c.Ui.Output(columnize.SimpleFormat(columns)) c.Ui.Output(columnize.SimpleFormat(columns))

View File

@ -61,7 +61,7 @@ type ServerCommand struct {
} }
func (c *ServerCommand) Run(args []string) int { func (c *ServerCommand) Run(args []string) int {
var dev, verifyOnly, devHA bool var dev, verifyOnly, devHA, devTransactional bool
var configPath []string var configPath []string
var logLevel, devRootTokenID, devListenAddress string var logLevel, devRootTokenID, devListenAddress string
flags := c.Meta.FlagSet("server", meta.FlagSetDefault) flags := c.Meta.FlagSet("server", meta.FlagSetDefault)
@ -70,7 +70,8 @@ func (c *ServerCommand) Run(args []string) int {
flags.StringVar(&devListenAddress, "dev-listen-address", "", "") flags.StringVar(&devListenAddress, "dev-listen-address", "", "")
flags.StringVar(&logLevel, "log-level", "info", "") flags.StringVar(&logLevel, "log-level", "info", "")
flags.BoolVar(&verifyOnly, "verify-only", false, "") flags.BoolVar(&verifyOnly, "verify-only", false, "")
flags.BoolVar(&devHA, "dev-ha", false, "") flags.BoolVar(&devHA, "ha", false, "")
flags.BoolVar(&devTransactional, "transactional", false, "")
flags.Usage = func() { c.Ui.Output(c.Help()) } flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config") flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
@ -122,7 +123,7 @@ func (c *ServerCommand) Run(args []string) int {
devListenAddress = os.Getenv("VAULT_DEV_LISTEN_ADDRESS") devListenAddress = os.Getenv("VAULT_DEV_LISTEN_ADDRESS")
} }
if devHA { if devHA || devTransactional {
dev = true dev = true
} }
@ -143,7 +144,7 @@ func (c *ServerCommand) Run(args []string) int {
// Load the configuration // Load the configuration
var config *server.Config var config *server.Config
if dev { if dev {
config = server.DevConfig(devHA) config = server.DevConfig(devHA, devTransactional)
if devListenAddress != "" { if devListenAddress != "" {
config.Listeners[0].Config["address"] = devListenAddress config.Listeners[0].Config["address"] = devListenAddress
} }
@ -235,6 +236,9 @@ func (c *ServerCommand) Run(args []string) int {
ClusterName: config.ClusterName, ClusterName: config.ClusterName,
CacheSize: config.CacheSize, CacheSize: config.CacheSize,
} }
if dev {
coreConfig.DevToken = devRootTokenID
}
var disableClustering bool var disableClustering bool

View File

@ -38,7 +38,7 @@ type Config struct {
} }
// DevConfig is a Config that is used for dev mode of Vault. // DevConfig is a Config that is used for dev mode of Vault.
func DevConfig(ha bool) *Config { func DevConfig(ha, transactional bool) *Config {
ret := &Config{ ret := &Config{
DisableCache: false, DisableCache: false,
DisableMlock: true, DisableMlock: true,
@ -63,7 +63,12 @@ func DevConfig(ha bool) *Config {
DefaultLeaseTTL: 32 * 24 * time.Hour, DefaultLeaseTTL: 32 * 24 * time.Hour,
} }
if ha { switch {
case ha && transactional:
ret.Backend.Type = "inmem_transactional_ha"
case !ha && transactional:
ret.Backend.Type = "inmem_transactional"
case ha && !transactional:
ret.Backend.Type = "inmem_ha" ret.Backend.Type = "inmem_ha"
} }

View File

@ -33,7 +33,7 @@ func TestServer_CommonHA(t *testing.T) {
args := []string{"-config", tmpfile.Name(), "-verify-only", "true"} args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s\n\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
} }
if !strings.Contains(ui.OutputWriter.String(), "(HA available)") { if !strings.Contains(ui.OutputWriter.String(), "(HA available)") {
@ -61,7 +61,7 @@ func TestServer_GoodSeparateHA(t *testing.T) {
args := []string{"-config", tmpfile.Name(), "-verify-only", "true"} args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("bad: %d\n\n%s\n\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
} }
if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") { if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {

View File

@ -71,6 +71,15 @@ func (lm *LockManager) CacheActive() bool {
return lm.cache != nil return lm.cache != nil
} }
func (lm *LockManager) InvalidatePolicy(name string) {
// Check if it's in our cache. If so, return right away.
if lm.CacheActive() {
lm.cacheMutex.Lock()
defer lm.cacheMutex.Unlock()
delete(lm.cache, name)
}
}
func (lm *LockManager) policyLock(name string, lockType bool) *sync.RWMutex { func (lm *LockManager) policyLock(name string, lockType bool) *sync.RWMutex {
lm.locksMutex.RLock() lm.locksMutex.RLock()
lock := lm.locks[name] lock := lm.locks[name]

View File

@ -16,6 +16,12 @@ func CubbyholeBackendFactory(conf *logical.BackendConfig) (logical.Backend, erro
b.Backend = &framework.Backend{ b.Backend = &framework.Backend{
Help: strings.TrimSpace(cubbyholeHelp), Help: strings.TrimSpace(cubbyholeHelp),
PathsSpecial: &logical.Paths{
LocalStorage: []string{
"*",
},
},
Paths: []*framework.Path{ Paths: []*framework.Path{
&framework.Path{ &framework.Path{
Pattern: ".*", Pattern: ".*",

View File

@ -12,9 +12,13 @@ import (
func TestCubbyholeBackend_RootPaths(t *testing.T) { func TestCubbyholeBackend_RootPaths(t *testing.T) {
b := testCubbyholeBackend() b := testCubbyholeBackend()
root := b.SpecialPaths() expected := []string{
if root != nil { "*",
t.Fatalf("unexpected: %v", root) }
actual := b.SpecialPaths().LocalStorage
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
} }
} }

View File

@ -9,6 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/duration" "github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework" "github.com/hashicorp/vault/logical/framework"
@ -39,12 +40,14 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
"audit", "audit",
"audit/*", "audit/*",
"raw/*", "raw/*",
"replication/primary/secondary-token",
"rotate", "rotate",
"config/auditing/*", "config/auditing/*",
}, },
Unauthenticated: []string{ Unauthenticated: []string{
"wrapping/pubkey", "wrapping/pubkey",
"replication/status",
}, },
}, },
@ -226,6 +229,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeMap, Type: framework.TypeMap,
Description: strings.TrimSpace(sysHelp["mount_config"][0]), Description: strings.TrimSpace(sysHelp["mount_config"][0]),
}, },
"local": &framework.FieldSchema{
Type: framework.TypeBool,
Default: false,
Description: strings.TrimSpace(sysHelp["mount_local"][0]),
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@ -377,6 +385,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeString, Type: framework.TypeString,
Description: strings.TrimSpace(sysHelp["auth_desc"][0]), Description: strings.TrimSpace(sysHelp["auth_desc"][0]),
}, },
"local": &framework.FieldSchema{
Type: framework.TypeBool,
Default: false,
Description: strings.TrimSpace(sysHelp["mount_local"][0]),
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@ -495,6 +508,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeMap, Type: framework.TypeMap,
Description: strings.TrimSpace(sysHelp["audit_opts"][0]), Description: strings.TrimSpace(sysHelp["audit_opts"][0]),
}, },
"local": &framework.FieldSchema{
Type: framework.TypeBool,
Default: false,
Description: strings.TrimSpace(sysHelp["mount_local"][0]),
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@ -657,6 +675,10 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
}, },
} }
b.Backend.Paths = append(b.Backend.Paths, b.replicationPaths()...)
b.Backend.Invalidate = b.invalidate
return b.Backend.Setup(config) return b.Backend.Setup(config)
} }
@ -668,6 +690,20 @@ type SystemBackend struct {
Backend *framework.Backend Backend *framework.Backend
} }
func (b *SystemBackend) invalidate(key string) {
if b.Core.logger.IsTrace() {
b.Core.logger.Trace("sys: invaliding key", "key", key)
}
switch {
case strings.HasPrefix(key, policySubPath):
b.Core.stateLock.RLock()
defer b.Core.stateLock.RUnlock()
if b.Core.policyStore != nil {
b.Core.policyStore.invalidate(strings.TrimPrefix(key, policySubPath))
}
}
}
// handleAuditedHeaderUpdate creates or overwrites a header entry // handleAuditedHeaderUpdate creates or overwrites a header entry
func (b *SystemBackend) handleAuditedHeaderUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) { func (b *SystemBackend) handleAuditedHeaderUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
header := d.Get("header").(string) header := d.Get("header").(string)
@ -869,6 +905,7 @@ func (b *SystemBackend) handleMountTable(
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
}, },
"local": entry.Local,
} }
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
@ -880,6 +917,15 @@ func (b *SystemBackend) handleMountTable(
// handleMount is used to mount a new path // handleMount is used to mount a new path
func (b *SystemBackend) handleMount( func (b *SystemBackend) handleMount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
local := data.Get("local").(bool)
if !local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot add a non-local mount to a replication secondary"), nil
}
// Get all the options // Get all the options
path := data.Get("path").(string) path := data.Get("path").(string)
logicalType := data.Get("type").(string) logicalType := data.Get("type").(string)
@ -954,6 +1000,7 @@ func (b *SystemBackend) handleMount(
Type: logicalType, Type: logicalType,
Description: description, Description: description,
Config: config, Config: config,
Local: local,
} }
// Attempt mount // Attempt mount
@ -979,6 +1026,10 @@ func handleError(
// handleUnmount is used to unmount a path // handleUnmount is used to unmount a path
func (b *SystemBackend) handleUnmount( func (b *SystemBackend) handleUnmount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
suffix := strings.TrimPrefix(req.Path, "mounts/") suffix := strings.TrimPrefix(req.Path, "mounts/")
if len(suffix) == 0 { if len(suffix) == 0 {
return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest
@ -986,6 +1037,11 @@ func (b *SystemBackend) handleUnmount(
suffix = sanitizeMountPath(suffix) suffix = sanitizeMountPath(suffix)
entry := b.Core.router.MatchingMountEntry(suffix)
if entry != nil && !entry.Local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot unmount a non-local mount on a replication secondary"), nil
}
// Attempt unmount // Attempt unmount
if existed, err := b.Core.unmount(suffix); existed && err != nil { if existed, err := b.Core.unmount(suffix); existed && err != nil {
b.Backend.Logger().Error("sys: unmount failed", "path", suffix, "error", err) b.Backend.Logger().Error("sys: unmount failed", "path", suffix, "error", err)
@ -998,6 +1054,10 @@ func (b *SystemBackend) handleUnmount(
// handleRemount is used to remount a path // handleRemount is used to remount a path
func (b *SystemBackend) handleRemount( func (b *SystemBackend) handleRemount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
// Get the paths // Get the paths
fromPath := data.Get("from").(string) fromPath := data.Get("from").(string)
toPath := data.Get("to").(string) toPath := data.Get("to").(string)
@ -1010,6 +1070,11 @@ func (b *SystemBackend) handleRemount(
fromPath = sanitizeMountPath(fromPath) fromPath = sanitizeMountPath(fromPath)
toPath = sanitizeMountPath(toPath) toPath = sanitizeMountPath(toPath)
entry := b.Core.router.MatchingMountEntry(fromPath)
if entry != nil && !entry.Local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot remount a non-local mount on a replication secondary"), nil
}
// Attempt remount // Attempt remount
if err := b.Core.remount(fromPath, toPath); err != nil { if err := b.Core.remount(fromPath, toPath); err != nil {
b.Backend.Logger().Error("sys: remount failed", "from_path", fromPath, "to_path", toPath, "error", err) b.Backend.Logger().Error("sys: remount failed", "from_path", fromPath, "to_path", toPath, "error", err)
@ -1095,6 +1160,10 @@ func (b *SystemBackend) handleMountTuneWrite(
// handleTuneWriteCommon is used to set config settings on a path // handleTuneWriteCommon is used to set config settings on a path
func (b *SystemBackend) handleTuneWriteCommon( func (b *SystemBackend) handleTuneWriteCommon(
path string, data *framework.FieldData) (*logical.Response, error) { path string, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
path = sanitizeMountPath(path) path = sanitizeMountPath(path)
// Prevent protected paths from being changed // Prevent protected paths from being changed
@ -1110,6 +1179,9 @@ func (b *SystemBackend) handleTuneWriteCommon(
b.Backend.Logger().Error("sys: tune failed: no mount entry found", "path", path) b.Backend.Logger().Error("sys: tune failed: no mount entry found", "path", path)
return handleError(fmt.Errorf("sys: tune of path '%s' failed: no mount entry found", path)) return handleError(fmt.Errorf("sys: tune of path '%s' failed: no mount entry found", path))
} }
if mountEntry != nil && !mountEntry.Local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot tune a non-local mount on a replication secondary"), nil
}
var lock *sync.RWMutex var lock *sync.RWMutex
switch { switch {
@ -1249,6 +1321,7 @@ func (b *SystemBackend) handleAuthTable(
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()), "default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()), "max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
}, },
"local": entry.Local,
} }
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
} }
@ -1258,6 +1331,15 @@ func (b *SystemBackend) handleAuthTable(
// handleEnableAuth is used to enable a new credential backend // handleEnableAuth is used to enable a new credential backend
func (b *SystemBackend) handleEnableAuth( func (b *SystemBackend) handleEnableAuth(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
local := data.Get("local").(bool)
if !local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot add a non-local mount to a replication secondary"), nil
}
// Get all the options // Get all the options
path := data.Get("path").(string) path := data.Get("path").(string)
logicalType := data.Get("type").(string) logicalType := data.Get("type").(string)
@ -1277,6 +1359,7 @@ func (b *SystemBackend) handleEnableAuth(
Path: path, Path: path,
Type: logicalType, Type: logicalType,
Description: description, Description: description,
Local: local,
} }
// Attempt enabling // Attempt enabling
@ -1391,6 +1474,7 @@ func (b *SystemBackend) handleAuditTable(
"type": entry.Type, "type": entry.Type,
"description": entry.Description, "description": entry.Description,
"options": entry.Options, "options": entry.Options,
"local": entry.Local,
} }
resp.Data[entry.Path] = info resp.Data[entry.Path] = info
} }
@ -1424,6 +1508,15 @@ func (b *SystemBackend) handleAuditHash(
// handleEnableAudit is used to enable a new audit backend // handleEnableAudit is used to enable a new audit backend
func (b *SystemBackend) handleEnableAudit( func (b *SystemBackend) handleEnableAudit(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
local := data.Get("local").(bool)
if !local && repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot add a non-local mount to a replication secondary"), nil
}
// Get all the options // Get all the options
path := data.Get("path").(string) path := data.Get("path").(string)
backendType := data.Get("type").(string) backendType := data.Get("type").(string)
@ -1447,6 +1540,7 @@ func (b *SystemBackend) handleEnableAudit(
Type: backendType, Type: backendType,
Description: description, Description: description,
Options: optionMap, Options: optionMap,
Local: local,
} }
// Attempt enabling // Attempt enabling
@ -1562,6 +1656,13 @@ func (b *SystemBackend) handleKeyStatus(
// handleRotate is used to trigger a key rotation // handleRotate is used to trigger a key rotation
func (b *SystemBackend) handleRotate( func (b *SystemBackend) handleRotate(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) { req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
if repState == consts.ReplicationSecondary {
return logical.ErrorResponse("cannot rotate on a replication secondary"), nil
}
// Rotate to the new term // Rotate to the new term
newTerm, err := b.Core.barrier.Rotate() newTerm, err := b.Core.barrier.Rotate()
if err != nil { if err != nil {
@ -1584,6 +1685,17 @@ func (b *SystemBackend) handleRotate(
} }
}) })
} }
// Write to the canary path, which will force a synchronous truing during
// replication
if err := b.Core.barrier.Put(&Entry{
Key: coreKeyringCanaryPath,
Value: []byte(fmt.Sprintf("new-rotation-term-%d", newTerm)),
}); err != nil {
b.Core.logger.Error("core: error saving keyring canary", "error", err)
return nil, fmt.Errorf("failed to save keyring canary: %v", err)
}
return nil, nil return nil, nil
} }
@ -1950,6 +2062,11 @@ west coast.
and max_lease_ttl.`, and max_lease_ttl.`,
}, },
"mount_local": {
`Mark the mount as a local mount, which is not replicated
and is unaffected by replication.`,
},
"tune_default_lease_ttl": { "tune_default_lease_ttl": {
`The default lease TTL for this mount.`, `The default lease TTL for this mount.`,
}, },

View File

@ -21,6 +21,7 @@ func TestSystemBackend_RootPaths(t *testing.T) {
"audit", "audit",
"audit/*", "audit/*",
"raw/*", "raw/*",
"replication/*",
"rotate", "rotate",
"config/auditing/*", "config/auditing/*",
} }
@ -50,6 +51,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64), "default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64), "max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
}, },
"local": false,
}, },
"sys/": map[string]interface{}{ "sys/": map[string]interface{}{
"type": "system", "type": "system",
@ -58,6 +60,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64), "default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64), "max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
}, },
"local": false,
}, },
"cubbyhole/": map[string]interface{}{ "cubbyhole/": map[string]interface{}{
"description": "per-token private secret storage", "description": "per-token private secret storage",
@ -66,6 +69,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64), "default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64), "max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
}, },
"local": false,
}, },
} }
if !reflect.DeepEqual(resp.Data, exp) { if !reflect.DeepEqual(resp.Data, exp) {
@ -580,6 +584,7 @@ func TestSystemBackend_authTable(t *testing.T) {
"default_lease_ttl": int64(0), "default_lease_ttl": int64(0),
"max_lease_ttl": int64(0), "max_lease_ttl": int64(0),
}, },
"local": false,
}, },
} }
if !reflect.DeepEqual(resp.Data, exp) { if !reflect.DeepEqual(resp.Data, exp) {
@ -843,6 +848,7 @@ func TestSystemBackend_auditTable(t *testing.T) {
req.Data["options"] = map[string]interface{}{ req.Data["options"] = map[string]interface{}{
"foo": "bar", "foo": "bar",
} }
req.Data["local"] = true
b.HandleRequest(req) b.HandleRequest(req)
req = logical.TestRequest(t, logical.ReadOperation, "audit") req = logical.TestRequest(t, logical.ReadOperation, "audit")
@ -859,6 +865,7 @@ func TestSystemBackend_auditTable(t *testing.T) {
"options": map[string]string{ "options": map[string]string{
"foo": "bar", "foo": "bar",
}, },
"local": true,
}, },
} }
if !reflect.DeepEqual(resp.Data, exp) { if !reflect.DeepEqual(resp.Data, exp) {

View File

@ -11,7 +11,7 @@
<ul class="nav"> <ul class="nav">
<li<%= sidebar_current("docs-internals-architecture") %>> <li<%= sidebar_current("docs-internals-architecture") %>>
<a href="/docs/internals/architecture.html">Architecture</a> <a href="/docs/internals/architecture.html">Architecture</a>
</li> </li>
<li<%= sidebar_current("docs-internals-ha") %>> <li<%= sidebar_current("docs-internals-ha") %>>
<a href="/docs/internals/high-availability.html">High Availability</a> <a href="/docs/internals/high-availability.html">High Availability</a>
@ -19,15 +19,15 @@
<li<%= sidebar_current("docs-internals-security") %>> <li<%= sidebar_current("docs-internals-security") %>>
<a href="/docs/internals/security.html">Security Model</a> <a href="/docs/internals/security.html">Security Model</a>
</li> </li>
<li<%= sidebar_current("docs-internals-telemetry") %>> <li<%= sidebar_current("docs-internals-telemetry") %>>
<a href="/docs/internals/telemetry.html">Telemetry</a> <a href="/docs/internals/telemetry.html">Telemetry</a>
</li> </li>
<li<%= sidebar_current("docs-internals-token") %>> <li<%= sidebar_current("docs-internals-token") %>>
<a href="/docs/internals/token.html">Token Authentication</a> <a href="/docs/internals/token.html">Token Authentication</a>
</li> </li>
<li<%= sidebar_current("docs-internals-rotation") %>> <li<%= sidebar_current("docs-internals-rotation") %>>
<a href="/docs/internals/rotation.html">Key Rotation</a> <a href="/docs/internals/rotation.html">Key Rotation</a>