parent
635fd18bf6
commit
14b65ff4db
|
@ -296,6 +296,7 @@ type RekeyRetrieveResponse struct {
|
|||
|
||||
type RekeyVerificationStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
|
|
|
@ -29,6 +29,7 @@ type OperatorRekeyCommand struct {
|
|||
flagPGPKeys []string
|
||||
flagStatus bool
|
||||
flagTarget string
|
||||
flagVerify bool
|
||||
|
||||
// Backup options
|
||||
flagBackup bool
|
||||
|
@ -167,6 +168,15 @@ func (c *OperatorRekeyCommand) Flags() *FlagSets {
|
|||
"is enabled.",
|
||||
})
|
||||
|
||||
f.BoolVar(&BoolVar{
|
||||
Name: "verify",
|
||||
Target: &c.flagVerify,
|
||||
Default: false,
|
||||
Usage: "Indicates that the action (-status, -cancel, or providing a key " +
|
||||
"share) will be affecting verification for the current rekey " +
|
||||
"attempt.",
|
||||
})
|
||||
|
||||
f.VarFlag(&VarFlag{
|
||||
Name: "pgp-keys",
|
||||
Value: (*pgpkeys.PubKeyFilesFlag)(&c.flagPGPKeys),
|
||||
|
@ -336,6 +346,7 @@ func (c *OperatorRekeyCommand) init(client *api.Client) int {
|
|||
StoredShares: c.flagStoredShares,
|
||||
PGPKeys: c.flagPGPKeys,
|
||||
Backup: c.flagBackup,
|
||||
RequireVerification: c.flagVerify,
|
||||
})
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Error initializing rekey: %s", err))
|
||||
|
@ -376,8 +387,15 @@ func (c *OperatorRekeyCommand) cancel(client *api.Client) int {
|
|||
switch strings.ToLower(strings.TrimSpace(c.flagTarget)) {
|
||||
case "barrier":
|
||||
fn = client.Sys().RekeyCancel
|
||||
if c.flagVerify {
|
||||
fn = client.Sys().RekeyVerificationCancel
|
||||
}
|
||||
case "recovery", "hsm":
|
||||
fn = client.Sys().RekeyRecoveryKeyCancel
|
||||
if c.flagVerify {
|
||||
fn = client.Sys().RekeyRecoveryKeyVerificationCancel
|
||||
}
|
||||
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget))
|
||||
return 1
|
||||
|
@ -396,16 +414,40 @@ func (c *OperatorRekeyCommand) cancel(client *api.Client) int {
|
|||
// provide prompts the user for the seal key and posts it to the update root
|
||||
// endpoint. If this is the last unseal, this function outputs it.
|
||||
func (c *OperatorRekeyCommand) provide(client *api.Client, key string) int {
|
||||
var statusFn func() (*api.RekeyStatusResponse, error)
|
||||
var updateFn func(string, string) (*api.RekeyUpdateResponse, error)
|
||||
var statusFn func() (interface{}, error)
|
||||
var updateFn func(string, string) (interface{}, error)
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(c.flagTarget)) {
|
||||
case "barrier":
|
||||
statusFn = client.Sys().RekeyStatus
|
||||
updateFn = client.Sys().RekeyUpdate
|
||||
statusFn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyStatus()
|
||||
}
|
||||
updateFn = func(s1 string, s2 string) (interface{}, error) {
|
||||
return client.Sys().RekeyUpdate(s1, s2)
|
||||
}
|
||||
if c.flagVerify {
|
||||
statusFn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyVerificationStatus()
|
||||
}
|
||||
updateFn = func(s1 string, s2 string) (interface{}, error) {
|
||||
return client.Sys().RekeyVerificationUpdate(s1, s2)
|
||||
}
|
||||
}
|
||||
case "recovery", "hsm":
|
||||
statusFn = client.Sys().RekeyRecoveryKeyStatus
|
||||
updateFn = client.Sys().RekeyRecoveryKeyUpdate
|
||||
statusFn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyStatus()
|
||||
}
|
||||
updateFn = func(s1 string, s2 string) (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyUpdate(s1, s2)
|
||||
}
|
||||
if c.flagVerify {
|
||||
statusFn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyVerificationStatus()
|
||||
}
|
||||
updateFn = func(s1 string, s2 string) (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyVerificationUpdate(s1, s2)
|
||||
}
|
||||
}
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget))
|
||||
return 1
|
||||
|
@ -417,17 +459,32 @@ func (c *OperatorRekeyCommand) provide(client *api.Client, key string) int {
|
|||
return 2
|
||||
}
|
||||
|
||||
var started bool
|
||||
var nonce string
|
||||
|
||||
switch status.(type) {
|
||||
case *api.RekeyStatusResponse:
|
||||
stat := status.(*api.RekeyStatusResponse)
|
||||
started = stat.Started
|
||||
nonce = stat.Nonce
|
||||
case *api.RekeyVerificationStatusResponse:
|
||||
stat := status.(*api.RekeyVerificationStatusResponse)
|
||||
started = stat.Started
|
||||
nonce = stat.Nonce
|
||||
default:
|
||||
c.UI.Error("Unknown status type")
|
||||
return 1
|
||||
}
|
||||
|
||||
// Verify a root token generation is in progress. If there is not one in
|
||||
// progress, return an error instructing the user to start one.
|
||||
if !status.Started {
|
||||
if !started {
|
||||
c.UI.Error(wrapAtLength(
|
||||
"No rekey is in progress. Start a rekey process by running " +
|
||||
"\"vault rekey -init\"."))
|
||||
return 1
|
||||
}
|
||||
|
||||
var nonce string
|
||||
|
||||
switch key {
|
||||
case "-": // Read from stdin
|
||||
nonce = c.flagNonce
|
||||
|
@ -447,8 +504,6 @@ func (c *OperatorRekeyCommand) provide(client *api.Client, key string) int {
|
|||
key = buf.String()
|
||||
case "": // Prompt using the tty
|
||||
// Nonce value is not required if we are prompting via the terminal
|
||||
nonce = status.Nonce
|
||||
|
||||
w := getWriterFromUI(c.UI)
|
||||
fmt.Fprintf(w, "Rekey operation nonce: %s\n", nonce)
|
||||
fmt.Fprintf(w, "Unseal Key (will be hidden): ")
|
||||
|
@ -489,22 +544,56 @@ func (c *OperatorRekeyCommand) provide(client *api.Client, key string) int {
|
|||
return 2
|
||||
}
|
||||
|
||||
if !resp.Complete {
|
||||
var complete bool
|
||||
var mightContainUnsealKeys bool
|
||||
|
||||
switch resp.(type) {
|
||||
case *api.RekeyUpdateResponse:
|
||||
complete = resp.(*api.RekeyUpdateResponse).Complete
|
||||
mightContainUnsealKeys = true
|
||||
case *api.RekeyVerificationUpdateResponse:
|
||||
complete = resp.(*api.RekeyVerificationUpdateResponse).Complete
|
||||
default:
|
||||
c.UI.Error("Unknown update response type")
|
||||
return 1
|
||||
}
|
||||
|
||||
if !complete {
|
||||
return c.status(client)
|
||||
}
|
||||
|
||||
return c.printUnsealKeys(status, resp)
|
||||
if mightContainUnsealKeys {
|
||||
return c.printUnsealKeys(client, status.(*api.RekeyStatusResponse),
|
||||
resp.(*api.RekeyUpdateResponse))
|
||||
}
|
||||
|
||||
c.UI.Output("Rekey verification successful.")
|
||||
return 0
|
||||
}
|
||||
|
||||
// status is used just to fetch and dump the status.
|
||||
func (c *OperatorRekeyCommand) status(client *api.Client) int {
|
||||
// Handle the different API requests
|
||||
var fn func() (*api.RekeyStatusResponse, error)
|
||||
var fn func() (interface{}, error)
|
||||
switch strings.ToLower(strings.TrimSpace(c.flagTarget)) {
|
||||
case "barrier":
|
||||
fn = client.Sys().RekeyStatus
|
||||
fn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyStatus()
|
||||
}
|
||||
if c.flagVerify {
|
||||
fn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyVerificationStatus()
|
||||
}
|
||||
}
|
||||
case "recovery", "hsm":
|
||||
fn = client.Sys().RekeyRecoveryKeyStatus
|
||||
fn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyStatus()
|
||||
}
|
||||
if c.flagVerify {
|
||||
fn = func() (interface{}, error) {
|
||||
return client.Sys().RekeyRecoveryKeyVerificationStatus()
|
||||
}
|
||||
}
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget))
|
||||
return 1
|
||||
|
@ -573,33 +662,47 @@ func (c *OperatorRekeyCommand) backupDelete(client *api.Client) int {
|
|||
}
|
||||
|
||||
// printStatus dumps the status to output
|
||||
func (c *OperatorRekeyCommand) printStatus(status *api.RekeyStatusResponse) int {
|
||||
func (c *OperatorRekeyCommand) printStatus(in interface{}) int {
|
||||
out := []string{}
|
||||
out = append(out, "Key | Value")
|
||||
|
||||
switch in.(type) {
|
||||
case *api.RekeyStatusResponse:
|
||||
status := in.(*api.RekeyStatusResponse)
|
||||
out = append(out, fmt.Sprintf("Nonce | %s", status.Nonce))
|
||||
out = append(out, fmt.Sprintf("Started | %t", status.Started))
|
||||
|
||||
if status.Started {
|
||||
out = append(out, fmt.Sprintf("Rekey Progress | %d/%d", status.Progress, status.Required))
|
||||
out = append(out, fmt.Sprintf("New Shares | %d", status.N))
|
||||
out = append(out, fmt.Sprintf("New Threshold | %d", status.T))
|
||||
out = append(out, fmt.Sprintf("Verification Required | %t", status.VerificationRequired))
|
||||
}
|
||||
|
||||
if len(status.PGPFingerprints) > 0 {
|
||||
out = append(out, fmt.Sprintf("PGP Fingerprints | %s", status.PGPFingerprints))
|
||||
out = append(out, fmt.Sprintf("Backup | %t", status.Backup))
|
||||
}
|
||||
case *api.RekeyVerificationStatusResponse:
|
||||
status := in.(*api.RekeyVerificationStatusResponse)
|
||||
out = append(out, fmt.Sprintf("Nonce | %s", status.Nonce))
|
||||
out = append(out, fmt.Sprintf("Started | %t", status.Started))
|
||||
out = append(out, fmt.Sprintf("New Shares | %d", status.N))
|
||||
out = append(out, fmt.Sprintf("New Threshold | %d", status.T))
|
||||
out = append(out, fmt.Sprintf("Verification Progress | %d/%d", status.Progress, status.T))
|
||||
default:
|
||||
c.UI.Error("Unknown status type")
|
||||
return 1
|
||||
}
|
||||
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
c.UI.Output(tableOutput(out, nil))
|
||||
return 0
|
||||
default:
|
||||
return OutputData(c.UI, status)
|
||||
return OutputData(c.UI, in)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OperatorRekeyCommand) printUnsealKeys(status *api.RekeyStatusResponse, resp *api.RekeyUpdateResponse) int {
|
||||
func (c *OperatorRekeyCommand) printUnsealKeys(client *api.Client, status *api.RekeyStatusResponse, resp *api.RekeyUpdateResponse) int {
|
||||
switch Format(c.UI) {
|
||||
case "table":
|
||||
default:
|
||||
|
@ -643,15 +746,40 @@ func (c *OperatorRekeyCommand) printUnsealKeys(status *api.RekeyStatusResponse,
|
|||
)))
|
||||
}
|
||||
|
||||
switch status.VerificationRequired {
|
||||
case false:
|
||||
c.UI.Output("")
|
||||
c.UI.Output(wrapAtLength(fmt.Sprintf(
|
||||
"Vault rekeyed with %d key shares an a key threshold of %d. Please "+
|
||||
"securely distributed the key shares printed above. When the Vault is "+
|
||||
"Vault rekeyed with %d key shares and a key threshold of %d. Please "+
|
||||
"securely distributed the key shares printed above. When Vault is "+
|
||||
"re-sealed, restarted, or stopped, you must supply at least %d of "+
|
||||
"these keys to unseal it before it can start servicing requests.",
|
||||
status.N,
|
||||
status.T,
|
||||
status.T)))
|
||||
default:
|
||||
c.UI.Output("")
|
||||
c.UI.Output(wrapAtLength(fmt.Sprintf(
|
||||
"Vault has created a new key, split into %d key shares and a key threshold "+
|
||||
"of %d. These will not be active until after verification is complete. "+
|
||||
"Please securely distributed the key shares printed above. When Vault "+
|
||||
"is re-sealed, restarted, or stopped, you must supply at least %d of "+
|
||||
"these keys to unseal it before it can start servicing requests.",
|
||||
status.N,
|
||||
status.T,
|
||||
status.T)))
|
||||
c.UI.Output("")
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"Again, these key shares are _not_ valid until verification is performed. " +
|
||||
"Do not lose or discard your current key shares until after verification " +
|
||||
"is complete or you will be unable to unseal Vault. The current " +
|
||||
"verification status, including initial nonce, is shown below.",
|
||||
))
|
||||
c.UI.Output("")
|
||||
|
||||
c.flagVerify = true
|
||||
return c.status(client)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool,
|
|||
}
|
||||
if rekeyConf != nil {
|
||||
// Get the progress
|
||||
progress, err := core.RekeyProgress(recovery, false)
|
||||
started, progress, err := core.RekeyProgress(recovery, false)
|
||||
if err != nil {
|
||||
respondError(w, err.Code(), err)
|
||||
return
|
||||
|
@ -85,7 +85,7 @@ func handleSysRekeyInitGet(ctx context.Context, core *vault.Core, recovery bool,
|
|||
}
|
||||
|
||||
status.Nonce = rekeyConf.Nonce
|
||||
status.Started = true
|
||||
status.Started = started
|
||||
status.T = rekeyConf.SecretThreshold
|
||||
status.N = rekeyConf.SecretShares
|
||||
status.Progress = progress
|
||||
|
@ -290,7 +290,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
|
|||
}
|
||||
|
||||
// Get the progress
|
||||
progress, err := core.RekeyProgress(recovery, true)
|
||||
started, progress, err := core.RekeyProgress(recovery, true)
|
||||
if err != nil {
|
||||
respondError(w, err.Code(), err)
|
||||
return
|
||||
|
@ -298,6 +298,7 @@ func handleSysRekeyVerifyGet(ctx context.Context, core *vault.Core, recovery boo
|
|||
|
||||
// Format the status
|
||||
status := &RekeyVerificationStatusResponse{
|
||||
Started: started,
|
||||
Nonce: rekeyConf.VerificationNonce,
|
||||
T: rekeyConf.SecretThreshold,
|
||||
N: rekeyConf.SecretShares,
|
||||
|
@ -410,6 +411,7 @@ type RekeyVerificationUpdateRequest struct {
|
|||
|
||||
type RekeyVerificationStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
|
|
|
@ -92,14 +92,14 @@ func (c *Core) RekeyThreshold(ctx context.Context, recovery bool) (int, logical.
|
|||
}
|
||||
|
||||
// RekeyProgress is used to return the rekey progress (num shares).
|
||||
func (c *Core) RekeyProgress(recovery, verification bool) (int, logical.HTTPCodedError) {
|
||||
func (c *Core) RekeyProgress(recovery, verification bool) (bool, int, logical.HTTPCodedError) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
|
||||
return false, 0, logical.CodedError(http.StatusServiceUnavailable, consts.ErrSealed.Error())
|
||||
}
|
||||
if c.standby {
|
||||
return 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
|
||||
return false, 0, logical.CodedError(http.StatusBadRequest, consts.ErrStandby.Error())
|
||||
}
|
||||
|
||||
c.rekeyLock.RLock()
|
||||
|
@ -112,10 +112,14 @@ func (c *Core) RekeyProgress(recovery, verification bool) (int, logical.HTTPCode
|
|||
conf = c.barrierRekeyConfig
|
||||
}
|
||||
|
||||
if verification {
|
||||
return len(conf.VerificationProgress), nil
|
||||
if conf == nil {
|
||||
return false, 0, logical.CodedError(http.StatusBadRequest, "rekey operation not in progress")
|
||||
}
|
||||
return len(conf.RekeyProgress), nil
|
||||
|
||||
if verification {
|
||||
return len(conf.VerificationKey) > 0, len(conf.VerificationProgress), nil
|
||||
}
|
||||
return true, len(conf.RekeyProgress), nil
|
||||
}
|
||||
|
||||
// RekeyConfig is used to read the rekey configuration
|
||||
|
@ -755,6 +759,10 @@ func (c *Core) RekeyVerify(ctx context.Context, key []byte, nonce string, recove
|
|||
return nil, logical.CodedError(http.StatusBadRequest, "no rekey in progress")
|
||||
}
|
||||
|
||||
if len(c.barrierRekeyConfig.VerificationKey) == 0 {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, "no rekey verification in progress")
|
||||
}
|
||||
|
||||
if nonce != config.VerificationNonce {
|
||||
return nil, logical.CodedError(http.StatusBadRequest, fmt.Sprintf("incorrect nonce supplied; nonce for this verify operation is %q", config.VerificationNonce))
|
||||
}
|
||||
|
@ -854,7 +862,7 @@ func (c *Core) RekeyCancel(recovery bool) logical.HTTPCodedError {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RekeyVerifyCancel is used to start the verification process over
|
||||
// RekeyVerifyRestart is used to start the verification process over
|
||||
func (c *Core) RekeyVerifyRestart(recovery bool) logical.HTTPCodedError {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
|
|
Loading…
Reference in New Issue