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 0c39b613c8
commit c81582fea0
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) {
// Initialize the salt
salt, err := salt.NewSalt(conf.StorageView, &salt.Config{
HashFunc: salt.SHA1Hash,
})
if err != nil {
return nil, err
}
var b backend
b.Salt = salt
b.MapAppId = &framework.PolicyMap{
PathMap: framework.PathMap{
Name: "app-id",
Salt: salt,
Schema: map[string]*framework.FieldSchema{
"display_name": &framework.FieldSchema{
Type: framework.TypeString,
@ -48,7 +38,6 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
b.MapUserId = &framework.PathMap{
Name: "user-id",
Salt: salt,
Schema: map[string]*framework.FieldSchema{
"cidr_block": &framework.FieldSchema{
Type: framework.TypeString,
@ -81,17 +70,11 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
),
AuthRenew: b.pathLoginRenew,
Init: b.initialize,
}
// 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(conf.StorageView); err != nil {
return nil, err
}
}
b.view = conf.StorageView
return b.Backend, nil
}
@ -100,10 +83,36 @@ type backend struct {
*framework.Backend
Salt *salt.Salt
view logical.Storage
MapAppId *framework.PolicyMap
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
// 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

View File

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

View File

@ -17,6 +17,9 @@ type backend struct {
// by this backend.
salt *salt.Salt
// The view to use when creating the salt
view logical.Storage
// Guard to clean-up the expired SecretID entries
tidySecretIDCASGuard uint32
@ -57,18 +60,9 @@ func Factory(conf *logical.BackendConfig) (logical.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
b := &backend{
// Set the salt object for the backend
salt: salt,
view: conf.StorageView,
// Create the map of locks to modify the registered roles
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),
}
var err error
// Create 256 locks each for managing RoleID and SecretIDs. This will avoid
// 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
@ -129,10 +125,22 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
pathTidySecretID(b),
},
),
Init: b.initialize,
}
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.
// 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

View File

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

View File

@ -23,6 +23,9 @@ type backend struct {
*framework.Backend
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.
configMutex sync.RWMutex
@ -59,18 +62,11 @@ type backend struct {
}
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{
// Setting the periodic func to be run once in an hour.
// If there is a real need, this can be made configurable.
tidyCooldownPeriod: time.Hour,
Salt: salt,
view: conf.StorageView,
EC2ClientsMap: make(map[string]map[string]*ec2.EC2),
IAMClientsMap: make(map[string]map[string]*iam.IAM),
}
@ -83,6 +79,9 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
Unauthenticated: []string{
"login",
},
LocalStorage: []string{
"whitelist/identity/",
},
},
Paths: []*framework.Path{
pathLogin(b),
@ -104,11 +103,26 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
pathIdentityWhitelist(b),
pathTidyIdentityWhitelist(b),
},
Invalidate: b.invalidate,
Init: b.initialize,
}
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.
// 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
}
func (b *backend) invalidate(key string) {
switch key {
case "config/client":
b.configMutex.Lock()
defer b.configMutex.Unlock()
b.flushCachedEC2Clients()
b.flushCachedIAMClients()
}
}
const backendHelp = `
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.

View File

@ -1,6 +1,7 @@
package cert
import (
"strings"
"sync"
"github.com/hashicorp/vault/logical"
@ -13,7 +14,7 @@ func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
if err != nil {
return b, err
}
return b, b.populateCRLs(conf.StorageView)
return b, nil
}
func Backend() *backend {
@ -36,9 +37,10 @@ func Backend() *backend {
}),
AuthRenew: b.pathLoginRenew,
Invalidate: b.invalidate,
}
b.crls = map[string]CRLInfo{}
b.crlUpdateMutex = &sync.RWMutex{}
return &b
@ -52,6 +54,15 @@ type backend struct {
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 = `
The "cert" credential provider allows authentication using
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()
defer b.crlUpdateMutex.Unlock()
if b.crls != nil {
return nil
}
b.crls = map[string]CRLInfo{}
keys, err := storage.List("crls/")
if err != nil {
return fmt.Errorf("error listing CRLs: %v", err)
@ -56,6 +62,7 @@ func (b *backend) populateCRLs(storage logical.Storage) error {
for _, key := range keys {
entry, err := storage.Get("crls/" + key)
if err != nil {
b.crls = nil
return fmt.Errorf("error loading CRL %s: %v", key, err)
}
if entry == nil {
@ -64,6 +71,7 @@ func (b *backend) populateCRLs(storage logical.Storage) error {
var crlInfo CRLInfo
err = entry.DecodeJSON(&crlInfo)
if err != nil {
b.crls = nil
return fmt.Errorf("error decoding CRL %s: %v", key, err)
}
b.crls[key] = crlInfo
@ -121,6 +129,10 @@ func (b *backend) pathCRLDelete(
return logical.ErrorResponse(`"name" parameter cannot be empty`), nil
}
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock()
@ -131,8 +143,7 @@ func (b *backend) pathCRLDelete(
)), nil
}
err := req.Storage.Delete("crls/" + name)
if err != nil {
if err := req.Storage.Delete("crls/" + name); err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"error deleting crl %s: %v", name, err),
), nil
@ -150,6 +161,10 @@ func (b *backend) pathCRLRead(
return logical.ErrorResponse(`"name" parameter must be set`), nil
}
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.RLock()
defer b.crlUpdateMutex.RUnlock()
@ -185,6 +200,10 @@ func (b *backend) pathCRLWrite(
return logical.ErrorResponse("parsed CRL is nil"), nil
}
if err := b.populateCRLs(req.Storage); err != nil {
return nil, err
}
b.crlUpdateMutex.Lock()
defer b.crlUpdateMutex.Unlock()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
package transit
import (
"strings"
"github.com/hashicorp/vault/helper/keysutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@ -39,6 +41,8 @@ func Backend(conf *logical.BackendConfig) *backend {
},
Secrets: []*framework.Secret{},
Invalidate: b.invalidate,
}
b.lm = keysutil.NewLockManager(conf.System.CachingDisabled())
@ -50,3 +54,14 @@ type backend struct {
*framework.Backend
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 {
var description, path, defaultLeaseTTL, maxLeaseTTL string
var local bool
flags := c.Meta.FlagSet("mount", meta.FlagSetDefault)
flags.StringVar(&description, "description", "", "")
flags.StringVar(&path, "path", "", "")
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
flags.BoolVar(&local, "local", false, "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
return 1
@ -54,6 +56,7 @@ func (c *MountCommand) Run(args []string) int {
DefaultLeaseTTL: defaultLeaseTTL,
MaxLeaseTTL: maxLeaseTTL,
},
Local: local,
}
if err := client.Sys().Mount(path, mountInfo); err != nil {
@ -102,6 +105,10 @@ Mount Options:
the previously set value. Set to '0' to
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)
}

View File

@ -42,7 +42,7 @@ func (c *MountsCommand) Run(args []string) int {
}
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 {
mount := mounts[path]
defTTL := "system"
@ -63,8 +63,12 @@ func (c *MountsCommand) Run(args []string) int {
case mount.Config.MaxLeaseTTL != 0:
maxTTL = strconv.Itoa(mount.Config.MaxLeaseTTL)
}
replicatedBehavior := "replicated"
if mount.Local {
replicatedBehavior = "local"
}
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))

View File

@ -61,7 +61,7 @@ type ServerCommand struct {
}
func (c *ServerCommand) Run(args []string) int {
var dev, verifyOnly, devHA bool
var dev, verifyOnly, devHA, devTransactional bool
var configPath []string
var logLevel, devRootTokenID, devListenAddress string
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(&logLevel, "log-level", "info", "")
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.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
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")
}
if devHA {
if devHA || devTransactional {
dev = true
}
@ -143,7 +144,7 @@ func (c *ServerCommand) Run(args []string) int {
// Load the configuration
var config *server.Config
if dev {
config = server.DevConfig(devHA)
config = server.DevConfig(devHA, devTransactional)
if devListenAddress != "" {
config.Listeners[0].Config["address"] = devListenAddress
}
@ -235,6 +236,9 @@ func (c *ServerCommand) Run(args []string) int {
ClusterName: config.ClusterName,
CacheSize: config.CacheSize,
}
if dev {
coreConfig.DevToken = devRootTokenID
}
var disableClustering bool

View File

@ -38,7 +38,7 @@ type Config struct {
}
// 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{
DisableCache: false,
DisableMlock: true,
@ -63,7 +63,12 @@ func DevConfig(ha bool) *Config {
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"
}

View File

@ -33,7 +33,7 @@ func TestServer_CommonHA(t *testing.T) {
args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}
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)") {
@ -61,7 +61,7 @@ func TestServer_GoodSeparateHA(t *testing.T) {
args := []string{"-config", tmpfile.Name(), "-verify-only", "true"}
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:") {

View File

@ -71,6 +71,15 @@ func (lm *LockManager) CacheActive() bool {
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 {
lm.locksMutex.RLock()
lock := lm.locks[name]

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@ -39,12 +40,14 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
"audit",
"audit/*",
"raw/*",
"replication/primary/secondary-token",
"rotate",
"config/auditing/*",
},
Unauthenticated: []string{
"wrapping/pubkey",
"replication/status",
},
},
@ -226,6 +229,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeMap,
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{
@ -377,6 +385,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeString,
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{
@ -495,6 +508,11 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) (logical.Backen
Type: framework.TypeMap,
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{
@ -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)
}
@ -668,6 +690,20 @@ type SystemBackend struct {
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
func (b *SystemBackend) handleAuditedHeaderUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
header := d.Get("header").(string)
@ -869,6 +905,7 @@ func (b *SystemBackend) handleMountTable(
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
},
"local": entry.Local,
}
resp.Data[entry.Path] = info
@ -880,6 +917,15 @@ func (b *SystemBackend) handleMountTable(
// handleMount is used to mount a new path
func (b *SystemBackend) handleMount(
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
path := data.Get("path").(string)
logicalType := data.Get("type").(string)
@ -954,6 +1000,7 @@ func (b *SystemBackend) handleMount(
Type: logicalType,
Description: description,
Config: config,
Local: local,
}
// Attempt mount
@ -979,6 +1026,10 @@ func handleError(
// handleUnmount is used to unmount a path
func (b *SystemBackend) handleUnmount(
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/")
if len(suffix) == 0 {
return logical.ErrorResponse("path cannot be blank"), logical.ErrInvalidRequest
@ -986,6 +1037,11 @@ func (b *SystemBackend) handleUnmount(
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
if existed, err := b.Core.unmount(suffix); existed && err != nil {
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
func (b *SystemBackend) handleRemount(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
// Get the paths
fromPath := data.Get("from").(string)
toPath := data.Get("to").(string)
@ -1010,6 +1070,11 @@ func (b *SystemBackend) handleRemount(
fromPath = sanitizeMountPath(fromPath)
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
if err := b.Core.remount(fromPath, toPath); err != nil {
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
func (b *SystemBackend) handleTuneWriteCommon(
path string, data *framework.FieldData) (*logical.Response, error) {
b.Core.clusterParamsLock.RLock()
repState := b.Core.replicationState
b.Core.clusterParamsLock.RUnlock()
path = sanitizeMountPath(path)
// 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)
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
switch {
@ -1249,6 +1321,7 @@ func (b *SystemBackend) handleAuthTable(
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
},
"local": entry.Local,
}
resp.Data[entry.Path] = info
}
@ -1258,6 +1331,15 @@ func (b *SystemBackend) handleAuthTable(
// handleEnableAuth is used to enable a new credential backend
func (b *SystemBackend) handleEnableAuth(
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
path := data.Get("path").(string)
logicalType := data.Get("type").(string)
@ -1277,6 +1359,7 @@ func (b *SystemBackend) handleEnableAuth(
Path: path,
Type: logicalType,
Description: description,
Local: local,
}
// Attempt enabling
@ -1391,6 +1474,7 @@ func (b *SystemBackend) handleAuditTable(
"type": entry.Type,
"description": entry.Description,
"options": entry.Options,
"local": entry.Local,
}
resp.Data[entry.Path] = info
}
@ -1424,6 +1508,15 @@ func (b *SystemBackend) handleAuditHash(
// handleEnableAudit is used to enable a new audit backend
func (b *SystemBackend) handleEnableAudit(
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
path := data.Get("path").(string)
backendType := data.Get("type").(string)
@ -1447,6 +1540,7 @@ func (b *SystemBackend) handleEnableAudit(
Type: backendType,
Description: description,
Options: optionMap,
Local: local,
}
// Attempt enabling
@ -1562,6 +1656,13 @@ func (b *SystemBackend) handleKeyStatus(
// handleRotate is used to trigger a key rotation
func (b *SystemBackend) handleRotate(
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
newTerm, err := b.Core.barrier.Rotate()
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
}
@ -1950,6 +2062,11 @@ west coast.
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": {
`The default lease TTL for this mount.`,
},

View File

@ -21,6 +21,7 @@ func TestSystemBackend_RootPaths(t *testing.T) {
"audit",
"audit/*",
"raw/*",
"replication/*",
"rotate",
"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),
"max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
},
"local": false,
},
"sys/": map[string]interface{}{
"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),
"max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
},
"local": false,
},
"cubbyhole/": map[string]interface{}{
"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),
"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) {
@ -580,6 +584,7 @@ func TestSystemBackend_authTable(t *testing.T) {
"default_lease_ttl": int64(0),
"max_lease_ttl": int64(0),
},
"local": false,
},
}
if !reflect.DeepEqual(resp.Data, exp) {
@ -843,6 +848,7 @@ func TestSystemBackend_auditTable(t *testing.T) {
req.Data["options"] = map[string]interface{}{
"foo": "bar",
}
req.Data["local"] = true
b.HandleRequest(req)
req = logical.TestRequest(t, logical.ReadOperation, "audit")
@ -859,6 +865,7 @@ func TestSystemBackend_auditTable(t *testing.T) {
"options": map[string]string{
"foo": "bar",
},
"local": true,
},
}
if !reflect.DeepEqual(resp.Data, exp) {

View File

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