open-vault/vault/init.go
2016-04-04 10:44:22 -04:00

283 lines
8.1 KiB
Go

package vault
import (
"encoding/hex"
"fmt"
"github.com/hashicorp/vault/helper/pgpkeys"
"github.com/hashicorp/vault/shamir"
)
// InitResult is used to provide the key parts back after
// they are generated as part of the initialization.
type InitResult struct {
SecretShares [][]byte
RecoveryShares [][]byte
RootToken string
}
// Initialized checks if the Vault is already initialized
func (c *Core) Initialized() (bool, error) {
// Check the barrier first
init, err := c.barrier.Initialized()
if err != nil {
c.logger.Printf("[ERR] core: barrier init check failed: %v", err)
return false, err
}
if !init {
c.logger.Printf("[INFO] core: security barrier not initialized")
return false, nil
}
// Verify the seal configuration
sealConf, err := c.seal.BarrierConfig()
if err != nil {
return false, err
}
if sealConf == nil {
return false, nil
}
return true, nil
}
func (c *Core) generateShares(sc *SealConfig) ([]byte, [][]byte, error) {
// Generate a master key
masterKey, err := c.barrier.GenerateKey()
if err != nil {
return nil, nil, fmt.Errorf("key generation failed: %v", err)
}
// Return the master key if only a single key part is used
var unsealKeys [][]byte
if sc.SecretShares == 1 {
unsealKeys = append(unsealKeys, masterKey)
} else {
// Split the master key using the Shamir algorithm
shares, err := shamir.Split(masterKey, sc.SecretShares, sc.SecretThreshold)
if err != nil {
return nil, nil, fmt.Errorf("failed to generate shares: %v", err)
}
unsealKeys = shares
}
// If we have PGP keys, perform the encryption
if len(sc.PGPKeys) > 0 {
hexEncodedShares := make([][]byte, len(unsealKeys))
for i, _ := range unsealKeys {
hexEncodedShares[i] = []byte(hex.EncodeToString(unsealKeys[i]))
}
_, encryptedShares, err := pgpkeys.EncryptShares(hexEncodedShares, sc.PGPKeys)
if err != nil {
return nil, nil, err
}
unsealKeys = encryptedShares
}
return masterKey, unsealKeys, nil
}
// Initialize is used to initialize the Vault with the given
// configurations.
func (c *Core) Initialize(barrierConfig, recoveryConfig *SealConfig) (*InitResult, error) {
if c.seal.RecoveryKeySupported() {
if recoveryConfig == nil {
return nil, fmt.Errorf("recovery configuration must be supplied")
}
if recoveryConfig.SecretShares == 0 {
return nil, fmt.Errorf("recovery configuration must specify a positive number of shares, or a negative number to disable")
}
if recoveryConfig.SecretShares > 0 {
// Check if the seal configuraiton is valid
if err := recoveryConfig.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err)
return nil, fmt.Errorf("invalid recovery configuration: %v", err)
}
}
}
if c.seal.StoredKeysSupported() {
if barrierConfig.SecretShares != 1 {
return nil, fmt.Errorf("secret shares must be 1")
}
if barrierConfig.SecretThreshold != barrierConfig.SecretShares {
return nil, fmt.Errorf("secret threshold must be same as secret shares")
}
if barrierConfig.StoredShares != barrierConfig.SecretShares {
return nil, fmt.Errorf("stored shares must be same as secret shares")
}
if barrierConfig.PGPKeys != nil && len(barrierConfig.PGPKeys) > 0 {
return nil, fmt.Errorf("PGP keys not supported when storing shares")
}
} else {
if barrierConfig.StoredShares > 0 {
return nil, fmt.Errorf("stored keys are not supported")
}
}
// Check if the seal configuraiton is valid
if err := barrierConfig.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
return nil, fmt.Errorf("invalid seal configuration: %v", err)
}
// We defer this now because the unseal operation locks the state lock, and
// defer operations are LIFO
defer c.unsealWithStoredKeys()
// Avoid an initialization race
c.stateLock.Lock()
defer c.stateLock.Unlock()
// Check if we are initialized
init, err := c.Initialized()
if err != nil {
return nil, err
}
if init {
return nil, ErrAlreadyInit
}
err = c.seal.Init()
if err != nil {
c.logger.Printf("[ERR] core: failed to initialize seal: %v", err)
return nil, fmt.Errorf("error initializing seal: %v", err)
}
err = c.seal.SetBarrierConfig(barrierConfig)
if err != nil {
c.logger.Printf("[ERR] core: failed to save barrier configuration: %v", err)
return nil, fmt.Errorf("barrier configuration saving failed: %v", err)
}
barrierKey, barrierUnsealKeys, err := c.generateShares(barrierConfig)
if err != nil {
c.logger.Printf("[ERR] core: %v", err)
return nil, err
}
// If we are storing shares, pop them out of the returned results and push
// them through the seal
if barrierConfig.StoredShares > 0 {
var keysToStore [][]byte
for i := 0; i < barrierConfig.StoredShares; i++ {
keysToStore = append(keysToStore, barrierUnsealKeys[0])
barrierUnsealKeys = barrierUnsealKeys[1:]
}
if err := c.seal.SetStoredKeys(keysToStore); err != nil {
c.logger.Printf("[ERR] core: failed to store keys: %v", err)
return nil, fmt.Errorf("failed to store keys: %v", err)
}
}
results := &InitResult{
SecretShares: barrierUnsealKeys,
}
// Save the configuration regardless, but only generate a key if it's not
// disabled
if c.seal.RecoveryKeySupported() {
err = c.seal.SetRecoveryConfig(recoveryConfig)
if err != nil {
c.logger.Printf("[ERR] core: failed to save recovery configuration: %v", err)
return nil, fmt.Errorf("recovery configuration saving failed: %v", err)
}
if recoveryConfig.SecretShares > 0 {
recoveryKey, recoveryUnsealKeys, err := c.generateShares(recoveryConfig)
if err != nil {
c.logger.Printf("[ERR] core: %v", err)
return nil, err
}
err = c.seal.SetRecoveryKey(recoveryKey)
if err != nil {
return nil, err
}
results.RecoveryShares = recoveryUnsealKeys
}
}
// Initialize the barrier
if err := c.barrier.Initialize(barrierKey); err != nil {
c.logger.Printf("[ERR] core: failed to initialize barrier: %v", err)
return nil, fmt.Errorf("failed to initialize barrier: %v", err)
}
c.logger.Printf("[INFO] core: security barrier initialized (shares: %d, threshold %d)",
barrierConfig.SecretShares, barrierConfig.SecretThreshold)
// Unseal the barrier
if err := c.barrier.Unseal(barrierKey); err != nil {
c.logger.Printf("[ERR] core: failed to unseal barrier: %v", err)
return nil, fmt.Errorf("failed to unseal barrier: %v", err)
}
// Ensure the barrier is re-sealed
defer func() {
if err := c.barrier.Seal(); err != nil {
c.logger.Printf("[ERR] core: failed to seal barrier: %v", err)
}
}()
// Perform initial setup
if err := c.postUnseal(); err != nil {
c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err)
return nil, err
}
// Generate a new root token
rootToken, err := c.tokenStore.rootToken()
if err != nil {
c.logger.Printf("[ERR] core: root token generation failed: %v", err)
return nil, err
}
results.RootToken = rootToken.ID
c.logger.Printf("[INFO] core: root token generated")
// Prepare to re-seal
if err := c.preSeal(); err != nil {
c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err)
return nil, err
}
return results, nil
}
func (c *Core) unsealWithStoredKeys() error {
if !c.seal.StoredKeysSupported() {
return nil
}
c.logger.Printf("[INFO] core: stored unseal keys supported, attempting fetch")
keys, err := c.seal.GetStoredKeys()
if err != nil {
c.logger.Printf("[ERR] core: fetching stored unseal keys failed: %v", err)
return &NonFatalError{Err: fmt.Errorf("fetching stored unseal keys failed: %v", err)}
}
if len(keys) == 0 {
c.logger.Printf("[WARN] core: stored unseal key(s) supported but none found")
} else {
unsealed := false
keysUsed := 0
for _, key := range keys {
unsealed, err = c.Unseal(key)
if err != nil {
c.logger.Printf("[ERR] core: unseal with stored unseal key failed: %v", err)
return &NonFatalError{Err: fmt.Errorf("unseal with stored key failed: %v", err)}
}
keysUsed += 1
if unsealed {
break
}
}
if !unsealed {
c.logger.Printf("[WARN] core: %d stored unseal key(s) used but Vault not unsealed yet", keysUsed)
} else {
c.logger.Printf("[INFO] core: successfully unsealed with %d stored key(s)", keysUsed)
}
}
return nil
}