Add nonce to unseal to allow seeing if the operation has reset (#2276)

This commit is contained in:
Jeff Mitchell 2017-01-17 11:47:06 -05:00 committed by GitHub
parent 0591ba4be8
commit dd0e44ca10
7 changed files with 58 additions and 23 deletions

View File

@ -53,6 +53,7 @@ type SealStatusResponse struct {
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`

View File

@ -40,11 +40,13 @@ func (c *StatusCommand) Run(args []string) int {
"Key Shares: %d\n"+
"Key Threshold: %d\n"+
"Unseal Progress: %d\n"+
"Unseal Nonce: %v"+
"Version: %s",
sealStatus.Sealed,
sealStatus.N,
sealStatus.T,
sealStatus.Progress,
sealStatus.Nonce,
sealStatus.Version)
if sealStatus.ClusterName != "" && sealStatus.ClusterID != "" {

View File

@ -84,11 +84,13 @@ func (c *UnsealCommand) Run(args []string) int {
"Sealed: %v\n"+
"Key Shares: %d\n"+
"Key Threshold: %d\n"+
"Unseal Progress: %d",
"Unseal Progress: %d\n"+
"Unseal Nonce: %v",
sealStatus.Sealed,
sealStatus.N,
sealStatus.T,
sealStatus.Progress,
sealStatus.Nonce,
))
return 0

View File

@ -186,11 +186,14 @@ func handleSysSealStatusRaw(core *vault.Core, w http.ResponseWriter, r *http.Req
clusterID = cluster.ID
}
progress, nonce := core.SecretProgress()
respondOk(w, &SealStatusResponse{
Sealed: sealed,
T: sealConfig.SecretThreshold,
N: sealConfig.SecretShares,
Progress: core.SecretProgress(),
Progress: progress,
Nonce: nonce,
Version: version.GetVersion().VersionNumber(),
ClusterName: clusterName,
ClusterID: clusterID,
@ -202,6 +205,7 @@ type SealStatusResponse struct {
T int `json:"t"`
N int `json:"n"`
Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`

View File

@ -29,6 +29,7 @@ func TestSysSealStatus(t *testing.T) {
"t": json.Number("1"),
"n": json.Number("1"),
"progress": json.Number("0"),
"nonce": "",
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -115,6 +116,7 @@ func TestSysUnseal(t *testing.T) {
"t": json.Number("1"),
"n": json.Number("1"),
"progress": json.Number("0"),
"nonce": "",
}
testResponseStatus(t, resp, 200)
testResponseBody(t, resp, &actual)
@ -189,6 +191,10 @@ func TestSysUnseal_Reset(t *testing.T) {
t.Fatalf("expected version information")
}
expected["version"] = actual["version"]
if actual["nonce"] == "" && expected["sealed"].(bool) {
t.Fatalf("expected a nonce")
}
expected["nonce"] = actual["nonce"]
if actual["cluster_name"] == nil {
delete(expected, "cluster_name")
} else {
@ -221,6 +227,7 @@ func TestSysUnseal_Reset(t *testing.T) {
t.Fatalf("expected version information")
}
expected["version"] = actual["version"]
expected["nonce"] = actual["nonce"]
if actual["cluster_name"] == nil {
delete(expected, "cluster_name")
} else {

View File

@ -124,6 +124,11 @@ type activeAdvertisement struct {
ClusterKeyParams *clusterKeyParams `json:"cluster_key_params,omitempty"`
}
type unlockInformation struct {
Parts [][]byte
Nonce string
}
// 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.
@ -167,9 +172,8 @@ type Core struct {
standbyStopCh chan struct{}
manualStepDownCh chan struct{}
// unlockParts has the keys provided to Unseal until
// the threshold number of parts is available.
unlockParts [][]byte
// unlockInfo has the keys provided to Unseal until the threshold number of parts is available, as well as the operation nonce
unlockInfo *unlockInformation
// generateRootProgress holds the shares until we reach enough
// to verify the master key
@ -735,10 +739,15 @@ func (c *Core) Leader() (isLeader bool, leaderAddr string, err error) {
}
// SecretProgress returns the number of keys provided so far
func (c *Core) SecretProgress() int {
func (c *Core) SecretProgress() (int, string) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return len(c.unlockParts)
switch c.unlockInfo {
case nil:
return 0, ""
default:
return len(c.unlockInfo.Parts), c.unlockInfo.Nonce
}
}
// ResetUnsealProcess removes the current unlock parts from memory, to reset
@ -749,7 +758,7 @@ func (c *Core) ResetUnsealProcess() {
if !c.sealed {
return
}
c.unlockParts = nil
c.unlockInfo = nil
}
// Unseal is used to provide one of the key parts to unseal the Vault.
@ -790,19 +799,29 @@ func (c *Core) Unseal(key []byte) (bool, error) {
}
// Check if we already have this piece
for _, existing := range c.unlockParts {
if bytes.Equal(existing, key) {
return false, nil
if c.unlockInfo != nil {
for _, existing := range c.unlockInfo.Parts {
if bytes.Equal(existing, key) {
return false, nil
}
}
} else {
uuid, err := uuid.GenerateUUID()
if err != nil {
return false, err
}
c.unlockInfo = &unlockInformation{
Nonce: uuid,
}
}
// Store this key
c.unlockParts = append(c.unlockParts, key)
c.unlockInfo.Parts = append(c.unlockInfo.Parts, key)
// Check if we don't have enough keys to unlock
if len(c.unlockParts) < config.SecretThreshold {
if len(c.unlockInfo.Parts) < config.SecretThreshold {
if c.logger.IsDebug() {
c.logger.Debug("core: cannot unseal, not enough keys", "keys", len(c.unlockParts), "threshold", config.SecretThreshold)
c.logger.Debug("core: cannot unseal, not enough keys", "keys", len(c.unlockInfo.Parts), "threshold", config.SecretThreshold, "nonce", c.unlockInfo.Nonce)
}
return false, nil
}
@ -810,11 +829,11 @@ func (c *Core) Unseal(key []byte) (bool, error) {
// Recover the master key
var masterKey []byte
if config.SecretThreshold == 1 {
masterKey = c.unlockParts[0]
c.unlockParts = nil
masterKey = c.unlockInfo.Parts[0]
c.unlockInfo = nil
} else {
masterKey, err = shamir.Combine(c.unlockParts)
c.unlockParts = nil
masterKey, err = shamir.Combine(c.unlockInfo.Parts)
c.unlockInfo = nil
if err != nil {
return false, fmt.Errorf("failed to compute master key: %v", err)
}

View File

@ -72,7 +72,7 @@ func TestCore_Unseal_MultiShare(t *testing.T) {
t.Fatalf("should be sealed")
}
if prog := c.SecretProgress(); prog != 0 {
if prog, _ := c.SecretProgress(); prog != 0 {
t.Fatalf("bad progress: %d", prog)
}
@ -91,14 +91,14 @@ func TestCore_Unseal_MultiShare(t *testing.T) {
if !unseal {
t.Fatalf("should be unsealed")
}
if prog := c.SecretProgress(); prog != 0 {
if prog, _ := c.SecretProgress(); prog != 0 {
t.Fatalf("bad progress: %d", prog)
}
} else {
if unseal {
t.Fatalf("should not be unsealed")
}
if prog := c.SecretProgress(); prog != i+1 {
if prog, _ := c.SecretProgress(); prog != i+1 {
t.Fatalf("bad progress: %d", prog)
}
}
@ -160,7 +160,7 @@ func TestCore_Unseal_Single(t *testing.T) {
t.Fatalf("should be sealed")
}
if prog := c.SecretProgress(); prog != 0 {
if prog, _ := c.SecretProgress(); prog != 0 {
t.Fatalf("bad progress: %d", prog)
}
@ -172,7 +172,7 @@ func TestCore_Unseal_Single(t *testing.T) {
if !unseal {
t.Fatalf("should be unsealed")
}
if prog := c.SecretProgress(); prog != 0 {
if prog, _ := c.SecretProgress(); prog != 0 {
t.Fatalf("bad progress: %d", prog)
}