2015-03-09 23:33:27 +00:00
|
|
|
package vault
|
|
|
|
|
|
|
|
import (
|
2015-03-11 18:43:36 +00:00
|
|
|
"bytes"
|
2015-03-10 00:45:34 +00:00
|
|
|
"errors"
|
2015-03-09 23:33:27 +00:00
|
|
|
"fmt"
|
2015-03-10 00:45:34 +00:00
|
|
|
"log"
|
2015-05-02 20:28:33 +00:00
|
|
|
"net/url"
|
2015-03-11 18:52:01 +00:00
|
|
|
"os"
|
2016-03-11 03:31:15 +00:00
|
|
|
"sort"
|
2015-04-03 01:05:23 +00:00
|
|
|
"strings"
|
2015-03-10 00:45:34 +00:00
|
|
|
"sync"
|
2015-04-08 23:43:17 +00:00
|
|
|
"time"
|
2015-03-09 23:33:27 +00:00
|
|
|
|
2015-04-08 23:43:17 +00:00
|
|
|
"github.com/armon/go-metrics"
|
2015-11-02 16:01:00 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2015-11-02 18:29:18 +00:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2015-12-16 17:56:20 +00:00
|
|
|
"github.com/hashicorp/go-uuid"
|
2015-03-27 20:45:13 +00:00
|
|
|
"github.com/hashicorp/vault/audit"
|
2015-04-28 21:59:43 +00:00
|
|
|
"github.com/hashicorp/vault/helper/mlock"
|
2016-04-06 00:30:38 +00:00
|
|
|
"github.com/hashicorp/vault/helper/strutil"
|
2015-03-15 21:53:41 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
2015-03-09 23:33:27 +00:00
|
|
|
"github.com/hashicorp/vault/physical"
|
2015-03-11 18:34:08 +00:00
|
|
|
"github.com/hashicorp/vault/shamir"
|
2015-03-09 23:33:27 +00:00
|
|
|
)
|
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
const (
|
2015-04-14 21:06:15 +00:00
|
|
|
// coreLockPath is the path used to acquire a coordinating lock
|
|
|
|
// for a highly-available deploy.
|
|
|
|
coreLockPath = "core/lock"
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// coreLeaderPrefix is the prefix used for the UUID that contains
|
|
|
|
// the currently elected leader.
|
|
|
|
coreLeaderPrefix = "core/leader/"
|
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
// lockRetryInterval is the interval we re-attempt to acquire the
|
|
|
|
// HA lock if an error is encountered
|
|
|
|
lockRetryInterval = 10 * time.Second
|
2015-05-28 23:11:31 +00:00
|
|
|
|
|
|
|
// keyRotateCheckInterval is how often a standby checks for a key
|
|
|
|
// rotation taking place.
|
|
|
|
keyRotateCheckInterval = 30 * time.Second
|
|
|
|
|
|
|
|
// keyRotateGracePeriod is how long we allow an upgrade path
|
|
|
|
// for standby instances before we delete the upgrade keys
|
|
|
|
keyRotateGracePeriod = 2 * time.Minute
|
2015-10-08 18:34:10 +00:00
|
|
|
|
|
|
|
// leaderPrefixCleanDelay is how long to wait between deletions
|
|
|
|
// of orphaned leader keys, to prevent slamming the backend.
|
|
|
|
leaderPrefixCleanDelay = 200 * time.Millisecond
|
2016-02-29 02:35:32 +00:00
|
|
|
|
|
|
|
// manualStepDownSleepPeriod is how long to sleep after a user-initiated
|
|
|
|
// step down of the active node, to prevent instantly regrabbing the lock
|
|
|
|
manualStepDownSleepPeriod = 10 * time.Second
|
2015-03-10 00:45:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrSealed is returned if an operation is performed on
|
|
|
|
// a sealed barrier. No operation is expected to succeed before unsealing
|
|
|
|
ErrSealed = errors.New("Vault is sealed")
|
|
|
|
|
2015-04-14 21:09:11 +00:00
|
|
|
// ErrStandby is returned if an operation is performed on
|
|
|
|
// a standby Vault. No operation is expected to succeed until active.
|
|
|
|
ErrStandby = errors.New("Vault is in standby mode")
|
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
// ErrAlreadyInit is returned if the core is already
|
|
|
|
// initialized. This prevents a re-initialization.
|
|
|
|
ErrAlreadyInit = errors.New("Vault is already initialized")
|
|
|
|
|
|
|
|
// ErrNotInit is returned if a non-initialized barrier
|
|
|
|
// is attempted to be unsealed.
|
|
|
|
ErrNotInit = errors.New("Vault is not initialized")
|
2015-03-16 22:28:50 +00:00
|
|
|
|
|
|
|
// ErrInternalError is returned when we don't want to leak
|
|
|
|
// any information about an internal error
|
|
|
|
ErrInternalError = errors.New("internal error")
|
2015-04-14 23:53:40 +00:00
|
|
|
|
|
|
|
// ErrHANotEnabled is returned if the operation only makes sense
|
|
|
|
// in an HA setting
|
|
|
|
ErrHANotEnabled = errors.New("Vault is not configured for highly-available mode")
|
2015-03-10 00:45:34 +00:00
|
|
|
)
|
|
|
|
|
2016-04-04 14:44:22 +00:00
|
|
|
// NonFatalError is an error that can be returned during NewCore that should be
|
|
|
|
// displayed but not cause a program exit
|
|
|
|
type NonFatalError struct {
|
|
|
|
Err error
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 14:44:22 +00:00
|
|
|
func (e *NonFatalError) WrappedErrors() []error {
|
|
|
|
return []error{e.Err}
|
2015-03-10 00:45:34 +00:00
|
|
|
}
|
|
|
|
|
2016-04-04 14:44:22 +00:00
|
|
|
func (e *NonFatalError) Error() string {
|
|
|
|
return e.Err.Error()
|
2015-03-10 00:45:34 +00:00
|
|
|
}
|
|
|
|
|
2015-03-12 18:20:27 +00:00
|
|
|
// ErrInvalidKey is returned if there is an error with a
|
|
|
|
// provided unseal key.
|
|
|
|
type ErrInvalidKey struct {
|
|
|
|
Reason string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *ErrInvalidKey) Error() string {
|
|
|
|
return fmt.Sprintf("invalid key: %v", e.Reason)
|
|
|
|
}
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// Core is used as the central manager of Vault activity. It is the primary point of
|
|
|
|
// interface for API handlers and is responsible for managing the logical and physical
|
|
|
|
// backends, router, security barrier, and audit trails.
|
|
|
|
type Core struct {
|
2015-04-14 21:06:15 +00:00
|
|
|
// HABackend may be available depending on the physical backend
|
|
|
|
ha physical.HABackend
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// AdvertiseAddr is the address we advertise as leader if held
|
|
|
|
advertiseAddr string
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// physical backend is the un-trusted backend with durable data
|
|
|
|
physical physical.Backend
|
|
|
|
|
2016-04-04 14:44:22 +00:00
|
|
|
// Our Seal, for seal configuration information
|
|
|
|
seal Seal
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// barrier is the security barrier wrapping the physical backend
|
|
|
|
barrier SecurityBarrier
|
|
|
|
|
|
|
|
// router is responsible for managing the mount points for logical backends.
|
|
|
|
router *Router
|
2015-03-10 00:45:34 +00:00
|
|
|
|
2015-03-18 22:21:41 +00:00
|
|
|
// logicalBackends is the mapping of backends to use for this core
|
|
|
|
logicalBackends map[string]logical.Factory
|
|
|
|
|
|
|
|
// credentialBackends is the mapping of backends to use for this core
|
2015-03-31 01:07:05 +00:00
|
|
|
credentialBackends map[string]logical.Factory
|
2015-03-15 23:25:38 +00:00
|
|
|
|
2015-03-27 20:45:13 +00:00
|
|
|
// auditBackends is the mapping of backends to use for this core
|
|
|
|
auditBackends map[string]audit.Factory
|
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
// stateLock protects mutable state
|
|
|
|
stateLock sync.RWMutex
|
|
|
|
sealed bool
|
|
|
|
|
2016-02-29 02:35:32 +00:00
|
|
|
standby bool
|
|
|
|
standbyDoneCh chan struct{}
|
|
|
|
standbyStopCh chan struct{}
|
|
|
|
manualStepDownCh chan struct{}
|
2015-04-14 21:06:15 +00:00
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
// unlockParts has the keys provided to Unseal until
|
|
|
|
// the threshold number of parts is available.
|
|
|
|
unlockParts [][]byte
|
|
|
|
|
2016-01-15 15:55:35 +00:00
|
|
|
// generateRootProgress holds the shares until we reach enough
|
2016-01-09 02:21:02 +00:00
|
|
|
// to verify the master key
|
2016-01-15 15:55:35 +00:00
|
|
|
generateRootConfig *GenerateRootConfig
|
|
|
|
generateRootProgress [][]byte
|
|
|
|
generateRootLock sync.Mutex
|
2016-01-09 02:21:02 +00:00
|
|
|
|
2016-04-04 14:44:22 +00:00
|
|
|
// These variables holds the config and shares we have until we reach
|
|
|
|
// enough to verify the appropriate master key. Note that the same lock is
|
|
|
|
// used; this isn't time-critical so this shouldn't be a problem.
|
|
|
|
barrierRekeyConfig *SealConfig
|
|
|
|
barrierRekeyProgress [][]byte
|
|
|
|
recoveryRekeyConfig *SealConfig
|
|
|
|
recoveryRekeyProgress [][]byte
|
|
|
|
rekeyLock sync.RWMutex
|
2015-05-28 18:40:01 +00:00
|
|
|
|
2015-03-11 22:19:41 +00:00
|
|
|
// mounts is loaded after unseal since it is a protected
|
|
|
|
// configuration
|
2015-03-17 22:28:01 +00:00
|
|
|
mounts *MountTable
|
2015-03-11 22:19:41 +00:00
|
|
|
|
2015-11-11 16:44:07 +00:00
|
|
|
// mountsLock is used to ensure that the mounts table does not
|
|
|
|
// change underneath a calling function
|
|
|
|
mountsLock sync.RWMutex
|
|
|
|
|
2015-03-18 22:46:07 +00:00
|
|
|
// auth is loaded after unseal since it is a protected
|
|
|
|
// configuration
|
2015-03-19 16:54:57 +00:00
|
|
|
auth *MountTable
|
2015-03-18 22:46:07 +00:00
|
|
|
|
2015-11-11 16:44:07 +00:00
|
|
|
// authLock is used to ensure that the auth table does not
|
|
|
|
// change underneath a calling function
|
|
|
|
authLock sync.RWMutex
|
|
|
|
|
2015-03-27 20:45:13 +00:00
|
|
|
// audit is loaded after unseal since it is a protected
|
|
|
|
// configuration
|
|
|
|
audit *MountTable
|
|
|
|
|
2015-11-11 16:44:07 +00:00
|
|
|
// auditLock is used to ensure that the audit table does not
|
|
|
|
// change underneath a calling function
|
|
|
|
auditLock sync.RWMutex
|
|
|
|
|
2015-03-31 20:22:40 +00:00
|
|
|
// auditBroker is used to ingest the audit events and fan
|
|
|
|
// out into the configured audit backends
|
|
|
|
auditBroker *AuditBroker
|
|
|
|
|
2015-09-04 20:58:12 +00:00
|
|
|
// systemBarrierView is the barrier view for the system backend
|
|
|
|
systemBarrierView *BarrierView
|
2015-03-12 19:41:12 +00:00
|
|
|
|
2015-04-08 20:35:32 +00:00
|
|
|
// expiration manager is used for managing LeaseIDs,
|
2015-03-12 19:44:22 +00:00
|
|
|
// renewal, expiration and revocation
|
|
|
|
expiration *ExpirationManager
|
|
|
|
|
2015-03-17 23:23:58 +00:00
|
|
|
// rollback manager is used to run rollbacks periodically
|
|
|
|
rollback *RollbackManager
|
|
|
|
|
2015-03-18 21:00:42 +00:00
|
|
|
// policy store is used to manage named ACL policies
|
2015-11-06 16:52:26 +00:00
|
|
|
policyStore *PolicyStore
|
2015-03-18 21:00:42 +00:00
|
|
|
|
2015-03-23 20:41:05 +00:00
|
|
|
// token store is used to manage authentication tokens
|
|
|
|
tokenStore *TokenStore
|
|
|
|
|
2015-04-08 23:43:17 +00:00
|
|
|
// metricsCh is used to stop the metrics streaming
|
|
|
|
metricsCh chan struct{}
|
|
|
|
|
2015-10-12 20:33:54 +00:00
|
|
|
// metricsMutex is used to prevent a race condition between
|
|
|
|
// metrics emission and sealing leading to a nil pointer
|
|
|
|
metricsMutex sync.Mutex
|
|
|
|
|
2015-08-27 14:50:16 +00:00
|
|
|
defaultLeaseTTL time.Duration
|
|
|
|
maxLeaseTTL time.Duration
|
2015-07-30 13:42:49 +00:00
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
logger *log.Logger
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 18:52:01 +00:00
|
|
|
// CoreConfig is used to parameterize a core
|
|
|
|
type CoreConfig struct {
|
2015-08-27 14:50:16 +00:00
|
|
|
LogicalBackends map[string]logical.Factory
|
|
|
|
CredentialBackends map[string]logical.Factory
|
|
|
|
AuditBackends map[string]audit.Factory
|
|
|
|
Physical physical.Backend
|
2015-12-18 15:34:54 +00:00
|
|
|
HAPhysical physical.HABackend // May be nil, which disables HA operations
|
2016-04-04 14:44:22 +00:00
|
|
|
Seal Seal
|
2015-12-18 15:34:54 +00:00
|
|
|
Logger *log.Logger
|
|
|
|
DisableCache bool // Disables the LRU cache on the physical backend
|
|
|
|
DisableMlock bool // Disables mlock syscall
|
|
|
|
CacheSize int // Custom cache size of zero for default
|
|
|
|
AdvertiseAddr string // Set as the leader address for HA
|
|
|
|
DefaultLeaseTTL time.Duration
|
|
|
|
MaxLeaseTTL time.Duration
|
2015-03-11 18:52:01 +00:00
|
|
|
}
|
|
|
|
|
2015-07-30 13:42:49 +00:00
|
|
|
// NewCore is used to construct a new core
|
2015-03-11 18:52:01 +00:00
|
|
|
func NewCore(conf *CoreConfig) (*Core, error) {
|
2015-12-14 22:58:30 +00:00
|
|
|
if conf.HAPhysical != nil && conf.AdvertiseAddr == "" {
|
2015-04-14 23:44:48 +00:00
|
|
|
return nil, fmt.Errorf("missing advertisement address")
|
|
|
|
}
|
2015-04-14 21:06:15 +00:00
|
|
|
|
2015-08-27 14:50:16 +00:00
|
|
|
if conf.DefaultLeaseTTL == 0 {
|
|
|
|
conf.DefaultLeaseTTL = defaultLeaseTTL
|
2015-07-30 13:42:49 +00:00
|
|
|
}
|
2015-08-27 14:50:16 +00:00
|
|
|
if conf.MaxLeaseTTL == 0 {
|
|
|
|
conf.MaxLeaseTTL = maxLeaseTTL
|
2015-07-30 13:42:49 +00:00
|
|
|
}
|
2015-08-27 14:50:16 +00:00
|
|
|
if conf.DefaultLeaseTTL > conf.MaxLeaseTTL {
|
|
|
|
return nil, fmt.Errorf("cannot have DefaultLeaseTTL larger than MaxLeaseTTL")
|
2015-07-30 13:42:49 +00:00
|
|
|
}
|
2015-08-20 17:14:13 +00:00
|
|
|
|
2015-05-02 20:28:33 +00:00
|
|
|
// Validate the advertise addr if its given to us
|
|
|
|
if conf.AdvertiseAddr != "" {
|
|
|
|
u, err := url.Parse(conf.AdvertiseAddr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("advertisement address is not valid url: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if u.Scheme == "" {
|
|
|
|
return nil, fmt.Errorf("advertisement address must include scheme (ex. 'http')")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-14 18:08:04 +00:00
|
|
|
// Wrap the backend in a cache unless disabled
|
|
|
|
if !conf.DisableCache {
|
|
|
|
_, isCache := conf.Physical.(*physical.Cache)
|
|
|
|
_, isInmem := conf.Physical.(*physical.InmemBackend)
|
|
|
|
if !isCache && !isInmem {
|
|
|
|
cache := physical.NewCache(conf.Physical, conf.CacheSize)
|
|
|
|
conf.Physical = cache
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-27 23:40:14 +00:00
|
|
|
if !conf.DisableMlock {
|
|
|
|
// Ensure our memory usage is locked into physical RAM
|
2015-04-28 21:59:43 +00:00
|
|
|
if err := mlock.LockMemory(); err != nil {
|
2015-04-29 01:56:16 +00:00
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"Failed to lock memory: %v\n\n"+
|
|
|
|
"This usually means that the mlock syscall is not available.\n"+
|
|
|
|
"Vault uses mlock to prevent memory from being swapped to\n"+
|
|
|
|
"disk. This requires root privileges as well as a machine\n"+
|
|
|
|
"that supports mlock. Please enable mlock on your system or\n"+
|
|
|
|
"disable Vault from using it. To disable Vault from using it,\n"+
|
|
|
|
"set the `disable_mlock` configuration option in your configuration\n"+
|
|
|
|
"file.",
|
|
|
|
err)
|
2015-04-27 23:40:14 +00:00
|
|
|
}
|
2015-04-19 20:42:47 +00:00
|
|
|
}
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// Construct a new AES-GCM barrier
|
2015-03-12 17:22:12 +00:00
|
|
|
barrier, err := NewAESGCMBarrier(conf.Physical)
|
2015-03-09 23:33:27 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("barrier setup failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-03-11 18:52:01 +00:00
|
|
|
// Make a default logger if not provided
|
2015-03-12 17:22:12 +00:00
|
|
|
if conf.Logger == nil {
|
|
|
|
conf.Logger = log.New(os.Stderr, "", log.LstdFlags)
|
2015-03-11 18:52:01 +00:00
|
|
|
}
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// Setup the core
|
|
|
|
c := &Core{
|
2015-12-14 22:58:30 +00:00
|
|
|
ha: conf.HAPhysical,
|
2015-08-27 14:50:16 +00:00
|
|
|
advertiseAddr: conf.AdvertiseAddr,
|
|
|
|
physical: conf.Physical,
|
2016-04-04 14:44:22 +00:00
|
|
|
seal: conf.Seal,
|
2015-08-27 14:50:16 +00:00
|
|
|
barrier: barrier,
|
|
|
|
router: NewRouter(),
|
|
|
|
sealed: true,
|
|
|
|
standby: true,
|
|
|
|
logger: conf.Logger,
|
|
|
|
defaultLeaseTTL: conf.DefaultLeaseTTL,
|
|
|
|
maxLeaseTTL: conf.MaxLeaseTTL,
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
2015-03-15 23:25:38 +00:00
|
|
|
|
|
|
|
// Setup the backends
|
2015-03-18 22:21:41 +00:00
|
|
|
logicalBackends := make(map[string]logical.Factory)
|
|
|
|
for k, f := range conf.LogicalBackends {
|
|
|
|
logicalBackends[k] = f
|
2015-03-15 23:25:38 +00:00
|
|
|
}
|
2015-09-19 22:24:53 +00:00
|
|
|
_, ok := logicalBackends["generic"]
|
|
|
|
if !ok {
|
|
|
|
logicalBackends["generic"] = PassthroughBackendFactory
|
|
|
|
}
|
2015-09-10 01:58:09 +00:00
|
|
|
logicalBackends["cubbyhole"] = CubbyholeBackendFactory
|
2015-09-04 20:58:12 +00:00
|
|
|
logicalBackends["system"] = func(config *logical.BackendConfig) (logical.Backend, error) {
|
|
|
|
return NewSystemBackend(c, config), nil
|
2015-03-15 23:25:38 +00:00
|
|
|
}
|
2015-03-18 22:21:41 +00:00
|
|
|
c.logicalBackends = logicalBackends
|
2015-03-15 23:25:38 +00:00
|
|
|
|
2015-03-31 01:07:05 +00:00
|
|
|
credentialBackends := make(map[string]logical.Factory)
|
2015-03-18 22:21:41 +00:00
|
|
|
for k, f := range conf.CredentialBackends {
|
|
|
|
credentialBackends[k] = f
|
|
|
|
}
|
2015-09-04 20:58:12 +00:00
|
|
|
credentialBackends["token"] = func(config *logical.BackendConfig) (logical.Backend, error) {
|
|
|
|
return NewTokenStore(c, config)
|
2015-03-19 02:11:52 +00:00
|
|
|
}
|
2015-03-18 22:21:41 +00:00
|
|
|
c.credentialBackends = credentialBackends
|
2015-03-27 20:45:13 +00:00
|
|
|
|
|
|
|
auditBackends := make(map[string]audit.Factory)
|
|
|
|
for k, f := range conf.AuditBackends {
|
|
|
|
auditBackends[k] = f
|
|
|
|
}
|
|
|
|
c.auditBackends = auditBackends
|
2016-04-04 14:44:22 +00:00
|
|
|
|
|
|
|
if c.seal == nil {
|
|
|
|
c.seal = &DefaultSeal{}
|
|
|
|
}
|
|
|
|
c.seal.SetCore(c)
|
|
|
|
|
|
|
|
// Attempt unsealing with stored keys; if there are no stored keys this
|
|
|
|
// returns nil, otherwise returns nil or an error
|
2016-04-14 01:12:58 +00:00
|
|
|
storedKeyErr := c.UnsealWithStoredKeys()
|
2016-04-04 14:44:22 +00:00
|
|
|
|
|
|
|
return c, storedKeyErr
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 01:23:59 +00:00
|
|
|
// Shutdown is invoked when the Vault instance is about to be terminated. It
|
|
|
|
// should not be accessible as part of an API call as it will cause an availability
|
|
|
|
// problem. It is only used to gracefully quit in the case of HA so that failover
|
|
|
|
// happens as quickly as possible.
|
|
|
|
func (c *Core) Shutdown() error {
|
|
|
|
c.stateLock.Lock()
|
|
|
|
defer c.stateLock.Unlock()
|
|
|
|
if c.sealed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seal the Vault, causes a leader stepdown
|
|
|
|
return c.sealInternal()
|
|
|
|
}
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// HandleRequest is used to handle a new incoming request
|
2015-05-09 18:47:46 +00:00
|
|
|
func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err error) {
|
2015-03-11 21:31:55 +00:00
|
|
|
c.stateLock.RLock()
|
|
|
|
defer c.stateLock.RUnlock()
|
|
|
|
if c.sealed {
|
|
|
|
return nil, ErrSealed
|
|
|
|
}
|
2015-04-14 23:11:39 +00:00
|
|
|
if c.standby {
|
2015-04-14 21:09:11 +00:00
|
|
|
return nil, ErrStandby
|
|
|
|
}
|
2015-03-11 21:31:55 +00:00
|
|
|
|
2016-01-22 15:13:49 +00:00
|
|
|
// Allowing writing to a path ending in / makes it extremely difficult to
|
|
|
|
// understand user intent for the filesystem-like backends (generic,
|
|
|
|
// cubbyhole) -- did they want a key named foo/ or did they want to write
|
|
|
|
// to a directory foo/ with no (or forgotten) key, or...? It also affects
|
|
|
|
// lookup, because paths ending in / are considered prefixes by some
|
|
|
|
// backends. Basically, it's all just terrible, so don't allow it.
|
2016-01-19 23:19:38 +00:00
|
|
|
if strings.HasSuffix(req.Path, "/") &&
|
|
|
|
(req.Operation == logical.UpdateOperation ||
|
|
|
|
req.Operation == logical.CreateOperation) {
|
|
|
|
return logical.ErrorResponse("cannot write to a path ending in '/'"), nil
|
|
|
|
}
|
|
|
|
|
2015-06-19 02:48:26 +00:00
|
|
|
var auth *logical.Auth
|
2015-03-31 03:26:39 +00:00
|
|
|
if c.router.LoginPath(req.Path) {
|
2015-06-19 02:48:26 +00:00
|
|
|
resp, auth, err = c.handleLoginRequest(req)
|
2015-03-31 03:26:39 +00:00
|
|
|
} else {
|
2015-06-19 02:48:26 +00:00
|
|
|
resp, auth, err = c.handleRequest(req)
|
2015-03-31 03:26:39 +00:00
|
|
|
}
|
2015-05-09 18:47:46 +00:00
|
|
|
|
|
|
|
// Ensure we don't leak internal data
|
|
|
|
if resp != nil {
|
|
|
|
if resp.Secret != nil {
|
|
|
|
resp.Secret.InternalData = nil
|
|
|
|
}
|
|
|
|
if resp.Auth != nil {
|
|
|
|
resp.Auth.InternalData = nil
|
|
|
|
}
|
|
|
|
}
|
2015-06-19 02:48:26 +00:00
|
|
|
|
|
|
|
// Create an audit trail of the response
|
|
|
|
if err := c.auditBroker.LogResponse(auth, req, resp, err); err != nil {
|
2015-10-03 01:31:46 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to audit response (request path: %s): %v",
|
|
|
|
req.Path, err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, ErrInternalError
|
|
|
|
}
|
|
|
|
|
2015-05-09 18:47:46 +00:00
|
|
|
return
|
2015-03-31 03:26:39 +00:00
|
|
|
}
|
|
|
|
|
2015-08-20 17:14:13 +00:00
|
|
|
func (c *Core) handleRequest(req *logical.Request) (retResp *logical.Response, retAuth *logical.Auth, retErr error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "handle_request"}, time.Now())
|
2015-06-19 01:30:18 +00:00
|
|
|
|
2015-03-31 16:59:02 +00:00
|
|
|
// Validate the token
|
2016-01-07 20:10:05 +00:00
|
|
|
auth, te, err := c.checkToken(req)
|
2015-08-20 17:14:13 +00:00
|
|
|
if te != nil {
|
|
|
|
defer func() {
|
|
|
|
// Attempt to use the token (decrement num_uses)
|
2015-09-14 14:35:00 +00:00
|
|
|
// If a secret was generated and num_uses is currently 1, it will be
|
2015-10-30 16:35:42 +00:00
|
|
|
// immediately revoked; in that case, don't return the leased
|
2015-09-14 14:35:00 +00:00
|
|
|
// credentials as they are now invalid.
|
2015-10-30 16:35:42 +00:00
|
|
|
if retResp != nil &&
|
|
|
|
te != nil && te.NumUses == 1 &&
|
|
|
|
retResp.Secret != nil &&
|
|
|
|
// Some backends return a TTL even without a Lease ID
|
|
|
|
retResp.Secret.LeaseID != "" {
|
|
|
|
retResp = logical.ErrorResponse("Secret cannot be returned; token had one use left, so leased credentials were immediately revoked.")
|
2015-09-14 14:35:00 +00:00
|
|
|
}
|
2015-08-20 17:14:13 +00:00
|
|
|
if err := c.tokenStore.UseToken(te); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to use token: %v", err)
|
2015-08-20 17:37:42 +00:00
|
|
|
retResp = nil
|
|
|
|
retAuth = nil
|
2015-08-20 17:14:13 +00:00
|
|
|
retErr = ErrInternalError
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2015-03-24 18:37:07 +00:00
|
|
|
if err != nil {
|
2015-03-31 16:59:02 +00:00
|
|
|
// If it is an internal error we return that, otherwise we
|
|
|
|
// return invalid request so that the status codes can be correct
|
2015-04-01 21:11:26 +00:00
|
|
|
var errType error
|
2015-03-31 16:59:02 +00:00
|
|
|
switch err {
|
2015-04-01 21:11:26 +00:00
|
|
|
case ErrInternalError, logical.ErrPermissionDenied:
|
2015-03-31 16:59:02 +00:00
|
|
|
errType = err
|
2015-04-01 21:11:26 +00:00
|
|
|
default:
|
|
|
|
errType = logical.ErrInvalidRequest
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
2015-03-24 18:37:07 +00:00
|
|
|
|
2015-06-19 01:30:18 +00:00
|
|
|
if err := c.auditBroker.LogRequest(auth, req, err); err != nil {
|
2015-10-03 01:31:46 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to audit request with path (%s): %v",
|
|
|
|
req.Path, err)
|
2015-06-19 01:30:18 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 02:48:26 +00:00
|
|
|
return logical.ErrorResponse(err.Error()), nil, errType
|
2015-03-24 18:37:07 +00:00
|
|
|
}
|
2015-03-11 21:31:55 +00:00
|
|
|
|
2015-04-15 21:12:34 +00:00
|
|
|
// Attach the display name
|
|
|
|
req.DisplayName = auth.DisplayName
|
|
|
|
|
2015-04-01 21:33:48 +00:00
|
|
|
// Create an audit trail of the request
|
2015-06-29 22:27:28 +00:00
|
|
|
if err := c.auditBroker.LogRequest(auth, req, nil); err != nil {
|
2015-10-03 01:31:46 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to audit request with path (%s): %v",
|
|
|
|
req.Path, err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, auth, ErrInternalError
|
2015-04-01 21:33:48 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 21:31:55 +00:00
|
|
|
// Route the request
|
2015-03-16 22:28:50 +00:00
|
|
|
resp, err := c.router.Route(req)
|
|
|
|
|
2015-03-19 22:11:42 +00:00
|
|
|
// If there is a secret, we must register it with the expiration manager.
|
2015-05-16 00:47:39 +00:00
|
|
|
// We exclude renewal of a lease, since it does not need to be re-registered
|
|
|
|
if resp != nil && resp.Secret != nil && !strings.HasPrefix(req.Path, "sys/renew/") {
|
2015-08-28 21:25:09 +00:00
|
|
|
// Get the SystemView for the mount
|
2015-09-04 20:58:12 +00:00
|
|
|
sysView := c.router.MatchingSystemView(req.Path)
|
|
|
|
if sysView == nil {
|
|
|
|
c.logger.Println("[ERR] core: unable to retrieve system view from router")
|
2015-08-28 21:25:09 +00:00
|
|
|
return nil, auth, ErrInternalError
|
|
|
|
}
|
|
|
|
|
2015-04-03 22:42:34 +00:00
|
|
|
// Apply the default lease if none given
|
2015-08-21 00:47:17 +00:00
|
|
|
if resp.Secret.TTL == 0 {
|
2015-10-10 00:26:39 +00:00
|
|
|
resp.Secret.TTL = sysView.DefaultLeaseTTL()
|
2015-04-03 22:42:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Limit the lease duration
|
2015-09-10 19:09:34 +00:00
|
|
|
maxTTL := sysView.MaxLeaseTTL()
|
2015-09-02 19:56:58 +00:00
|
|
|
if resp.Secret.TTL > maxTTL {
|
|
|
|
resp.Secret.TTL = maxTTL
|
2015-04-03 22:42:34 +00:00
|
|
|
}
|
|
|
|
|
2015-09-19 22:24:53 +00:00
|
|
|
// Generic mounts should return the TTL but not register
|
|
|
|
// for a lease as this provides a massive slowdown
|
|
|
|
registerLease := true
|
2015-09-21 20:57:41 +00:00
|
|
|
matchingBackend := c.router.MatchingBackend(req.Path)
|
|
|
|
if matchingBackend == nil {
|
|
|
|
c.logger.Println("[ERR] core: unable to retrieve generic backend from router")
|
|
|
|
return nil, auth, ErrInternalError
|
|
|
|
}
|
|
|
|
if ptbe, ok := matchingBackend.(*PassthroughBackend); ok {
|
|
|
|
if !ptbe.GeneratesLeases() {
|
2015-09-19 22:24:53 +00:00
|
|
|
registerLease = false
|
|
|
|
resp.Secret.Renewable = false
|
|
|
|
}
|
|
|
|
}
|
2015-09-21 20:57:41 +00:00
|
|
|
|
2015-09-19 22:24:53 +00:00
|
|
|
if registerLease {
|
|
|
|
leaseID, err := c.expiration.Register(req, resp)
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf(
|
|
|
|
"[ERR] core: failed to register lease "+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s): %v", req.Path, err)
|
2015-09-19 22:24:53 +00:00
|
|
|
return nil, auth, ErrInternalError
|
|
|
|
}
|
|
|
|
resp.Secret.LeaseID = leaseID
|
|
|
|
}
|
2015-03-16 22:28:50 +00:00
|
|
|
}
|
|
|
|
|
2015-04-03 01:05:23 +00:00
|
|
|
// Only the token store is allowed to return an auth block, for any
|
2015-06-17 22:22:50 +00:00
|
|
|
// other request this is an internal error. We exclude renewal of a token,
|
|
|
|
// since it does not need to be re-registered
|
2016-03-31 18:52:49 +00:00
|
|
|
if resp != nil && resp.Auth != nil && !strings.HasPrefix(req.Path, "auth/token/renew") {
|
2015-04-03 01:05:23 +00:00
|
|
|
if !strings.HasPrefix(req.Path, "auth/token/") {
|
|
|
|
c.logger.Printf(
|
|
|
|
"[ERR] core: unexpected Auth response for non-token backend "+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s)", req.Path)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, auth, ErrInternalError
|
2015-04-03 01:05:23 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 11:56:09 +00:00
|
|
|
// Register with the expiration manager. We use the token's actual path
|
|
|
|
// here because roles allow suffixes.
|
|
|
|
te, err := c.tokenStore.Lookup(resp.Auth.ClientToken)
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to lookup token: %v", err)
|
|
|
|
return nil, nil, ErrInternalError
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.expiration.RegisterAuth(te.Path, resp.Auth); err != nil {
|
2015-04-03 01:05:23 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to register token lease "+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s): %v", req.Path, err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, auth, ErrInternalError
|
2015-04-03 01:05:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-16 22:28:50 +00:00
|
|
|
// Return the response and error
|
2015-06-19 02:48:26 +00:00
|
|
|
return resp, auth, err
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 03:26:39 +00:00
|
|
|
// handleLoginRequest is used to handle a login request, which is an
|
|
|
|
// unauthenticated request to the backend.
|
2015-06-19 02:48:26 +00:00
|
|
|
func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, *logical.Auth, error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "handle_login_request"}, time.Now())
|
|
|
|
|
2015-04-01 21:48:37 +00:00
|
|
|
// Create an audit trail of the request, auth is not available on login requests
|
2015-06-29 22:27:28 +00:00
|
|
|
if err := c.auditBroker.LogRequest(nil, req, nil); err != nil {
|
2015-10-03 01:31:46 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to audit request with path %s: %v",
|
|
|
|
req.Path, err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, nil, ErrInternalError
|
2015-04-01 21:48:37 +00:00
|
|
|
}
|
|
|
|
|
2015-03-23 20:56:43 +00:00
|
|
|
// Route the request
|
2015-03-31 03:26:39 +00:00
|
|
|
resp, err := c.router.Route(req)
|
2015-03-23 20:56:43 +00:00
|
|
|
|
2015-05-09 18:51:58 +00:00
|
|
|
// A login request should never return a secret!
|
|
|
|
if resp != nil && resp.Secret != nil {
|
|
|
|
c.logger.Printf("[ERR] core: unexpected Secret response for login path"+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s)", req.Path)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, nil, ErrInternalError
|
2015-05-09 18:51:58 +00:00
|
|
|
}
|
|
|
|
|
2015-03-31 03:26:39 +00:00
|
|
|
// If the response generated an authentication, then generate the token
|
2015-04-01 21:48:37 +00:00
|
|
|
var auth *logical.Auth
|
2015-03-31 03:26:39 +00:00
|
|
|
if resp != nil && resp.Auth != nil {
|
2015-04-03 00:52:11 +00:00
|
|
|
auth = resp.Auth
|
|
|
|
|
2015-04-15 21:12:34 +00:00
|
|
|
// Determine the source of the login
|
|
|
|
source := c.router.MatchingMount(req.Path)
|
2015-04-16 00:19:59 +00:00
|
|
|
source = strings.TrimPrefix(source, credentialRoutePrefix)
|
2015-04-15 21:12:34 +00:00
|
|
|
source = strings.Replace(source, "/", "-", -1)
|
|
|
|
|
|
|
|
// Prepend the source to the display name
|
|
|
|
auth.DisplayName = strings.TrimSuffix(source+auth.DisplayName, "-")
|
|
|
|
|
2015-09-18 20:33:52 +00:00
|
|
|
sysView := c.router.MatchingSystemView(req.Path)
|
|
|
|
if sysView == nil {
|
|
|
|
c.logger.Printf("[ERR] core: unable to look up sys view for login path"+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s)", req.Path)
|
2015-09-18 20:33:52 +00:00
|
|
|
return nil, nil, ErrInternalError
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the default lease if non-provided, root tokens are exempt
|
2016-04-06 00:30:38 +00:00
|
|
|
if auth.TTL == 0 && !strutil.StrListContains(auth.Policies, "root") {
|
2015-09-18 20:33:52 +00:00
|
|
|
auth.TTL = sysView.DefaultLeaseTTL()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Limit the lease duration
|
|
|
|
if auth.TTL > sysView.MaxLeaseTTL() {
|
|
|
|
auth.TTL = sysView.MaxLeaseTTL()
|
|
|
|
}
|
|
|
|
|
2015-03-23 20:56:43 +00:00
|
|
|
// Generate a token
|
|
|
|
te := TokenEntry{
|
2015-09-18 20:33:52 +00:00
|
|
|
Path: req.Path,
|
|
|
|
Policies: auth.Policies,
|
|
|
|
Meta: auth.Metadata,
|
|
|
|
DisplayName: auth.DisplayName,
|
|
|
|
CreationTime: time.Now().Unix(),
|
|
|
|
TTL: auth.TTL,
|
2015-03-23 20:56:43 +00:00
|
|
|
}
|
2015-09-18 20:33:52 +00:00
|
|
|
|
2016-04-06 00:30:38 +00:00
|
|
|
if strutil.StrListSubset(te.Policies, []string{"root"}) {
|
2016-03-15 18:05:25 +00:00
|
|
|
te.Policies = []string{"root"}
|
|
|
|
} else {
|
|
|
|
// Use a map to filter out/prevent duplicates
|
|
|
|
policyMap := map[string]bool{}
|
|
|
|
for _, policy := range te.Policies {
|
|
|
|
if policy == "" {
|
|
|
|
// Don't allow a policy with no name, even though it is a valid
|
|
|
|
// slice member
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
policyMap[policy] = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the default policy
|
|
|
|
policyMap["default"] = true
|
2016-03-11 03:31:15 +00:00
|
|
|
|
2016-03-15 18:05:25 +00:00
|
|
|
te.Policies = []string{}
|
|
|
|
for k, _ := range policyMap {
|
|
|
|
te.Policies = append(te.Policies, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(te.Policies)
|
2015-11-06 22:27:15 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 14:59:26 +00:00
|
|
|
if err := c.tokenStore.create(&te); err != nil {
|
2015-03-23 20:56:43 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to create token: %v", err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, auth, ErrInternalError
|
2015-03-23 20:56:43 +00:00
|
|
|
}
|
|
|
|
|
2016-03-09 22:15:33 +00:00
|
|
|
// Populate the client token and accessor
|
2015-09-10 18:43:47 +00:00
|
|
|
auth.ClientToken = te.ID
|
2016-03-09 22:15:33 +00:00
|
|
|
auth.Accessor = te.Accessor
|
2016-03-15 18:05:25 +00:00
|
|
|
auth.Policies = te.Policies
|
2015-03-23 20:56:43 +00:00
|
|
|
|
2015-04-03 00:52:11 +00:00
|
|
|
// Register with the expiration manager
|
2016-04-14 11:56:09 +00:00
|
|
|
if err := c.expiration.RegisterAuth(te.Path, auth); err != nil {
|
2015-04-03 00:52:11 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to register token lease "+
|
2015-10-03 01:31:46 +00:00
|
|
|
"(request path: %s): %v", req.Path, err)
|
2015-06-19 02:48:26 +00:00
|
|
|
return nil, auth, ErrInternalError
|
2015-04-03 00:52:11 +00:00
|
|
|
}
|
2015-04-15 21:12:34 +00:00
|
|
|
|
|
|
|
// Attach the display name, might be used by audit backends
|
|
|
|
req.DisplayName = auth.DisplayName
|
2015-03-23 20:56:43 +00:00
|
|
|
}
|
2015-03-31 03:26:39 +00:00
|
|
|
|
2015-06-19 02:48:26 +00:00
|
|
|
return resp, auth, err
|
2015-03-23 20:56:43 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 20:10:05 +00:00
|
|
|
func (c *Core) fetchACLandTokenEntry(req *logical.Request) (*ACL, *TokenEntry, error) {
|
|
|
|
defer metrics.MeasureSince([]string{"core", "fetch_acl_and_token"}, time.Now())
|
2015-04-08 23:43:17 +00:00
|
|
|
|
2015-03-31 16:59:02 +00:00
|
|
|
// Ensure there is a client token
|
2016-01-07 20:10:05 +00:00
|
|
|
if req.ClientToken == "" {
|
2015-08-20 17:14:13 +00:00
|
|
|
return nil, nil, fmt.Errorf("missing client token")
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 12:21:47 +00:00
|
|
|
if c.tokenStore == nil {
|
|
|
|
c.logger.Printf("[ERR] core: token store is unavailable")
|
|
|
|
return nil, nil, ErrInternalError
|
|
|
|
}
|
|
|
|
|
2015-03-31 16:59:02 +00:00
|
|
|
// Resolve the token policy
|
2016-01-07 20:10:05 +00:00
|
|
|
te, err := c.tokenStore.Lookup(req.ClientToken)
|
2015-03-31 16:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to lookup token: %v", err)
|
2015-08-20 17:14:13 +00:00
|
|
|
return nil, nil, ErrInternalError
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the token is valid
|
|
|
|
if te == nil {
|
2015-08-20 17:14:13 +00:00
|
|
|
return nil, nil, logical.ErrPermissionDenied
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the corresponding ACL object
|
2015-11-06 16:52:26 +00:00
|
|
|
acl, err := c.policyStore.ACL(te.Policies...)
|
2015-03-31 16:59:02 +00:00
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to construct ACL: %v", err)
|
2015-08-20 17:14:13 +00:00
|
|
|
return nil, nil, ErrInternalError
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 20:10:05 +00:00
|
|
|
return acl, te, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Core) checkToken(req *logical.Request) (*logical.Auth, *TokenEntry, error) {
|
|
|
|
defer metrics.MeasureSince([]string{"core", "check_token"}, time.Now())
|
|
|
|
|
|
|
|
acl, te, err := c.fetchACLandTokenEntry(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2015-03-31 16:59:02 +00:00
|
|
|
// Check if this is a root protected path
|
2016-01-07 20:10:05 +00:00
|
|
|
rootPath := c.router.RootPath(req.Path)
|
|
|
|
|
2016-01-12 20:13:54 +00:00
|
|
|
// When we receive a write of either type, rather than require clients to
|
|
|
|
// PUT/POST and trust the operation, we ask the backend to give us the real
|
|
|
|
// skinny -- if the backend implements an existence check, it can tell us
|
|
|
|
// whether a particular resource exists. Then we can mark it as an update
|
|
|
|
// or creation as appropriate.
|
2016-01-07 20:10:05 +00:00
|
|
|
if req.Operation == logical.CreateOperation || req.Operation == logical.UpdateOperation {
|
2016-01-12 20:09:16 +00:00
|
|
|
checkExists, resourceExists, err := c.router.RouteExistenceCheck(req)
|
2016-01-23 19:05:09 +00:00
|
|
|
switch err {
|
|
|
|
case logical.ErrUnsupportedPath:
|
|
|
|
// fail later via bad path to avoid confusing items in the log
|
|
|
|
checkExists = false
|
|
|
|
case nil:
|
|
|
|
// Continue on
|
|
|
|
default:
|
2016-01-07 20:10:05 +00:00
|
|
|
c.logger.Printf("[ERR] core: failed to run existence check: %v", err)
|
|
|
|
return nil, nil, ErrInternalError
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
2016-01-12 20:09:16 +00:00
|
|
|
case checkExists == false:
|
2016-01-07 20:10:05 +00:00
|
|
|
// No existence check, so always treate it as an update operation, which is how it is pre 0.5
|
|
|
|
req.Operation = logical.UpdateOperation
|
2016-01-12 20:09:16 +00:00
|
|
|
case resourceExists == true:
|
2016-01-07 20:10:05 +00:00
|
|
|
// It exists, so force an update operation
|
|
|
|
req.Operation = logical.UpdateOperation
|
2016-01-12 20:09:16 +00:00
|
|
|
case resourceExists == false:
|
2016-01-07 20:10:05 +00:00
|
|
|
// It doesn't exist, force a create operation
|
|
|
|
req.Operation = logical.CreateOperation
|
|
|
|
default:
|
2016-01-12 20:09:16 +00:00
|
|
|
panic("unreachable code")
|
2016-01-07 20:10:05 +00:00
|
|
|
}
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the standard non-root ACLs
|
2016-01-07 20:10:05 +00:00
|
|
|
allowed, rootPrivs := acl.AllowOperation(req.Operation, req.Path)
|
|
|
|
if !allowed {
|
|
|
|
return nil, nil, logical.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
if rootPath && !rootPrivs {
|
2015-08-20 17:14:13 +00:00
|
|
|
return nil, nil, logical.ErrPermissionDenied
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
2015-04-01 21:33:48 +00:00
|
|
|
// Create the auth response
|
|
|
|
auth := &logical.Auth{
|
2016-01-07 20:10:05 +00:00
|
|
|
ClientToken: req.ClientToken,
|
2015-04-01 21:33:48 +00:00
|
|
|
Policies: te.Policies,
|
|
|
|
Metadata: te.Meta,
|
2015-04-15 21:12:34 +00:00
|
|
|
DisplayName: te.DisplayName,
|
2015-04-01 21:33:48 +00:00
|
|
|
}
|
2015-08-20 17:14:13 +00:00
|
|
|
return auth, te, nil
|
2015-03-31 16:59:02 +00:00
|
|
|
}
|
|
|
|
|
2015-03-09 23:33:27 +00:00
|
|
|
// Sealed checks if the Vault is current sealed
|
|
|
|
func (c *Core) Sealed() (bool, error) {
|
2015-03-10 00:45:34 +00:00
|
|
|
c.stateLock.RLock()
|
|
|
|
defer c.stateLock.RUnlock()
|
|
|
|
return c.sealed, nil
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
|
|
|
|
2015-04-14 21:09:11 +00:00
|
|
|
// Standby checks if the Vault is in standby mode
|
|
|
|
func (c *Core) Standby() (bool, error) {
|
|
|
|
c.stateLock.RLock()
|
|
|
|
defer c.stateLock.RUnlock()
|
2015-04-14 23:11:39 +00:00
|
|
|
return c.standby, nil
|
2015-04-14 21:09:11 +00:00
|
|
|
}
|
|
|
|
|
2015-04-14 23:53:40 +00:00
|
|
|
// Leader is used to get the current active leader
|
|
|
|
func (c *Core) Leader() (bool, string, error) {
|
|
|
|
c.stateLock.RLock()
|
|
|
|
defer c.stateLock.RUnlock()
|
|
|
|
// Check if HA enabled
|
|
|
|
if c.ha == nil {
|
|
|
|
return false, "", ErrHANotEnabled
|
|
|
|
}
|
|
|
|
|
2015-04-20 19:19:09 +00:00
|
|
|
// Check if sealed
|
|
|
|
if c.sealed {
|
|
|
|
return false, "", ErrSealed
|
|
|
|
}
|
|
|
|
|
2015-04-14 23:53:40 +00:00
|
|
|
// Check if we are the leader
|
|
|
|
if !c.standby {
|
|
|
|
return true, c.advertiseAddr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize a lock
|
|
|
|
lock, err := c.ha.LockWith(coreLockPath, "read")
|
|
|
|
if err != nil {
|
|
|
|
return false, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the value
|
|
|
|
held, value, err := lock.Value()
|
|
|
|
if err != nil {
|
|
|
|
return false, "", err
|
|
|
|
}
|
|
|
|
if !held {
|
|
|
|
return false, "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value is the UUID of the leader, fetch the key
|
|
|
|
key := coreLeaderPrefix + value
|
|
|
|
entry, err := c.barrier.Get(key)
|
|
|
|
if err != nil {
|
|
|
|
return false, "", err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return false, "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leader address is in the entry
|
|
|
|
return false, string(entry.Value), nil
|
|
|
|
}
|
|
|
|
|
2015-03-11 18:52:01 +00:00
|
|
|
// SecretProgress returns the number of keys provided so far
|
|
|
|
func (c *Core) SecretProgress() int {
|
|
|
|
c.stateLock.RLock()
|
|
|
|
defer c.stateLock.RUnlock()
|
|
|
|
return len(c.unlockParts)
|
|
|
|
}
|
|
|
|
|
2015-10-28 19:59:39 +00:00
|
|
|
// ResetUnsealProcess removes the current unlock parts from memory, to reset
|
|
|
|
// the unsealing process
|
|
|
|
func (c *Core) ResetUnsealProcess() {
|
|
|
|
c.stateLock.Lock()
|
|
|
|
defer c.stateLock.Unlock()
|
|
|
|
if !c.sealed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.unlockParts = nil
|
|
|
|
}
|
|
|
|
|
2015-03-15 00:47:11 +00:00
|
|
|
// Unseal is used to provide one of the key parts to unseal the Vault.
|
2015-03-15 01:25:36 +00:00
|
|
|
//
|
|
|
|
// They key given as a parameter will automatically be zerod after
|
|
|
|
// this method is done with it. If you want to keep the key around, a copy
|
|
|
|
// should be made.
|
|
|
|
func (c *Core) Unseal(key []byte) (bool, error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "unseal"}, time.Now())
|
|
|
|
|
2015-03-12 18:20:27 +00:00
|
|
|
// Verify the key length
|
|
|
|
min, max := c.barrier.KeyLength()
|
|
|
|
max += shamir.ShareOverhead
|
|
|
|
if len(key) < min {
|
|
|
|
return false, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
|
|
|
|
}
|
|
|
|
if len(key) > max {
|
|
|
|
return false, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
|
|
|
|
}
|
|
|
|
|
2015-03-11 18:43:36 +00:00
|
|
|
// Get the seal configuration
|
2016-04-04 14:44:22 +00:00
|
|
|
config, err := c.seal.BarrierConfig()
|
2015-03-11 18:43:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the barrier is initialized
|
|
|
|
if config == nil {
|
|
|
|
return false, ErrNotInit
|
|
|
|
}
|
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
c.stateLock.Lock()
|
|
|
|
defer c.stateLock.Unlock()
|
|
|
|
|
2015-03-11 18:43:36 +00:00
|
|
|
// Check if already unsealed
|
|
|
|
if !c.sealed {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we already have this piece
|
|
|
|
for _, existing := range c.unlockParts {
|
|
|
|
if bytes.Equal(existing, key) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store this key
|
|
|
|
c.unlockParts = append(c.unlockParts, key)
|
|
|
|
|
|
|
|
// Check if we don't have enough keys to unlock
|
|
|
|
if len(c.unlockParts) < config.SecretThreshold {
|
|
|
|
c.logger.Printf("[DEBUG] core: cannot unseal, have %d of %d keys",
|
|
|
|
len(c.unlockParts), config.SecretThreshold)
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recover the master key
|
|
|
|
var masterKey []byte
|
|
|
|
if config.SecretThreshold == 1 {
|
|
|
|
masterKey = c.unlockParts[0]
|
|
|
|
c.unlockParts = nil
|
|
|
|
} else {
|
|
|
|
masterKey, err = shamir.Combine(c.unlockParts)
|
|
|
|
c.unlockParts = nil
|
|
|
|
if err != nil {
|
|
|
|
return false, fmt.Errorf("failed to compute master key: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
defer memzero(masterKey)
|
|
|
|
|
|
|
|
// Attempt to unlock
|
|
|
|
if err := c.barrier.Unseal(masterKey); err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2015-03-13 18:34:40 +00:00
|
|
|
c.logger.Printf("[INFO] core: vault is unsealed")
|
2015-03-11 18:43:36 +00:00
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
// Do post-unseal setup if HA is not enabled
|
|
|
|
if c.ha == nil {
|
|
|
|
if err := c.postUnseal(); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err)
|
|
|
|
c.barrier.Seal()
|
|
|
|
c.logger.Printf("[WARN] core: vault is sealed")
|
|
|
|
return false, err
|
|
|
|
}
|
2015-12-17 18:48:08 +00:00
|
|
|
c.standby = false
|
2015-04-14 21:06:15 +00:00
|
|
|
} else {
|
|
|
|
// Go to standby mode, wait until we are active to unseal
|
|
|
|
c.standbyDoneCh = make(chan struct{})
|
|
|
|
c.standbyStopCh = make(chan struct{})
|
2016-02-29 02:35:32 +00:00
|
|
|
c.manualStepDownCh = make(chan struct{})
|
|
|
|
go c.runStandby(c.standbyDoneCh, c.standbyStopCh, c.manualStepDownCh)
|
2015-03-11 22:19:41 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 18:43:36 +00:00
|
|
|
// Success!
|
|
|
|
c.sealed = false
|
2016-04-23 02:55:17 +00:00
|
|
|
if c.ha != nil {
|
|
|
|
sd, ok := c.ha.(physical.ServiceDiscovery)
|
|
|
|
if ok {
|
|
|
|
go func() {
|
2016-04-24 14:04:51 +00:00
|
|
|
if err := sd.AdvertiseSealed(false); err != nil {
|
2016-04-23 02:55:17 +00:00
|
|
|
c.logger.Printf("[WARN] core: failed to advertise unsealed status: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
2015-03-11 18:43:36 +00:00
|
|
|
return true, nil
|
2015-03-09 23:33:27 +00:00
|
|
|
}
|
2015-03-10 00:45:34 +00:00
|
|
|
|
2015-03-13 18:16:24 +00:00
|
|
|
// Seal is used to re-seal the Vault. This requires the Vault to
|
2015-03-10 00:45:34 +00:00
|
|
|
// be unsealed again to perform any further operations.
|
2015-08-20 17:14:13 +00:00
|
|
|
func (c *Core) Seal(token string) (retErr error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "seal"}, time.Now())
|
2016-02-27 00:43:55 +00:00
|
|
|
|
2015-03-10 00:45:34 +00:00
|
|
|
c.stateLock.Lock()
|
|
|
|
defer c.stateLock.Unlock()
|
|
|
|
if c.sealed {
|
|
|
|
return nil
|
|
|
|
}
|
2015-03-31 16:59:02 +00:00
|
|
|
|
|
|
|
// Validate the token is a root token
|
2016-01-07 20:10:05 +00:00
|
|
|
req := &logical.Request{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Path: "sys/seal",
|
|
|
|
ClientToken: token,
|
|
|
|
}
|
|
|
|
|
2016-02-29 02:35:32 +00:00
|
|
|
acl, te, err := c.fetchACLandTokenEntry(req)
|
2015-03-31 16:59:02 +00:00
|
|
|
if err != nil {
|
2016-02-03 16:35:47 +00:00
|
|
|
// Since there is no token store in standby nodes, sealing cannot
|
|
|
|
// be done. Ideally, the request has to be forwarded to leader node
|
|
|
|
// for validation and the operation should be performed. But for now,
|
|
|
|
// just returning with an error and recommending a vault restart, which
|
|
|
|
// essentially does the same thing.
|
2016-02-03 15:58:33 +00:00
|
|
|
if c.standby {
|
2016-02-29 02:35:32 +00:00
|
|
|
c.logger.Printf("[ERR] core: vault cannot seal when in standby mode; please restart instead")
|
|
|
|
return errors.New("vault cannot seal when in standby mode; please restart instead")
|
2016-02-03 15:58:33 +00:00
|
|
|
}
|
2015-03-31 16:59:02 +00:00
|
|
|
return err
|
|
|
|
}
|
2016-02-29 02:35:32 +00:00
|
|
|
// Attempt to use the token (decrement num_uses)
|
|
|
|
// If we can't, we still continue attempting the seal, so long as the token
|
|
|
|
// has appropriate permissions
|
|
|
|
if te != nil {
|
|
|
|
if err := c.tokenStore.UseToken(te); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to use token: %v", err)
|
|
|
|
retErr = ErrInternalError
|
|
|
|
}
|
|
|
|
}
|
2015-03-31 16:59:02 +00:00
|
|
|
|
2016-01-07 20:10:05 +00:00
|
|
|
// Verify that this operation is allowed
|
|
|
|
allowed, rootPrivs := acl.AllowOperation(req.Operation, req.Path)
|
|
|
|
if !allowed {
|
|
|
|
return logical.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
|
|
|
|
// We always require root privileges for this operation
|
|
|
|
if !rootPrivs {
|
|
|
|
return logical.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
|
2016-02-29 02:35:32 +00:00
|
|
|
//Seal the Vault
|
2015-08-20 17:37:42 +00:00
|
|
|
err = c.sealInternal()
|
|
|
|
if err == nil && retErr == ErrInternalError {
|
|
|
|
c.logger.Printf("[ERR] core: core is successfully sealed but another error occurred during the operation")
|
|
|
|
} else {
|
|
|
|
retErr = err
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
2015-06-18 01:23:59 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 02:35:32 +00:00
|
|
|
// StepDown is used to step down from leadership
|
|
|
|
func (c *Core) StepDown(token string) error {
|
|
|
|
defer metrics.MeasureSince([]string{"core", "step_down"}, time.Now())
|
|
|
|
|
|
|
|
c.stateLock.Lock()
|
|
|
|
defer c.stateLock.Unlock()
|
|
|
|
if c.sealed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if c.ha == nil || c.standby {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the token is a root token
|
|
|
|
req := &logical.Request{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Path: "sys/step-down",
|
|
|
|
ClientToken: token,
|
|
|
|
}
|
|
|
|
|
|
|
|
acl, te, err := c.fetchACLandTokenEntry(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Attempt to use the token (decrement num_uses)
|
|
|
|
if te != nil {
|
|
|
|
if err := c.tokenStore.UseToken(te); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to use token: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that this operation is allowed
|
|
|
|
allowed, rootPrivs := acl.AllowOperation(req.Operation, req.Path)
|
|
|
|
if !allowed {
|
|
|
|
return logical.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
|
|
|
|
// We always require root privileges for this operation
|
|
|
|
if !rootPrivs {
|
|
|
|
return logical.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
|
2016-03-03 17:29:30 +00:00
|
|
|
select {
|
|
|
|
case c.manualStepDownCh <- struct{}{}:
|
|
|
|
default:
|
|
|
|
c.logger.Printf("[WARN] core: manual step-down operation already queued")
|
|
|
|
}
|
2016-02-29 02:35:32 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-27 00:43:55 +00:00
|
|
|
// sealInternal is an internal method used to seal the vault. It does not do
|
|
|
|
// any authorization checking. The stateLock must be held prior to calling.
|
2015-06-18 01:23:59 +00:00
|
|
|
func (c *Core) sealInternal() error {
|
2015-04-14 21:06:15 +00:00
|
|
|
// Enable that we are sealed to prevent furthur transactions
|
2015-03-10 00:45:34 +00:00
|
|
|
c.sealed = true
|
2015-03-13 18:16:24 +00:00
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
// Do pre-seal teardown if HA is not enabled
|
|
|
|
if c.ha == nil {
|
|
|
|
if err := c.preSeal(); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err)
|
|
|
|
return fmt.Errorf("internal error")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Signal the standby goroutine to shutdown, wait for completion
|
|
|
|
close(c.standbyStopCh)
|
|
|
|
|
|
|
|
// Release the lock while we wait to avoid deadlocking
|
|
|
|
c.stateLock.Unlock()
|
|
|
|
<-c.standbyDoneCh
|
|
|
|
c.stateLock.Lock()
|
2015-03-13 18:16:24 +00:00
|
|
|
}
|
|
|
|
|
2015-03-13 18:34:40 +00:00
|
|
|
if err := c.barrier.Seal(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.logger.Printf("[INFO] core: vault is sealed")
|
2016-02-27 00:43:55 +00:00
|
|
|
|
2016-04-23 02:55:17 +00:00
|
|
|
if c.ha != nil {
|
|
|
|
sd, ok := c.ha.(physical.ServiceDiscovery)
|
|
|
|
if ok {
|
|
|
|
go func() {
|
2016-04-24 14:04:51 +00:00
|
|
|
if err := sd.AdvertiseSealed(true); err != nil {
|
2016-04-23 02:55:17 +00:00
|
|
|
c.logger.Printf("[WARN] core: failed to advertise sealed status: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-13 18:34:40 +00:00
|
|
|
return nil
|
2015-03-10 00:45:34 +00:00
|
|
|
}
|
2015-03-11 22:19:41 +00:00
|
|
|
|
|
|
|
// postUnseal is invoked after the barrier is unsealed, but before
|
|
|
|
// allowing any user operations. This allows us to setup any state that
|
|
|
|
// requires the Vault to be unsealed such as mount tables, logical backends,
|
|
|
|
// credential stores, etc.
|
2015-11-02 16:01:00 +00:00
|
|
|
func (c *Core) postUnseal() (retErr error) {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "post_unseal"}, time.Now())
|
2015-11-02 16:01:00 +00:00
|
|
|
defer func() {
|
|
|
|
if retErr != nil {
|
|
|
|
c.preSeal()
|
|
|
|
}
|
|
|
|
}()
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[INFO] core: post-unseal setup starting")
|
2015-04-14 18:08:04 +00:00
|
|
|
if cache, ok := c.physical.(*physical.Cache); ok {
|
|
|
|
cache.Purge()
|
|
|
|
}
|
2015-05-28 23:11:31 +00:00
|
|
|
// HA mode requires us to handle keyring rotation and rekeying
|
|
|
|
if c.ha != nil {
|
|
|
|
if err := c.checkKeyUpgrades(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-05-29 21:30:03 +00:00
|
|
|
if err := c.barrier.ReloadMasterKey(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-05-28 23:11:31 +00:00
|
|
|
if err := c.barrier.ReloadKeyring(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-05-28 23:52:06 +00:00
|
|
|
if err := c.scheduleUpgradeCleanup(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-05-28 23:11:31 +00:00
|
|
|
}
|
2015-03-11 22:19:41 +00:00
|
|
|
if err := c.loadMounts(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-11 22:50:27 +00:00
|
|
|
if err := c.setupMounts(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-17 23:23:58 +00:00
|
|
|
if err := c.startRollback(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-18 21:00:42 +00:00
|
|
|
if err := c.setupPolicyStore(); err != nil {
|
2015-11-02 16:01:00 +00:00
|
|
|
return err
|
2015-03-18 21:00:42 +00:00
|
|
|
}
|
2015-03-18 22:46:07 +00:00
|
|
|
if err := c.loadCredentials(); err != nil {
|
2015-11-02 16:01:00 +00:00
|
|
|
return err
|
2015-03-18 22:46:07 +00:00
|
|
|
}
|
2015-03-18 22:30:31 +00:00
|
|
|
if err := c.setupCredentials(); err != nil {
|
2015-11-02 16:01:00 +00:00
|
|
|
return err
|
2015-03-18 22:30:31 +00:00
|
|
|
}
|
2015-03-24 01:00:14 +00:00
|
|
|
if err := c.setupExpiration(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-27 21:00:38 +00:00
|
|
|
if err := c.loadAudits(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := c.setupAudits(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-08 23:43:17 +00:00
|
|
|
c.metricsCh = make(chan struct{})
|
|
|
|
go c.emitMetrics(c.metricsCh)
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[INFO] core: post-unseal setup complete")
|
2015-03-11 22:19:41 +00:00
|
|
|
return nil
|
|
|
|
}
|
2015-03-13 18:16:24 +00:00
|
|
|
|
|
|
|
// preSeal is invoked before the barrier is sealed, allowing
|
|
|
|
// for any state teardown required.
|
2015-11-02 18:29:18 +00:00
|
|
|
func (c *Core) preSeal() error {
|
2015-04-08 23:43:17 +00:00
|
|
|
defer metrics.MeasureSince([]string{"core", "pre_seal"}, time.Now())
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[INFO] core: pre-seal teardown starting")
|
2015-05-28 19:07:52 +00:00
|
|
|
|
|
|
|
// Clear any rekey progress
|
2016-04-04 14:44:22 +00:00
|
|
|
c.barrierRekeyConfig = nil
|
|
|
|
c.barrierRekeyProgress = nil
|
|
|
|
c.recoveryRekeyConfig = nil
|
|
|
|
c.recoveryRekeyProgress = nil
|
2015-05-28 19:07:52 +00:00
|
|
|
|
2015-04-08 23:43:17 +00:00
|
|
|
if c.metricsCh != nil {
|
|
|
|
close(c.metricsCh)
|
|
|
|
c.metricsCh = nil
|
|
|
|
}
|
2015-11-02 18:29:18 +00:00
|
|
|
var result error
|
2015-03-27 21:00:38 +00:00
|
|
|
if err := c.teardownAudits(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error tearing down audits: {{err}}", err))
|
2015-03-27 21:00:38 +00:00
|
|
|
}
|
2015-03-24 01:00:14 +00:00
|
|
|
if err := c.stopExpiration(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error stopping expiration: {{err}}", err))
|
2015-03-24 01:00:14 +00:00
|
|
|
}
|
2015-03-18 22:30:31 +00:00
|
|
|
if err := c.teardownCredentials(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error tearing down credentials: {{err}}", err))
|
2015-03-18 22:30:31 +00:00
|
|
|
}
|
2015-03-18 21:00:42 +00:00
|
|
|
if err := c.teardownPolicyStore(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error tearing down policy store: {{err}}", err))
|
2015-03-18 21:00:42 +00:00
|
|
|
}
|
2015-03-17 23:23:58 +00:00
|
|
|
if err := c.stopRollback(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error stopping rollback: {{err}}", err))
|
2015-03-17 23:23:58 +00:00
|
|
|
}
|
2015-03-13 18:16:24 +00:00
|
|
|
if err := c.unloadMounts(); err != nil {
|
2015-11-02 18:29:18 +00:00
|
|
|
result = multierror.Append(result, errwrap.Wrapf("[ERR] error unloading mounts: {{err}}", err))
|
2015-03-13 18:16:24 +00:00
|
|
|
}
|
2015-04-14 18:08:04 +00:00
|
|
|
if cache, ok := c.physical.(*physical.Cache); ok {
|
|
|
|
cache.Purge()
|
|
|
|
}
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[INFO] core: pre-seal teardown complete")
|
2015-11-02 18:29:18 +00:00
|
|
|
return result
|
2015-03-13 18:16:24 +00:00
|
|
|
}
|
2015-04-08 23:43:17 +00:00
|
|
|
|
2015-04-14 23:11:39 +00:00
|
|
|
// runStandby is a long running routine that is used when an HA backend
|
2015-04-14 21:06:15 +00:00
|
|
|
// is enabled. It waits until we are leader and switches this Vault to
|
|
|
|
// active.
|
2016-02-29 02:35:32 +00:00
|
|
|
func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) {
|
2015-04-14 21:06:15 +00:00
|
|
|
defer close(doneCh)
|
2016-02-29 02:35:32 +00:00
|
|
|
defer close(manualStepDownCh)
|
2015-04-14 23:06:58 +00:00
|
|
|
c.logger.Printf("[INFO] core: entering standby mode")
|
2015-05-28 23:11:31 +00:00
|
|
|
|
|
|
|
// Monitor for key rotation
|
|
|
|
keyRotateDone := make(chan struct{})
|
|
|
|
keyRotateStop := make(chan struct{})
|
|
|
|
go c.periodicCheckKeyUpgrade(keyRotateDone, keyRotateStop)
|
|
|
|
defer func() {
|
|
|
|
close(keyRotateStop)
|
|
|
|
<-keyRotateDone
|
|
|
|
}()
|
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
for {
|
|
|
|
// Check for a shutdown
|
|
|
|
select {
|
|
|
|
case <-stopCh:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a lock
|
2016-01-13 18:40:08 +00:00
|
|
|
uuid, err := uuid.GenerateUUID()
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to generate uuid: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2015-04-14 23:44:48 +00:00
|
|
|
lock, err := c.ha.LockWith(coreLockPath, uuid)
|
2015-04-14 21:06:15 +00:00
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to create lock: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt the acquisition
|
2015-10-08 18:34:10 +00:00
|
|
|
leaderLostCh := c.acquireLock(lock, stopCh)
|
2015-04-14 21:06:15 +00:00
|
|
|
|
|
|
|
// Bail if we are being shutdown
|
2015-10-08 18:34:10 +00:00
|
|
|
if leaderLostCh == nil {
|
2015-04-14 21:06:15 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
c.logger.Printf("[INFO] core: acquired lock, enabling active operation")
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// Advertise ourself as leader
|
2015-10-08 18:34:10 +00:00
|
|
|
if err := c.advertiseLeader(uuid, leaderLostCh); err != nil {
|
2015-04-14 23:44:48 +00:00
|
|
|
c.logger.Printf("[ERR] core: leader advertisement setup failed: %v", err)
|
|
|
|
lock.Unlock()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
// Attempt the post-unseal process
|
|
|
|
c.stateLock.Lock()
|
|
|
|
err = c.postUnseal()
|
|
|
|
if err == nil {
|
2015-04-14 23:11:39 +00:00
|
|
|
c.standby = false
|
2015-04-14 21:06:15 +00:00
|
|
|
}
|
|
|
|
c.stateLock.Unlock()
|
|
|
|
|
|
|
|
// Handle a failure to unseal
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err)
|
|
|
|
lock.Unlock()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Monitor a loss of leadership
|
2016-02-29 02:35:32 +00:00
|
|
|
var manualStepDown bool
|
2015-04-14 21:06:15 +00:00
|
|
|
select {
|
2015-10-08 18:34:10 +00:00
|
|
|
case <-leaderLostCh:
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[WARN] core: leadership lost, stopping active operation")
|
|
|
|
case <-stopCh:
|
|
|
|
c.logger.Printf("[WARN] core: stopping active operation")
|
2016-02-29 02:35:32 +00:00
|
|
|
case <-manualStepDownCh:
|
|
|
|
c.logger.Printf("[WARN] core: stepping down from active operation to standby")
|
|
|
|
manualStepDown = true
|
2015-04-14 21:06:15 +00:00
|
|
|
}
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// Clear ourself as leader
|
|
|
|
if err := c.clearLeader(uuid); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: clearing leader advertisement failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-04-14 21:06:15 +00:00
|
|
|
// Attempt the pre-seal process
|
|
|
|
c.stateLock.Lock()
|
2015-04-14 23:11:39 +00:00
|
|
|
c.standby = true
|
2015-11-02 16:01:00 +00:00
|
|
|
preSealErr := c.preSeal()
|
2015-04-14 21:06:15 +00:00
|
|
|
c.stateLock.Unlock()
|
|
|
|
|
|
|
|
// Give up leadership
|
|
|
|
lock.Unlock()
|
|
|
|
|
|
|
|
// Check for a failure to prepare to seal
|
2015-11-02 16:01:00 +00:00
|
|
|
if preSealErr != nil {
|
2015-04-14 21:06:15 +00:00
|
|
|
c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err)
|
|
|
|
}
|
2016-02-27 00:43:55 +00:00
|
|
|
|
|
|
|
// If we've merely stepped down, we could instantly grab the lock
|
|
|
|
// again. Give the other nodes a chance.
|
2016-02-29 02:35:32 +00:00
|
|
|
if manualStepDown {
|
|
|
|
time.Sleep(manualStepDownSleepPeriod)
|
|
|
|
}
|
2015-04-14 21:06:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-28 23:11:31 +00:00
|
|
|
// periodicCheckKeyUpgrade is used to watch for key rotation events as a standby
|
|
|
|
func (c *Core) periodicCheckKeyUpgrade(doneCh, stopCh chan struct{}) {
|
|
|
|
defer close(doneCh)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(keyRotateCheckInterval):
|
|
|
|
// Only check if we are a standby
|
|
|
|
c.stateLock.RLock()
|
|
|
|
standby := c.standby
|
|
|
|
c.stateLock.RUnlock()
|
|
|
|
if !standby {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.checkKeyUpgrades(); err != nil {
|
2015-10-05 22:22:43 +00:00
|
|
|
c.logger.Printf("[ERR] core: key rotation periodic upgrade check failed: %v", err)
|
2015-05-28 23:11:31 +00:00
|
|
|
}
|
|
|
|
case <-stopCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkKeyUpgrades is used to check if there have been any key rotations
|
|
|
|
// and if there is a chain of upgrades available
|
|
|
|
func (c *Core) checkKeyUpgrades() error {
|
|
|
|
for {
|
2015-05-28 23:43:44 +00:00
|
|
|
// Check for an upgrade
|
|
|
|
didUpgrade, newTerm, err := c.barrier.CheckUpgrade()
|
2015-05-28 23:11:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing to do if no upgrade
|
2015-05-28 23:43:44 +00:00
|
|
|
if !didUpgrade {
|
2015-05-28 23:11:31 +00:00
|
|
|
break
|
|
|
|
}
|
2015-05-28 23:43:44 +00:00
|
|
|
c.logger.Printf("[INFO] core: upgraded to key term %d", newTerm)
|
2015-05-28 23:11:31 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-05-28 23:52:06 +00:00
|
|
|
// scheduleUpgradeCleanup is used to ensure that all the upgrade paths
|
|
|
|
// are cleaned up in a timely manner if a leader failover takes place
|
|
|
|
func (c *Core) scheduleUpgradeCleanup() error {
|
|
|
|
// List the upgrades
|
|
|
|
upgrades, err := c.barrier.List(keyringUpgradePrefix)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to list upgrades: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing to do if no upgrades
|
|
|
|
if len(upgrades) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Schedule cleanup for all of them
|
|
|
|
time.AfterFunc(keyRotateGracePeriod, func() {
|
|
|
|
for _, upgrade := range upgrades {
|
|
|
|
path := fmt.Sprintf("%s%s", keyringUpgradePrefix, upgrade)
|
|
|
|
if err := c.barrier.Delete(path); err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to cleanup upgrade: %s", path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-10-08 18:34:10 +00:00
|
|
|
// acquireLock blocks until the lock is acquired, returning the leaderLostCh
|
2015-04-14 21:06:15 +00:00
|
|
|
func (c *Core) acquireLock(lock physical.Lock, stopCh <-chan struct{}) <-chan struct{} {
|
|
|
|
for {
|
|
|
|
// Attempt lock acquisition
|
2015-10-08 18:34:10 +00:00
|
|
|
leaderLostCh, err := lock.Lock(stopCh)
|
2015-04-14 21:06:15 +00:00
|
|
|
if err == nil {
|
2015-10-08 18:34:10 +00:00
|
|
|
return leaderLostCh
|
2015-04-14 21:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Retry the acquisition
|
|
|
|
c.logger.Printf("[ERR] core: failed to acquire lock: %v", err)
|
|
|
|
select {
|
|
|
|
case <-time.After(lockRetryInterval):
|
|
|
|
case <-stopCh:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// advertiseLeader is used to advertise the current node as leader
|
2015-10-08 18:34:10 +00:00
|
|
|
func (c *Core) advertiseLeader(uuid string, leaderLostCh <-chan struct{}) error {
|
|
|
|
go c.cleanLeaderPrefix(uuid, leaderLostCh)
|
2015-04-14 23:44:48 +00:00
|
|
|
ent := &Entry{
|
|
|
|
Key: coreLeaderPrefix + uuid,
|
|
|
|
Value: []byte(c.advertiseAddr),
|
|
|
|
}
|
2016-04-23 02:55:17 +00:00
|
|
|
err := c.barrier.Put(ent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
sd, ok := c.ha.(physical.ServiceDiscovery)
|
|
|
|
if ok {
|
|
|
|
go func() {
|
|
|
|
if err := sd.AdvertiseActive(true); err != nil {
|
|
|
|
c.logger.Printf("[WARN] core: failed to advertise active status: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
return nil
|
2015-04-14 23:44:48 +00:00
|
|
|
}
|
|
|
|
|
2015-10-08 18:34:10 +00:00
|
|
|
func (c *Core) cleanLeaderPrefix(uuid string, leaderLostCh <-chan struct{}) {
|
2015-10-08 17:47:21 +00:00
|
|
|
keys, err := c.barrier.List(coreLeaderPrefix)
|
|
|
|
if err != nil {
|
|
|
|
c.logger.Printf("[ERR] core: failed to list entries in core/leader: %v", err)
|
|
|
|
return
|
|
|
|
}
|
2015-10-08 18:34:10 +00:00
|
|
|
for len(keys) > 0 {
|
2015-10-08 17:47:21 +00:00
|
|
|
select {
|
2015-10-08 18:34:10 +00:00
|
|
|
case <-time.After(leaderPrefixCleanDelay):
|
2015-10-08 17:47:21 +00:00
|
|
|
if keys[0] != uuid {
|
|
|
|
c.barrier.Delete(coreLeaderPrefix + keys[0])
|
|
|
|
}
|
|
|
|
keys = keys[1:]
|
2015-10-08 18:34:10 +00:00
|
|
|
case <-leaderLostCh:
|
2015-10-08 17:47:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-14 23:44:48 +00:00
|
|
|
// clearLeader is used to clear our leadership entry
|
|
|
|
func (c *Core) clearLeader(uuid string) error {
|
|
|
|
key := coreLeaderPrefix + uuid
|
2016-04-24 14:04:51 +00:00
|
|
|
err := c.barrier.Delete(key)
|
|
|
|
|
|
|
|
// Advertise ourselves as a standby
|
|
|
|
sd, ok := c.ha.(physical.ServiceDiscovery)
|
|
|
|
if ok {
|
|
|
|
go func() {
|
|
|
|
if err := sd.AdvertiseActive(false); err != nil {
|
|
|
|
c.logger.Printf("[WARN] core: failed to advertise standby status: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
2015-04-14 23:44:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-08 23:43:17 +00:00
|
|
|
// emitMetrics is used to periodically expose metrics while runnig
|
|
|
|
func (c *Core) emitMetrics(stopCh chan struct{}) {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(time.Second):
|
2015-10-12 20:33:54 +00:00
|
|
|
c.metricsMutex.Lock()
|
|
|
|
if c.expiration != nil {
|
|
|
|
c.expiration.emitMetrics()
|
|
|
|
}
|
|
|
|
c.metricsMutex.Unlock()
|
2015-04-08 23:43:17 +00:00
|
|
|
case <-stopCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-04-04 14:44:22 +00:00
|
|
|
|
|
|
|
func (c *Core) SealAccess() *SealAccess {
|
|
|
|
sa := &SealAccess{}
|
|
|
|
sa.SetSeal(c.seal)
|
|
|
|
return sa
|
|
|
|
}
|