mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Return status for rekey/root generation at init time. This mitigates a
(very unlikely) potential timing attack between init-ing and fetching status. Fixes #1054
This commit is contained in:
@@ -13,7 +13,7 @@ func (c *Sys) GenerateRootStatus() (*GenerateRootStatusResponse, error) {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateRootInit(otp, pgpKey string) error {
|
||||
func (c *Sys) GenerateRootInit(otp, pgpKey string) (*GenerateRootStatusResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"otp": otp,
|
||||
"pgp_key": pgpKey,
|
||||
@@ -21,14 +21,18 @@ func (c *Sys) GenerateRootInit(otp, pgpKey string) error {
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/generate-root/attempt")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return err
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result GenerateRootStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateRootCancel() error {
|
||||
|
||||
@@ -13,17 +13,21 @@ func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyInit(config *RekeyInitRequest) error {
|
||||
func (c *Sys) RekeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return err
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyCancel() error {
|
||||
|
||||
@@ -140,16 +140,11 @@ func (c *GenerateRootCommand) Run(args []string) int {
|
||||
|
||||
// Start the root generation process if not started
|
||||
if !rootGenerationStatus.Started {
|
||||
err = client.Sys().GenerateRootInit(otp, pgpKey)
|
||||
rootGenerationStatus, err = client.Sys().GenerateRootInit(otp, pgpKey)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing root generation: %s", err))
|
||||
return 1
|
||||
}
|
||||
rootGenerationStatus, err = client.Sys().GenerateRootStatus()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error reading root generation status: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.Nonce = rootGenerationStatus.Nonce
|
||||
}
|
||||
|
||||
@@ -229,14 +224,15 @@ func (c *GenerateRootCommand) decode(encodedVal, otp string) int {
|
||||
// initGenerateRoot is used to start the generation process
|
||||
func (c *GenerateRootCommand) initGenerateRoot(client *api.Client, otp string, pgpKey string) int {
|
||||
// Start the rekey
|
||||
err := client.Sys().GenerateRootInit(otp, pgpKey)
|
||||
status, err := client.Sys().GenerateRootInit(otp, pgpKey)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing root generation: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Provide the current status
|
||||
return c.rootGenerationStatus(client)
|
||||
c.dumpStatus(status)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// cancelGenerateRoot is used to abort the generation process
|
||||
|
||||
@@ -78,7 +78,7 @@ func (c *RekeyCommand) Run(args []string) int {
|
||||
|
||||
// Start the rekey process if not started
|
||||
if !rekeyStatus.Started {
|
||||
err := client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
rekeyStatus, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
@@ -87,11 +87,6 @@ func (c *RekeyCommand) Run(args []string) int {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing rekey: %s", err))
|
||||
return 1
|
||||
}
|
||||
rekeyStatus, err = client.Sys().RekeyStatus()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error reading rekey status: %s", err))
|
||||
return 1
|
||||
}
|
||||
c.Nonce = rekeyStatus.Nonce
|
||||
}
|
||||
|
||||
@@ -182,7 +177,7 @@ func (c *RekeyCommand) initRekey(client *api.Client,
|
||||
pgpKeys pgpkeys.PubKeyFilesFlag,
|
||||
backup bool) int {
|
||||
// Start the rekey
|
||||
err := client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
@@ -214,7 +209,7 @@ be deleted at a later time with 'vault rekey -delete'.
|
||||
}
|
||||
|
||||
// Provide the current status
|
||||
return c.rekeyStatus(client)
|
||||
return c.dumpRekeyStatus(status)
|
||||
}
|
||||
|
||||
// cancelRekey is used to abort the rekey process
|
||||
@@ -237,6 +232,10 @@ func (c *RekeyCommand) rekeyStatus(client *api.Client) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
return c.dumpRekeyStatus(status)
|
||||
}
|
||||
|
||||
func (c *RekeyCommand) dumpRekeyStatus(status *api.RekeyStatusResponse) int {
|
||||
// Dump the status
|
||||
statString := fmt.Sprintf(
|
||||
"Nonce: %s\n"+
|
||||
|
||||
@@ -86,7 +86,8 @@ func handleSysGenerateRootAttemptPut(core *vault.Core, w http.ResponseWriter, r
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
respondOk(w, nil)
|
||||
|
||||
handleSysGenerateRootAttemptGet(core, w, r)
|
||||
}
|
||||
|
||||
func handleSysGenerateRootAttemptDelete(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -56,9 +56,7 @@ func TestSysGenerateRootAttempt_Setup_OTP(t *testing.T) {
|
||||
resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{
|
||||
"otp": otp,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/generate-root/attempt")
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
var actual map[string]interface{}
|
||||
expected := map[string]interface{}{
|
||||
@@ -75,6 +73,24 @@ func TestSysGenerateRootAttempt_Setup_OTP(t *testing.T) {
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/generate-root/attempt")
|
||||
|
||||
actual = map[string]interface{}{}
|
||||
expected = map[string]interface{}{
|
||||
"started": true,
|
||||
"progress": float64(0),
|
||||
"required": float64(1),
|
||||
"complete": false,
|
||||
"encoded_root_token": "",
|
||||
"pgp_fingerprint": "",
|
||||
}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["nonce"] = actual["nonce"]
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysGenerateRootAttempt_Setup_PGP(t *testing.T) {
|
||||
@@ -86,7 +102,7 @@ func TestSysGenerateRootAttempt_Setup_PGP(t *testing.T) {
|
||||
resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{
|
||||
"pgp_key": pgpkeys.TestPubKey1,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/generate-root/attempt")
|
||||
|
||||
@@ -123,6 +139,23 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) {
|
||||
"otp": otp,
|
||||
})
|
||||
|
||||
var actual map[string]interface{}
|
||||
expected := map[string]interface{}{
|
||||
"started": true,
|
||||
"progress": float64(0),
|
||||
"required": float64(1),
|
||||
"complete": false,
|
||||
"encoded_root_token": "",
|
||||
"pgp_fingerprint": "",
|
||||
}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["nonce"] = actual["nonce"]
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
initialNonce := expected["nonce"].(string)
|
||||
|
||||
resp = testHttpDelete(t, token, addr+"/v1/sys/generate-root/attempt")
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
@@ -131,8 +164,8 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
var actual map[string]interface{}
|
||||
expected := map[string]interface{}{
|
||||
actual = map[string]interface{}{}
|
||||
expected = map[string]interface{}{
|
||||
"started": false,
|
||||
"progress": float64(0),
|
||||
"required": float64(1),
|
||||
@@ -146,6 +179,10 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) {
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
|
||||
if expected["nonce"].(string) == initialNonce {
|
||||
t.Fatalf("Same nonce detected across two invocations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysGenerateRoot_badKey(t *testing.T) {
|
||||
@@ -181,7 +218,7 @@ func TestSysGenerateRoot_ReAttemptUpdate(t *testing.T) {
|
||||
resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{
|
||||
"otp": otp,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
resp = testHttpDelete(t, token, addr+"/v1/sys/generate-root/attempt")
|
||||
testResponseStatus(t, resp, 204)
|
||||
@@ -190,7 +227,7 @@ func TestSysGenerateRoot_ReAttemptUpdate(t *testing.T) {
|
||||
"pgp_key": pgpkeys.TestPubKey1,
|
||||
})
|
||||
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
}
|
||||
|
||||
func TestSysGenerateRoot_Update_OTP(t *testing.T) {
|
||||
@@ -208,13 +245,6 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
|
||||
resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{
|
||||
"otp": otp,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
// We need to get the nonce first before we update
|
||||
resp, err = http.Get(addr + "/v1/sys/generate-root/attempt")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
var rootGenerationStatus map[string]interface{}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &rootGenerationStatus)
|
||||
@@ -287,7 +317,7 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) {
|
||||
resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{
|
||||
"pgp_key": pgpkeys.TestPubKey1,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
// We need to get the nonce first before we update
|
||||
resp, err := http.Get(addr + "/v1/sys/generate-root/attempt")
|
||||
|
||||
@@ -100,7 +100,8 @@ func handleSysRekeyInitPut(core *vault.Core, w http.ResponseWriter, r *http.Requ
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
respondOk(w, nil)
|
||||
|
||||
handleSysRekeyInitGet(core, w, r)
|
||||
}
|
||||
|
||||
func handleSysRekeyInitDelete(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -48,9 +48,7 @@ func TestSysRekeyInit_Setup(t *testing.T) {
|
||||
"secret_shares": 5,
|
||||
"secret_threshold": 3,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/rekey/init")
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
var actual map[string]interface{}
|
||||
expected := map[string]interface{}{
|
||||
@@ -68,6 +66,25 @@ func TestSysRekeyInit_Setup(t *testing.T) {
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
|
||||
resp = testHttpGet(t, token, addr+"/v1/sys/rekey/init")
|
||||
|
||||
actual = map[string]interface{}{}
|
||||
expected = map[string]interface{}{
|
||||
"started": true,
|
||||
"t": float64(3),
|
||||
"n": float64(5),
|
||||
"progress": float64(0),
|
||||
"required": float64(1),
|
||||
"pgp_fingerprints": interface{}(nil),
|
||||
"backup": false,
|
||||
}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &actual)
|
||||
expected["nonce"] = actual["nonce"]
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysRekeyInit_Cancel(t *testing.T) {
|
||||
@@ -80,7 +97,7 @@ func TestSysRekeyInit_Cancel(t *testing.T) {
|
||||
"secret_shares": 5,
|
||||
"secret_threshold": 3,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
resp = testHttpDelete(t, token, addr+"/v1/sys/rekey/init")
|
||||
testResponseStatus(t, resp, 204)
|
||||
@@ -130,13 +147,6 @@ func TestSysRekey_Update(t *testing.T) {
|
||||
"secret_shares": 5,
|
||||
"secret_threshold": 3,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
|
||||
// We need to get the nonce first before we update
|
||||
resp, err := http.Get(addr + "/v1/sys/rekey/init")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
var rekeyStatus map[string]interface{}
|
||||
testResponseStatus(t, resp, 200)
|
||||
testResponseBody(t, resp, &rekeyStatus)
|
||||
@@ -177,7 +187,7 @@ func TestSysRekey_ReInitUpdate(t *testing.T) {
|
||||
"secret_shares": 5,
|
||||
"secret_threshold": 3,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
resp = testHttpDelete(t, token, addr+"/v1/sys/rekey/init")
|
||||
testResponseStatus(t, resp, 204)
|
||||
@@ -186,7 +196,7 @@ func TestSysRekey_ReInitUpdate(t *testing.T) {
|
||||
"secret_shares": 5,
|
||||
"secret_threshold": 3,
|
||||
})
|
||||
testResponseStatus(t, resp, 204)
|
||||
testResponseStatus(t, resp, 200)
|
||||
|
||||
resp = testHttpPut(t, token, addr+"/v1/sys/rekey/update", map[string]interface{}{
|
||||
"key": hex.EncodeToString(master),
|
||||
|
||||
Reference in New Issue
Block a user