1package command 2 3import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "strings" 9 10 "github.com/fatih/structs" 11 "github.com/hashicorp/vault/api" 12 "github.com/hashicorp/vault/helper/pgpkeys" 13 "github.com/hashicorp/vault/sdk/helper/password" 14 "github.com/mitchellh/cli" 15 "github.com/posener/complete" 16) 17 18var _ cli.Command = (*OperatorRekeyCommand)(nil) 19var _ cli.CommandAutocomplete = (*OperatorRekeyCommand)(nil) 20 21type OperatorRekeyCommand struct { 22 *BaseCommand 23 24 flagCancel bool 25 flagInit bool 26 flagKeyShares int 27 flagKeyThreshold int 28 flagNonce string 29 flagPGPKeys []string 30 flagStatus bool 31 flagTarget string 32 flagVerify bool 33 34 // Backup options 35 flagBackup bool 36 flagBackupDelete bool 37 flagBackupRetrieve bool 38 39 testStdin io.Reader // for tests 40} 41 42func (c *OperatorRekeyCommand) Synopsis() string { 43 return "Generates new unseal keys" 44} 45 46func (c *OperatorRekeyCommand) Help() string { 47 helpText := ` 48Usage: vault operator rekey [options] [KEY] 49 50 Generates a new set of unseal keys. This can optionally change the total 51 number of key shares or the required threshold of those key shares to 52 reconstruct the master key. This operation is zero downtime, but it requires 53 the Vault is unsealed and a quorum of existing unseal keys are provided. 54 55 An unseal key may be provided directly on the command line as an argument to 56 the command. If key is specified as "-", the command will read from stdin. If 57 a TTY is available, the command will prompt for text. 58 59 Initialize a rekey: 60 61 $ vault operator rekey \ 62 -init \ 63 -key-shares=15 \ 64 -key-threshold=9 65 66 Rekey and encrypt the resulting unseal keys with PGP: 67 68 $ vault operator rekey \ 69 -init \ 70 -key-shares=3 \ 71 -key-threshold=2 \ 72 -pgp-keys="keybase:hashicorp,keybase:jefferai,keybase:sethvargo" 73 74 Store encrypted PGP keys in Vault's core: 75 76 $ vault operator rekey \ 77 -init \ 78 -pgp-keys="..." \ 79 -backup 80 81 Retrieve backed-up unseal keys: 82 83 $ vault operator rekey -backup-retrieve 84 85 Delete backed-up unseal keys: 86 87 $ vault operator rekey -backup-delete 88 89` + c.Flags().Help() 90 return strings.TrimSpace(helpText) 91} 92 93func (c *OperatorRekeyCommand) Flags() *FlagSets { 94 set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) 95 96 f := set.NewFlagSet("Common Options") 97 98 f.BoolVar(&BoolVar{ 99 Name: "init", 100 Target: &c.flagInit, 101 Default: false, 102 Usage: "Initialize the rekeying operation. This can only be done if no " + 103 "rekeying operation is in progress. Customize the new number of key " + 104 "shares and key threshold using the -key-shares and -key-threshold " + 105 "flags.", 106 }) 107 108 f.BoolVar(&BoolVar{ 109 Name: "cancel", 110 Target: &c.flagCancel, 111 Default: false, 112 Usage: "Reset the rekeying progress. This will discard any submitted " + 113 "unseal keys or configuration.", 114 }) 115 116 f.BoolVar(&BoolVar{ 117 Name: "status", 118 Target: &c.flagStatus, 119 Default: false, 120 Usage: "Print the status of the current attempt without providing an " + 121 "unseal key.", 122 }) 123 124 f.IntVar(&IntVar{ 125 Name: "key-shares", 126 Aliases: []string{"n"}, 127 Target: &c.flagKeyShares, 128 Default: 5, 129 Completion: complete.PredictAnything, 130 Usage: "Number of key shares to split the generated master key into. " + 131 "This is the number of \"unseal keys\" to generate.", 132 }) 133 134 f.IntVar(&IntVar{ 135 Name: "key-threshold", 136 Aliases: []string{"t"}, 137 Target: &c.flagKeyThreshold, 138 Default: 3, 139 Completion: complete.PredictAnything, 140 Usage: "Number of key shares required to reconstruct the master key. " + 141 "This must be less than or equal to -key-shares.", 142 }) 143 144 f.StringVar(&StringVar{ 145 Name: "nonce", 146 Target: &c.flagNonce, 147 Default: "", 148 EnvVar: "", 149 Completion: complete.PredictAnything, 150 Usage: "Nonce value provided at initialization. The same nonce value " + 151 "must be provided with each unseal key.", 152 }) 153 154 f.StringVar(&StringVar{ 155 Name: "target", 156 Target: &c.flagTarget, 157 Default: "barrier", 158 EnvVar: "", 159 Completion: complete.PredictSet("barrier", "recovery"), 160 Usage: "Target for rekeying. \"recovery\" only applies when HSM support " + 161 "is enabled.", 162 }) 163 164 f.BoolVar(&BoolVar{ 165 Name: "verify", 166 Target: &c.flagVerify, 167 Default: false, 168 Usage: "Indicates that the action (-status, -cancel, or providing a key " + 169 "share) will be affecting verification for the current rekey " + 170 "attempt.", 171 }) 172 173 f.VarFlag(&VarFlag{ 174 Name: "pgp-keys", 175 Value: (*pgpkeys.PubKeyFilesFlag)(&c.flagPGPKeys), 176 Completion: complete.PredictAnything, 177 Usage: "Comma-separated list of paths to files on disk containing " + 178 "public GPG keys OR a comma-separated list of Keybase usernames using " + 179 "the format \"keybase:<username>\". When supplied, the generated " + 180 "unseal keys will be encrypted and base64-encoded in the order " + 181 "specified in this list.", 182 }) 183 184 f = set.NewFlagSet("Backup Options") 185 186 f.BoolVar(&BoolVar{ 187 Name: "backup", 188 Target: &c.flagBackup, 189 Default: false, 190 Usage: "Store a backup of the current PGP encrypted unseal keys in " + 191 "Vault's core. The encrypted values can be recovered in the event of " + 192 "failure or discarded after success. See the -backup-delete and " + 193 "-backup-retrieve options for more information. This option only " + 194 "applies when the existing unseal keys were PGP encrypted.", 195 }) 196 197 f.BoolVar(&BoolVar{ 198 Name: "backup-delete", 199 Target: &c.flagBackupDelete, 200 Default: false, 201 Usage: "Delete any stored backup unseal keys.", 202 }) 203 204 f.BoolVar(&BoolVar{ 205 Name: "backup-retrieve", 206 Target: &c.flagBackupRetrieve, 207 Default: false, 208 Usage: "Retrieve the backed-up unseal keys. This option is only available " + 209 "if the PGP keys were provided and the backup has not been deleted.", 210 }) 211 212 return set 213} 214 215func (c *OperatorRekeyCommand) AutocompleteArgs() complete.Predictor { 216 return complete.PredictAnything 217} 218 219func (c *OperatorRekeyCommand) AutocompleteFlags() complete.Flags { 220 return c.Flags().Completions() 221} 222 223func (c *OperatorRekeyCommand) Run(args []string) int { 224 f := c.Flags() 225 226 if err := f.Parse(args); err != nil { 227 c.UI.Error(err.Error()) 228 return 1 229 } 230 231 args = f.Args() 232 if len(args) > 1 { 233 c.UI.Error(fmt.Sprintf("Too many arguments (expected 0-1, got %d)", len(args))) 234 return 1 235 } 236 237 // Create the client 238 client, err := c.Client() 239 if err != nil { 240 c.UI.Error(err.Error()) 241 return 2 242 } 243 244 switch { 245 case c.flagBackupDelete: 246 return c.backupDelete(client) 247 case c.flagBackupRetrieve: 248 return c.backupRetrieve(client) 249 case c.flagCancel: 250 return c.cancel(client) 251 case c.flagInit: 252 return c.init(client) 253 case c.flagStatus: 254 return c.status(client) 255 default: 256 // If there are no other flags, prompt for an unseal key. 257 key := "" 258 if len(args) > 0 { 259 key = strings.TrimSpace(args[0]) 260 } 261 return c.provide(client, key) 262 } 263} 264 265// init starts the rekey process. 266func (c *OperatorRekeyCommand) init(client *api.Client) int { 267 // Handle the different API requests 268 var fn func(*api.RekeyInitRequest) (*api.RekeyStatusResponse, error) 269 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 270 case "barrier": 271 fn = client.Sys().RekeyInit 272 case "recovery", "hsm": 273 fn = client.Sys().RekeyRecoveryKeyInit 274 default: 275 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 276 return 1 277 } 278 279 // Make the request 280 status, err := fn(&api.RekeyInitRequest{ 281 SecretShares: c.flagKeyShares, 282 SecretThreshold: c.flagKeyThreshold, 283 PGPKeys: c.flagPGPKeys, 284 Backup: c.flagBackup, 285 RequireVerification: c.flagVerify, 286 }) 287 if err != nil { 288 c.UI.Error(fmt.Sprintf("Error initializing rekey: %s", err)) 289 return 2 290 } 291 292 // Print warnings about recovery, etc. 293 if len(c.flagPGPKeys) == 0 { 294 if Format(c.UI) == "table" { 295 c.UI.Warn(wrapAtLength( 296 "WARNING! If you lose the keys after they are returned, there is no " + 297 "recovery. Consider canceling this operation and re-initializing " + 298 "with the -pgp-keys flag to protect the returned unseal keys along " + 299 "with -backup to allow recovery of the encrypted keys in case of " + 300 "emergency. You can delete the stored keys later using the -delete " + 301 "flag.")) 302 c.UI.Output("") 303 } 304 } 305 if len(c.flagPGPKeys) > 0 && !c.flagBackup { 306 if Format(c.UI) == "table" { 307 c.UI.Warn(wrapAtLength( 308 "WARNING! You are using PGP keys for encrypted the resulting unseal " + 309 "keys, but you did not enable the option to backup the keys to " + 310 "Vault's core. If you lose the encrypted keys after they are " + 311 "returned, you will not be able to recover them. Consider canceling " + 312 "this operation and re-running with -backup to allow recovery of the " + 313 "encrypted unseal keys in case of emergency. You can delete the " + 314 "stored keys later using the -delete flag.")) 315 c.UI.Output("") 316 } 317 } 318 319 // Provide the current status 320 return c.printStatus(status) 321} 322 323// cancel is used to abort the rekey process. 324func (c *OperatorRekeyCommand) cancel(client *api.Client) int { 325 // Handle the different API requests 326 var fn func() error 327 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 328 case "barrier": 329 fn = client.Sys().RekeyCancel 330 if c.flagVerify { 331 fn = client.Sys().RekeyVerificationCancel 332 } 333 case "recovery", "hsm": 334 fn = client.Sys().RekeyRecoveryKeyCancel 335 if c.flagVerify { 336 fn = client.Sys().RekeyRecoveryKeyVerificationCancel 337 } 338 339 default: 340 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 341 return 1 342 } 343 344 // Make the request 345 if err := fn(); err != nil { 346 c.UI.Error(fmt.Sprintf("Error canceling rekey: %s", err)) 347 return 2 348 } 349 350 c.UI.Output("Success! Canceled rekeying (if it was started)") 351 return 0 352} 353 354// provide prompts the user for the seal key and posts it to the update root 355// endpoint. If this is the last unseal, this function outputs it. 356func (c *OperatorRekeyCommand) provide(client *api.Client, key string) int { 357 var statusFn func() (interface{}, error) 358 var updateFn func(string, string) (interface{}, error) 359 360 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 361 case "barrier": 362 statusFn = func() (interface{}, error) { 363 return client.Sys().RekeyStatus() 364 } 365 updateFn = func(s1 string, s2 string) (interface{}, error) { 366 return client.Sys().RekeyUpdate(s1, s2) 367 } 368 if c.flagVerify { 369 statusFn = func() (interface{}, error) { 370 return client.Sys().RekeyVerificationStatus() 371 } 372 updateFn = func(s1 string, s2 string) (interface{}, error) { 373 return client.Sys().RekeyVerificationUpdate(s1, s2) 374 } 375 } 376 case "recovery", "hsm": 377 statusFn = func() (interface{}, error) { 378 return client.Sys().RekeyRecoveryKeyStatus() 379 } 380 updateFn = func(s1 string, s2 string) (interface{}, error) { 381 return client.Sys().RekeyRecoveryKeyUpdate(s1, s2) 382 } 383 if c.flagVerify { 384 statusFn = func() (interface{}, error) { 385 return client.Sys().RekeyRecoveryKeyVerificationStatus() 386 } 387 updateFn = func(s1 string, s2 string) (interface{}, error) { 388 return client.Sys().RekeyRecoveryKeyVerificationUpdate(s1, s2) 389 } 390 } 391 default: 392 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 393 return 1 394 } 395 396 status, err := statusFn() 397 if err != nil { 398 c.UI.Error(fmt.Sprintf("Error getting rekey status: %s", err)) 399 return 2 400 } 401 402 var started bool 403 var nonce string 404 405 switch status.(type) { 406 case *api.RekeyStatusResponse: 407 stat := status.(*api.RekeyStatusResponse) 408 started = stat.Started 409 nonce = stat.Nonce 410 case *api.RekeyVerificationStatusResponse: 411 stat := status.(*api.RekeyVerificationStatusResponse) 412 started = stat.Started 413 nonce = stat.Nonce 414 default: 415 c.UI.Error("Unknown status type") 416 return 1 417 } 418 419 // Verify a root token generation is in progress. If there is not one in 420 // progress, return an error instructing the user to start one. 421 if !started { 422 c.UI.Error(wrapAtLength( 423 "No rekey is in progress. Start a rekey process by running " + 424 "\"vault operator rekey -init\".")) 425 return 1 426 } 427 428 switch key { 429 case "-": // Read from stdin 430 nonce = c.flagNonce 431 432 // Pull our fake stdin if needed 433 stdin := (io.Reader)(os.Stdin) 434 if c.testStdin != nil { 435 stdin = c.testStdin 436 } 437 438 var buf bytes.Buffer 439 if _, err := io.Copy(&buf, stdin); err != nil { 440 c.UI.Error(fmt.Sprintf("Failed to read from stdin: %s", err)) 441 return 1 442 } 443 444 key = buf.String() 445 case "": // Prompt using the tty 446 // Nonce value is not required if we are prompting via the terminal 447 w := getWriterFromUI(c.UI) 448 fmt.Fprintf(w, "Rekey operation nonce: %s\n", nonce) 449 fmt.Fprintf(w, "Unseal Key (will be hidden): ") 450 key, err = password.Read(os.Stdin) 451 fmt.Fprintf(w, "\n") 452 if err != nil { 453 if err == password.ErrInterrupted { 454 c.UI.Error("user canceled") 455 return 1 456 } 457 458 c.UI.Error(wrapAtLength(fmt.Sprintf("An error occurred attempting to "+ 459 "ask for the unseal key. The raw error message is shown below, but "+ 460 "usually this is because you attempted to pipe a value into the "+ 461 "command or you are executing outside of a terminal (tty). If you "+ 462 "want to pipe the value, pass \"-\" as the argument to read from "+ 463 "stdin. The raw error was: %s", err))) 464 return 1 465 } 466 default: // Supplied directly as an arg 467 nonce = c.flagNonce 468 } 469 470 // Trim any whitespace from they key, especially since we might have 471 // prompted the user for it. 472 key = strings.TrimSpace(key) 473 474 // Verify we have a nonce value 475 if nonce == "" { 476 c.UI.Error("Missing nonce value: specify it via the -nonce flag") 477 return 1 478 } 479 480 // Provide the key, this may potentially complete the update 481 resp, err := updateFn(key, nonce) 482 if err != nil { 483 c.UI.Error(fmt.Sprintf("Error posting unseal key: %s", err)) 484 return 2 485 } 486 487 var complete bool 488 var mightContainUnsealKeys bool 489 490 switch resp.(type) { 491 case *api.RekeyUpdateResponse: 492 complete = resp.(*api.RekeyUpdateResponse).Complete 493 mightContainUnsealKeys = true 494 case *api.RekeyVerificationUpdateResponse: 495 complete = resp.(*api.RekeyVerificationUpdateResponse).Complete 496 default: 497 c.UI.Error("Unknown update response type") 498 return 1 499 } 500 501 if !complete { 502 return c.status(client) 503 } 504 505 if mightContainUnsealKeys { 506 return c.printUnsealKeys(client, status.(*api.RekeyStatusResponse), 507 resp.(*api.RekeyUpdateResponse)) 508 } 509 510 c.UI.Output(wrapAtLength("Rekey verification successful. The rekey operation is complete and the new keys are now active.")) 511 return 0 512} 513 514// status is used just to fetch and dump the status. 515func (c *OperatorRekeyCommand) status(client *api.Client) int { 516 // Handle the different API requests 517 var fn func() (interface{}, error) 518 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 519 case "barrier": 520 fn = func() (interface{}, error) { 521 return client.Sys().RekeyStatus() 522 } 523 if c.flagVerify { 524 fn = func() (interface{}, error) { 525 return client.Sys().RekeyVerificationStatus() 526 } 527 } 528 case "recovery", "hsm": 529 fn = func() (interface{}, error) { 530 return client.Sys().RekeyRecoveryKeyStatus() 531 } 532 if c.flagVerify { 533 fn = func() (interface{}, error) { 534 return client.Sys().RekeyRecoveryKeyVerificationStatus() 535 } 536 } 537 default: 538 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 539 return 1 540 } 541 542 // Make the request 543 status, err := fn() 544 if err != nil { 545 c.UI.Error(fmt.Sprintf("Error reading rekey status: %s", err)) 546 return 2 547 } 548 549 return c.printStatus(status) 550} 551 552// backupRetrieve retrieves the stored backup keys. 553func (c *OperatorRekeyCommand) backupRetrieve(client *api.Client) int { 554 // Handle the different API requests 555 var fn func() (*api.RekeyRetrieveResponse, error) 556 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 557 case "barrier": 558 fn = client.Sys().RekeyRetrieveBackup 559 case "recovery", "hsm": 560 fn = client.Sys().RekeyRetrieveRecoveryBackup 561 default: 562 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 563 return 1 564 } 565 566 // Make the request 567 storedKeys, err := fn() 568 if err != nil { 569 c.UI.Error(fmt.Sprintf("Error retrieving rekey stored keys: %s", err)) 570 return 2 571 } 572 573 secret := &api.Secret{ 574 Data: structs.New(storedKeys).Map(), 575 } 576 577 return OutputSecret(c.UI, secret) 578} 579 580// backupDelete deletes the stored backup keys. 581func (c *OperatorRekeyCommand) backupDelete(client *api.Client) int { 582 // Handle the different API requests 583 var fn func() error 584 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 585 case "barrier": 586 fn = client.Sys().RekeyDeleteBackup 587 case "recovery", "hsm": 588 fn = client.Sys().RekeyDeleteRecoveryBackup 589 default: 590 c.UI.Error(fmt.Sprintf("Unknown target: %s", c.flagTarget)) 591 return 1 592 } 593 594 // Make the request 595 if err := fn(); err != nil { 596 c.UI.Error(fmt.Sprintf("Error deleting rekey stored keys: %s", err)) 597 return 2 598 } 599 600 c.UI.Output("Success! Delete stored keys (if they existed)") 601 return 0 602} 603 604// printStatus dumps the status to output 605func (c *OperatorRekeyCommand) printStatus(in interface{}) int { 606 out := []string{} 607 out = append(out, "Key | Value") 608 609 switch in.(type) { 610 case *api.RekeyStatusResponse: 611 status := in.(*api.RekeyStatusResponse) 612 out = append(out, fmt.Sprintf("Nonce | %s", status.Nonce)) 613 out = append(out, fmt.Sprintf("Started | %t", status.Started)) 614 if status.Started { 615 if status.Progress == status.Required { 616 out = append(out, fmt.Sprintf("Rekey Progress | %d/%d (verification in progress)", status.Progress, status.Required)) 617 } else { 618 out = append(out, fmt.Sprintf("Rekey Progress | %d/%d", status.Progress, status.Required)) 619 } 620 out = append(out, fmt.Sprintf("New Shares | %d", status.N)) 621 out = append(out, fmt.Sprintf("New Threshold | %d", status.T)) 622 out = append(out, fmt.Sprintf("Verification Required | %t", status.VerificationRequired)) 623 if status.VerificationNonce != "" { 624 out = append(out, fmt.Sprintf("Verification Nonce | %s", status.VerificationNonce)) 625 } 626 } 627 if len(status.PGPFingerprints) > 0 { 628 out = append(out, fmt.Sprintf("PGP Fingerprints | %s", status.PGPFingerprints)) 629 out = append(out, fmt.Sprintf("Backup | %t", status.Backup)) 630 } 631 case *api.RekeyVerificationStatusResponse: 632 status := in.(*api.RekeyVerificationStatusResponse) 633 out = append(out, fmt.Sprintf("Started | %t", status.Started)) 634 out = append(out, fmt.Sprintf("New Shares | %d", status.N)) 635 out = append(out, fmt.Sprintf("New Threshold | %d", status.T)) 636 out = append(out, fmt.Sprintf("Verification Nonce | %s", status.Nonce)) 637 out = append(out, fmt.Sprintf("Verification Progress | %d/%d", status.Progress, status.T)) 638 default: 639 c.UI.Error("Unknown status type") 640 return 1 641 } 642 643 switch Format(c.UI) { 644 case "table": 645 c.UI.Output(tableOutput(out, nil)) 646 return 0 647 default: 648 return OutputData(c.UI, in) 649 } 650} 651 652func (c *OperatorRekeyCommand) printUnsealKeys(client *api.Client, status *api.RekeyStatusResponse, resp *api.RekeyUpdateResponse) int { 653 switch Format(c.UI) { 654 case "table": 655 default: 656 return OutputData(c.UI, resp) 657 } 658 659 // Space between the key prompt, if any, and the output 660 c.UI.Output("") 661 662 // Provide the keys 663 var haveB64 bool 664 if resp.KeysB64 != nil && len(resp.KeysB64) == len(resp.Keys) { 665 haveB64 = true 666 } 667 for i, key := range resp.Keys { 668 if len(resp.PGPFingerprints) > 0 { 669 if haveB64 { 670 c.UI.Output(fmt.Sprintf("Key %d fingerprint: %s; value: %s", i+1, resp.PGPFingerprints[i], resp.KeysB64[i])) 671 } else { 672 c.UI.Output(fmt.Sprintf("Key %d fingerprint: %s; value: %s", i+1, resp.PGPFingerprints[i], key)) 673 } 674 } else { 675 if haveB64 { 676 c.UI.Output(fmt.Sprintf("Key %d: %s", i+1, resp.KeysB64[i])) 677 } else { 678 c.UI.Output(fmt.Sprintf("Key %d: %s", i+1, key)) 679 } 680 } 681 } 682 683 c.UI.Output("") 684 c.UI.Output(fmt.Sprintf("Operation nonce: %s", resp.Nonce)) 685 686 if len(resp.PGPFingerprints) > 0 && resp.Backup { 687 c.UI.Output("") 688 switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { 689 case "barrier": 690 c.UI.Output(wrapAtLength(fmt.Sprintf( 691 "The encrypted unseal keys are backed up to \"core/unseal-keys-backup\" " + 692 "in the storage backend. Remove these keys at any time using " + 693 "\"vault operator rekey -backup-delete\". Vault does not automatically " + 694 "remove these keys.", 695 ))) 696 case "recovery", "hsm": 697 c.UI.Output(wrapAtLength(fmt.Sprintf( 698 "The encrypted unseal keys are backed up to \"core/recovery-keys-backup\" " + 699 "in the storage backend. Remove these keys at any time using " + 700 "\"vault operator rekey -backup-delete -target=recovery\". Vault does not automatically " + 701 "remove these keys.", 702 ))) 703 } 704 } 705 706 switch status.VerificationRequired { 707 case false: 708 c.UI.Output("") 709 c.UI.Output(wrapAtLength(fmt.Sprintf( 710 "Vault rekeyed with %d key shares and a key threshold of %d. Please "+ 711 "securely distribute the key shares printed above. When Vault is "+ 712 "re-sealed, restarted, or stopped, you must supply at least %d of "+ 713 "these keys to unseal it before it can start servicing requests.", 714 status.N, 715 status.T, 716 status.T))) 717 default: 718 c.UI.Output("") 719 c.UI.Output(wrapAtLength(fmt.Sprintf( 720 "Vault has created a new key, split into %d key shares and a key threshold "+ 721 "of %d. These will not be active until after verification is complete. "+ 722 "Please securely distribute the key shares printed above. When Vault "+ 723 "is re-sealed, restarted, or stopped, you must supply at least %d of "+ 724 "these keys to unseal it before it can start servicing requests.", 725 status.N, 726 status.T, 727 status.T))) 728 c.UI.Output("") 729 c.UI.Warn(wrapAtLength( 730 "Again, these key shares are _not_ valid until verification is performed. " + 731 "Do not lose or discard your current key shares until after verification " + 732 "is complete or you will be unable to unseal Vault. If you cancel the " + 733 "rekey process or seal Vault before verification is complete the new " + 734 "shares will be discarded and the current shares will remain valid.", 735 )) 736 c.UI.Output("") 737 c.UI.Warn(wrapAtLength( 738 "The current verification status, including initial nonce, is shown below.", 739 )) 740 c.UI.Output("") 741 742 c.flagVerify = true 743 return c.status(client) 744 } 745 746 return 0 747} 748