SealInterface
This commit is contained in:
parent
b47d6dc64f
commit
afae46feb7
|
@ -31,9 +31,13 @@ func (c *Sys) Init(opts *InitRequest) (*InitResponse, error) {
|
|||
}
|
||||
|
||||
type InitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
RecoveryShares int `json:"recovery_shares"`
|
||||
RecoveryThreshold int `json:"recovery_threshold"`
|
||||
RecoveryPGPKeys []string `json:"recovery_pgp_keys"`
|
||||
}
|
||||
|
||||
type InitStatusResponse struct {
|
||||
|
@ -41,6 +45,7 @@ type InitStatusResponse struct {
|
|||
}
|
||||
|
||||
type InitResponse struct {
|
||||
Keys []string
|
||||
RootToken string `json:"root_token"`
|
||||
Keys []string
|
||||
RecoveryKeys []string `json:"recovery_keys"`
|
||||
RootToken string `json:"root_token"`
|
||||
}
|
||||
|
|
|
@ -13,6 +13,19 @@ func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) {
|
|||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyStatus() (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey-recovery-key/init")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
|
@ -30,6 +43,23 @@ func (c *Sys) RekeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error)
|
|||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/init")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
|
@ -39,6 +69,15 @@ func (c *Sys) RekeyCancel() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey-recovery-key/init")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
|
@ -61,6 +100,28 @@ func (c *Sys) RekeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
|||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/update")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/backup")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
|
@ -74,6 +135,19 @@ func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) {
|
|||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRetrieveRecoveryBackup() (*RekeyRetrieveResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-backup")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyRetrieveResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyDeleteBackup() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/backup")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
|
@ -84,6 +158,16 @@ func (c *Sys) RekeyDeleteBackup() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyDeleteRecoveryBackup() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-backup")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type RekeyInitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
|
|
115
command/init.go
115
command/init.go
|
@ -15,15 +15,19 @@ type InitCommand struct {
|
|||
}
|
||||
|
||||
func (c *InitCommand) Run(args []string) int {
|
||||
var threshold, shares int
|
||||
var pgpKeys pgpkeys.PubKeyFilesFlag
|
||||
var threshold, shares, storedShares, recoveryThreshold, recoveryShares int
|
||||
var pgpKeys, recoveryPgpKeys pgpkeys.PubKeyFilesFlag
|
||||
var check bool
|
||||
flags := c.Meta.FlagSet("init", meta.FlagSetDefault)
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
flags.IntVar(&shares, "key-shares", 5, "")
|
||||
flags.IntVar(&threshold, "key-threshold", 3, "")
|
||||
flags.BoolVar(&check, "check", false, "")
|
||||
flags.IntVar(&storedShares, "stored-shares", 0, "")
|
||||
flags.Var(&pgpKeys, "pgp-keys", "")
|
||||
flags.IntVar(&recoveryShares, "recovery-key-shares", 0, "")
|
||||
flags.IntVar(&recoveryThreshold, "recovery-key-threshold", 0, "")
|
||||
flags.Var(&recoveryPgpKeys, "recovery-key-pgp-keys", "")
|
||||
flags.BoolVar(&check, "check", false, "")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
@ -40,9 +44,13 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
resp, err := client.Sys().Init(&api.InitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
StoredShares: storedShares,
|
||||
PGPKeys: pgpKeys,
|
||||
RecoveryShares: recoveryShares,
|
||||
RecoveryThreshold: recoveryThreshold,
|
||||
RecoveryPGPKeys: recoveryPgpKeys,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
|
@ -51,24 +59,43 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
for i, key := range resp.Keys {
|
||||
c.Ui.Output(fmt.Sprintf("Key %d: %s", i+1, key))
|
||||
c.Ui.Output(fmt.Sprintf("Unseal Key %d: %s", i+1, key))
|
||||
}
|
||||
for i, key := range resp.RecoveryKeys {
|
||||
c.Ui.Output(fmt.Sprintf("Recovery Key %d: %s", i+1, key))
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf("Initial Root Token: %s", resp.RootToken))
|
||||
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"\n"+
|
||||
"Vault initialized with %d keys and a key threshold of %d. Please\n"+
|
||||
"securely distribute the above keys. When the Vault is re-sealed,\n"+
|
||||
"restarted, or stopped, you must provide at least %d of these keys\n"+
|
||||
"to unseal it again.\n\n"+
|
||||
"Vault does not store the master key. Without at least %d keys,\n"+
|
||||
"your Vault will remain permanently sealed.",
|
||||
shares,
|
||||
threshold,
|
||||
threshold,
|
||||
threshold,
|
||||
))
|
||||
if storedShares < 1 {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"\n"+
|
||||
"Vault initialized with %d keys and a key threshold of %d. Please\n"+
|
||||
"securely distribute the above keys. When the Vault is re-sealed,\n"+
|
||||
"restarted, or stopped, you must provide at least %d of these keys\n"+
|
||||
"to unseal it again.\n\n"+
|
||||
"Vault does not store the master key. Without at least %d keys,\n"+
|
||||
"your Vault will remain permanently sealed.",
|
||||
shares,
|
||||
threshold,
|
||||
threshold,
|
||||
threshold,
|
||||
))
|
||||
} else {
|
||||
c.Ui.Output(
|
||||
"\n" +
|
||||
"Vault initialized successfully.",
|
||||
)
|
||||
}
|
||||
if len(resp.RecoveryKeys) > 0 {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"\n"+
|
||||
"Recovery key initialized with %d keys and a key threshold of %d. Please\n"+
|
||||
"securely distribute the above keys.",
|
||||
shares,
|
||||
threshold,
|
||||
))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@ -109,27 +136,39 @@ General Options:
|
|||
` + meta.GeneralOptionsUsage() + `
|
||||
Init Options:
|
||||
|
||||
-check Don't actually initialize, just check if Vault is
|
||||
already initialized. A return code of 0 means Vault
|
||||
is initialized; a return code of 2 means Vault is not
|
||||
initialized; a return code of 1 means an error was
|
||||
encountered.
|
||||
-check Don't actually initialize, just check if Vault is
|
||||
already initialized. A return code of 0 means Vault
|
||||
is initialized; a return code of 2 means Vault is not
|
||||
initialized; a return code of 1 means an error was
|
||||
encountered.
|
||||
|
||||
-key-shares=5 The number of key shares to split the master key
|
||||
into.
|
||||
-key-shares=5 The number of key shares to split the master key
|
||||
into.
|
||||
|
||||
-key-threshold=3 The number of key shares required to reconstruct
|
||||
the master key.
|
||||
-key-threshold=3 The number of key shares required to reconstruct
|
||||
the master key.
|
||||
|
||||
-pgp-keys If provided, must be a comma-separated list of
|
||||
files on disk containing binary- or base64-format
|
||||
public PGP keys, or Keybase usernames specified as
|
||||
"keybase:<username>". The number of given entries
|
||||
must match 'key-shares'. The output unseal keys will
|
||||
be encrypted and hex-encoded, in order, with the
|
||||
given public keys. If you want to use them with the
|
||||
'vault unseal' command, you will need to hex decode
|
||||
and decrypt; this will be the plaintext unseal key.
|
||||
-stored-shares=0 The number of unseal keys to store. This is not
|
||||
normally available.
|
||||
|
||||
-pgp-keys If provided, must be a comma-separated list of
|
||||
files on disk containing binary- or base64-format
|
||||
public PGP keys, or Keybase usernames specified as
|
||||
"keybase:<username>". The number of given entries
|
||||
must match 'key-shares'. The output unseal keys will
|
||||
be encrypted and hex-encoded, in order, with the
|
||||
given public keys. If you want to use them with the
|
||||
'vault unseal' command, you will need to hex decode
|
||||
and decrypt; this will be the plaintext unseal key.
|
||||
|
||||
-recovery-key-shares=0 The number of key shares to split the recovery key
|
||||
into. This is not normally available.
|
||||
|
||||
-recovery-key-threshold=0 The number of key shares required to reconstruct
|
||||
the recovery key. This is not normally available.
|
||||
|
||||
-recovery-key-pgp-keys If provided, behaves like "pgp-keys" but for the
|
||||
recovery key shares. This is not normally available.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ func TestInit(t *testing.T) {
|
|||
t.Fatal("should be initialized")
|
||||
}
|
||||
|
||||
sealConf, err := core.SealConfig()
|
||||
sealConf, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func TestInit(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
}
|
||||
if !reflect.DeepEqual(expected, sealConf) {
|
||||
t.Fatalf("bad: %#v", sealConf)
|
||||
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, sealConf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ func TestInit_custom(t *testing.T) {
|
|||
t.Fatal("should be initialized")
|
||||
}
|
||||
|
||||
sealConf, err := core.SealConfig()
|
||||
sealConf, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ func TestInit_custom(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
}
|
||||
if !reflect.DeepEqual(expected, sealConf) {
|
||||
t.Fatalf("bad: %#v", sealConf)
|
||||
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, sealConf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ func TestInit_PGP(t *testing.T) {
|
|||
t.Fatal("should be initialized")
|
||||
}
|
||||
|
||||
sealConf, err := core.SealConfig()
|
||||
sealConf, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ func TestInit_PGP(t *testing.T) {
|
|||
PGPKeys: pgpKeys,
|
||||
}
|
||||
if !reflect.DeepEqual(expected, sealConf) {
|
||||
t.Fatalf("bad:\nexpected: %#v\ngot: %#v", expected, sealConf)
|
||||
t.Fatalf("expected:\n%#v\ngot:\n%#v\n", expected, sealConf)
|
||||
}
|
||||
|
||||
re, err := regexp.Compile("\\s+Initial Root Token:\\s+(.*)")
|
||||
|
|
106
command/rekey.go
106
command/rekey.go
|
@ -22,10 +22,13 @@ type RekeyCommand struct {
|
|||
|
||||
// The nonce for the rekey request to send along
|
||||
Nonce string
|
||||
|
||||
// Whether to use the recovery key instead of barrier key, if available
|
||||
RecoveryKey bool
|
||||
}
|
||||
|
||||
func (c *RekeyCommand) Run(args []string) int {
|
||||
var init, cancel, status, delete, retrieve, backup bool
|
||||
var init, cancel, status, delete, retrieve, backup, recoveryKey bool
|
||||
var shares, threshold int
|
||||
var nonce string
|
||||
var pgpKeys pgpkeys.PubKeyFilesFlag
|
||||
|
@ -36,6 +39,7 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
flags.BoolVar(&delete, "delete", false, "")
|
||||
flags.BoolVar(&retrieve, "retrieve", false, "")
|
||||
flags.BoolVar(&backup, "backup", false, "")
|
||||
flags.BoolVar(&recoveryKey, "recovery-key", c.RecoveryKey, "")
|
||||
flags.IntVar(&shares, "key-shares", 5, "")
|
||||
flags.IntVar(&threshold, "key-threshold", 3, "")
|
||||
flags.StringVar(&nonce, "nonce", "", "")
|
||||
|
@ -59,19 +63,24 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
// Check if we are running doing any restricted variants
|
||||
switch {
|
||||
case init:
|
||||
return c.initRekey(client, shares, threshold, pgpKeys, backup)
|
||||
return c.initRekey(client, shares, threshold, pgpKeys, backup, recoveryKey)
|
||||
case cancel:
|
||||
return c.cancelRekey(client)
|
||||
return c.cancelRekey(client, recoveryKey)
|
||||
case status:
|
||||
return c.rekeyStatus(client)
|
||||
return c.rekeyStatus(client, recoveryKey)
|
||||
case retrieve:
|
||||
return c.rekeyRetrieveStored(client)
|
||||
return c.rekeyRetrieveStored(client, recoveryKey)
|
||||
case delete:
|
||||
return c.rekeyDeleteStored(client)
|
||||
return c.rekeyDeleteStored(client, recoveryKey)
|
||||
}
|
||||
|
||||
// Check if the rekey is started
|
||||
rekeyStatus, err := client.Sys().RekeyStatus()
|
||||
var rekeyStatus *api.RekeyStatusResponse
|
||||
if recoveryKey {
|
||||
rekeyStatus, err = client.Sys().RekeyRecoveryKeyStatus()
|
||||
} else {
|
||||
rekeyStatus, err = client.Sys().RekeyStatus()
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error reading rekey status: %s", err))
|
||||
return 1
|
||||
|
@ -79,11 +88,19 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
|
||||
// Start the rekey process if not started
|
||||
if !rekeyStatus.Started {
|
||||
rekeyStatus, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
})
|
||||
if recoveryKey {
|
||||
rekeyStatus, err = client.Sys().RekeyRecoveryKeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
})
|
||||
} else {
|
||||
rekeyStatus, err = client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing rekey: %s", err))
|
||||
return 1
|
||||
|
@ -122,7 +139,12 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Provide the key, this may potentially complete the update
|
||||
result, err := client.Sys().RekeyUpdate(strings.TrimSpace(key), c.Nonce)
|
||||
var result *api.RekeyUpdateResponse
|
||||
if recoveryKey {
|
||||
result, err = client.Sys().RekeyRecoveryKeyUpdate(strings.TrimSpace(key), c.Nonce)
|
||||
} else {
|
||||
result, err = client.Sys().RekeyUpdate(strings.TrimSpace(key), c.Nonce)
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error attempting rekey update: %s", err))
|
||||
return 1
|
||||
|
@ -130,7 +152,7 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
|
||||
// If we are not complete, then dump the status
|
||||
if !result.Complete {
|
||||
return c.rekeyStatus(client)
|
||||
return c.rekeyStatus(client, recoveryKey)
|
||||
}
|
||||
|
||||
// Space between the key prompt, if any, and the output
|
||||
|
@ -176,14 +198,21 @@ func (c *RekeyCommand) Run(args []string) int {
|
|||
func (c *RekeyCommand) initRekey(client *api.Client,
|
||||
shares, threshold int,
|
||||
pgpKeys pgpkeys.PubKeyFilesFlag,
|
||||
backup bool) int {
|
||||
backup, recoveryKey bool) int {
|
||||
// Start the rekey
|
||||
status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{
|
||||
request := &api.RekeyInitRequest{
|
||||
SecretShares: shares,
|
||||
SecretThreshold: threshold,
|
||||
PGPKeys: pgpKeys,
|
||||
Backup: backup,
|
||||
})
|
||||
}
|
||||
var status *api.RekeyStatusResponse
|
||||
var err error
|
||||
if recoveryKey {
|
||||
status, err = client.Sys().RekeyRecoveryKeyInit(request)
|
||||
} else {
|
||||
status, err = client.Sys().RekeyInit(request)
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing rekey: %s", err))
|
||||
return 1
|
||||
|
@ -214,8 +243,13 @@ be deleted at a later time with 'vault rekey -delete'.
|
|||
}
|
||||
|
||||
// cancelRekey is used to abort the rekey process
|
||||
func (c *RekeyCommand) cancelRekey(client *api.Client) int {
|
||||
err := client.Sys().RekeyCancel()
|
||||
func (c *RekeyCommand) cancelRekey(client *api.Client, recovery bool) int {
|
||||
var err error
|
||||
if recovery {
|
||||
err = client.Sys().RekeyRecoveryKeyCancel()
|
||||
} else {
|
||||
err = client.Sys().RekeyCancel()
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to cancel rekey: %s", err))
|
||||
return 1
|
||||
|
@ -225,9 +259,15 @@ func (c *RekeyCommand) cancelRekey(client *api.Client) int {
|
|||
}
|
||||
|
||||
// rekeyStatus is used just to fetch and dump the status
|
||||
func (c *RekeyCommand) rekeyStatus(client *api.Client) int {
|
||||
func (c *RekeyCommand) rekeyStatus(client *api.Client, recovery bool) int {
|
||||
// Check the status
|
||||
status, err := client.Sys().RekeyStatus()
|
||||
var status *api.RekeyStatusResponse
|
||||
var err error
|
||||
if recovery {
|
||||
status, err = client.Sys().RekeyRecoveryKeyStatus()
|
||||
} else {
|
||||
status, err = client.Sys().RekeyStatus()
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error reading rekey status: %s", err))
|
||||
return 1
|
||||
|
@ -240,7 +280,7 @@ func (c *RekeyCommand) dumpRekeyStatus(status *api.RekeyStatusResponse) int {
|
|||
// Dump the status
|
||||
statString := fmt.Sprintf(
|
||||
"Nonce: %s\n"+
|
||||
"Started: %v\n"+
|
||||
"Started: %t\n"+
|
||||
"Key Shares: %d\n"+
|
||||
"Key Threshold: %d\n"+
|
||||
"Rekey Progress: %d\n"+
|
||||
|
@ -260,8 +300,14 @@ func (c *RekeyCommand) dumpRekeyStatus(status *api.RekeyStatusResponse) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (c *RekeyCommand) rekeyRetrieveStored(client *api.Client) int {
|
||||
storedKeys, err := client.Sys().RekeyRetrieveBackup()
|
||||
func (c *RekeyCommand) rekeyRetrieveStored(client *api.Client, recovery bool) int {
|
||||
var storedKeys *api.RekeyRetrieveResponse
|
||||
var err error
|
||||
if recovery {
|
||||
storedKeys, err = client.Sys().RekeyRetrieveRecoveryBackup()
|
||||
} else {
|
||||
storedKeys, err = client.Sys().RekeyRetrieveBackup()
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error retrieving stored keys: %s", err))
|
||||
return 1
|
||||
|
@ -274,8 +320,13 @@ func (c *RekeyCommand) rekeyRetrieveStored(client *api.Client) int {
|
|||
return OutputSecret(c.Ui, "table", secret)
|
||||
}
|
||||
|
||||
func (c *RekeyCommand) rekeyDeleteStored(client *api.Client) int {
|
||||
err := client.Sys().RekeyDeleteBackup()
|
||||
func (c *RekeyCommand) rekeyDeleteStored(client *api.Client, recovery bool) int {
|
||||
var err error
|
||||
if recovery {
|
||||
err = client.Sys().RekeyDeleteRecoveryBackup()
|
||||
} else {
|
||||
err = client.Sys().RekeyDeleteBackup()
|
||||
}
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to delete stored keys: %s", err))
|
||||
return 1
|
||||
|
@ -347,6 +398,9 @@ Rekey Options:
|
|||
stored at "core/unseal-keys-backup" in your physical
|
||||
storage. You can retrieve or delete them via the
|
||||
'sys/rekey/backup' endpoint.
|
||||
|
||||
-recovery-key=false Whether to rekey the recovery key instead of the
|
||||
barrier key. This is not normally available.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ func TestRekey(t *testing.T) {
|
|||
|
||||
ui := new(cli.MockUi)
|
||||
c := &RekeyCommand{
|
||||
Key: hex.EncodeToString(key),
|
||||
Key: hex.EncodeToString(key),
|
||||
RecoveryKey: false,
|
||||
Meta: meta.Meta{
|
||||
Ui: ui,
|
||||
},
|
||||
|
@ -33,7 +34,7 @@ func TestRekey(t *testing.T) {
|
|||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
config, err := core.SealConfig()
|
||||
config, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -49,6 +50,7 @@ func TestRekey_arg(t *testing.T) {
|
|||
|
||||
ui := new(cli.MockUi)
|
||||
c := &RekeyCommand{
|
||||
RecoveryKey: false,
|
||||
Meta: meta.Meta{
|
||||
Ui: ui,
|
||||
},
|
||||
|
@ -59,7 +61,7 @@ func TestRekey_arg(t *testing.T) {
|
|||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
config, err := core.SealConfig()
|
||||
config, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -91,7 +93,7 @@ func TestRekey_init(t *testing.T) {
|
|||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
config, err := core.RekeyConfig()
|
||||
config, err := core.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -126,7 +128,7 @@ func TestRekey_cancel(t *testing.T) {
|
|||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
config, err := core.RekeyConfig()
|
||||
config, err := core.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -204,7 +206,7 @@ func TestRekey_init_pgp(t *testing.T) {
|
|||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||
}
|
||||
|
||||
config, err := core.RekeyConfig()
|
||||
config, err := core.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/logutils"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
|
@ -161,6 +162,7 @@ func (c *ServerCommand) Run(args []string) int {
|
|||
Physical: backend,
|
||||
AdvertiseAddr: config.Backend.AdvertiseAddr,
|
||||
HAPhysical: nil,
|
||||
Seal: &vault.DefaultSeal{},
|
||||
AuditBackends: c.AuditBackends,
|
||||
CredentialBackends: c.CredentialBackends,
|
||||
LogicalBackends: c.LogicalBackends,
|
||||
|
@ -217,10 +219,12 @@ func (c *ServerCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
// Initialize the core
|
||||
core, err := vault.NewCore(coreConfig)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing core: %s", err))
|
||||
return 1
|
||||
core, newCoreError := vault.NewCore(coreConfig)
|
||||
if newCoreError != nil {
|
||||
if !errwrap.ContainsType(newCoreError, new(vault.NonFatalError)) {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing core: %s", newCoreError))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// If we're in dev mode, then initialize the core
|
||||
|
@ -341,6 +345,11 @@ func (c *ServerCommand) Run(args []string) int {
|
|||
go server.Serve(ln)
|
||||
}
|
||||
|
||||
if newCoreError != nil {
|
||||
c.Ui.Output("==> Warning:\n\nNon-fatal error during initialization; check the logs for more information.")
|
||||
c.Ui.Output("")
|
||||
}
|
||||
|
||||
// Output the header that the server has started
|
||||
c.Ui.Output("==> Vault server started! Log data will stream in below:\n")
|
||||
|
||||
|
@ -372,7 +381,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, rootTokenID string) (*vault.
|
|||
init, err := core.Initialize(&vault.SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ func Handler(core *vault.Core) http.Handler {
|
|||
mux.Handle("/v1/sys/health", handleSysHealth(core))
|
||||
mux.Handle("/v1/sys/generate-root/attempt", handleSysGenerateRootAttempt(core))
|
||||
mux.Handle("/v1/sys/generate-root/update", handleSysGenerateRootUpdate(core))
|
||||
mux.Handle("/v1/sys/rekey/init", handleSysRekeyInit(core))
|
||||
mux.Handle("/v1/sys/rekey/update", handleSysRekeyUpdate(core))
|
||||
mux.Handle("/v1/sys/rekey/init", handleSysRekeyInit(core, false))
|
||||
mux.Handle("/v1/sys/rekey/update", handleSysRekeyUpdate(core, false))
|
||||
mux.Handle("/v1/sys/rekey-recovery-key/init", handleSysRekeyInit(core, true))
|
||||
mux.Handle("/v1/sys/rekey-recovery-key/update", handleSysRekeyUpdate(core, true))
|
||||
mux.Handle("/v1/sys/capabilities-self", handleLogical(core, true, sysCapabilitiesSelfCallback))
|
||||
mux.Handle("/v1/sys/", handleLogical(core, true, nil))
|
||||
mux.Handle("/v1/", handleLogical(core, false, nil))
|
||||
|
|
|
@ -26,17 +26,26 @@ func handleSysGenerateRootAttempt(core *vault.Core) http.Handler {
|
|||
|
||||
func handleSysGenerateRootAttemptGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
// Get the current seal configuration
|
||||
sealConfig, err := core.SealConfig()
|
||||
barrierConfig, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if sealConfig == nil {
|
||||
if barrierConfig == nil {
|
||||
respondError(w, http.StatusBadRequest, fmt.Errorf(
|
||||
"server is not yet initialized"))
|
||||
return
|
||||
}
|
||||
|
||||
sealConfig := barrierConfig
|
||||
if core.SealAccess().RecoveryKeySupported() {
|
||||
sealConfig, err = core.SealAccess().RecoveryConfig()
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get the generation configuration
|
||||
generationConfig, err := core.GenerateRootConfiguration()
|
||||
if err != nil {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
|
@ -41,14 +42,28 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
// Initialize
|
||||
result, err := core.Initialize(&vault.SealConfig{
|
||||
barrierConfig := &vault.SealConfig{
|
||||
SecretShares: req.SecretShares,
|
||||
SecretThreshold: req.SecretThreshold,
|
||||
StoredShares: req.StoredShares,
|
||||
PGPKeys: req.PGPKeys,
|
||||
})
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
recoveryConfig := &vault.SealConfig{
|
||||
SecretShares: req.RecoveryShares,
|
||||
SecretThreshold: req.RecoveryThreshold,
|
||||
PGPKeys: req.RecoveryPGPKeys,
|
||||
}
|
||||
|
||||
result, initErr := core.Initialize(barrierConfig, recoveryConfig)
|
||||
if initErr != nil {
|
||||
if !errwrap.ContainsType(initErr, new(vault.NonFatalError)) {
|
||||
respondError(w, http.StatusBadRequest, initErr)
|
||||
return
|
||||
} else {
|
||||
// Add a warnings field? The error will be logged in the vault log
|
||||
// already.
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the keys
|
||||
|
@ -57,21 +72,35 @@ func handleSysInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request)
|
|||
keys = append(keys, hex.EncodeToString(k))
|
||||
}
|
||||
|
||||
respondOk(w, &InitResponse{
|
||||
resp := &InitResponse{
|
||||
Keys: keys,
|
||||
RootToken: result.RootToken,
|
||||
})
|
||||
}
|
||||
|
||||
if len(result.RecoveryShares) > 0 {
|
||||
resp.RecoveryKeys = make([]string, 0, len(result.RecoveryShares))
|
||||
for _, k := range result.RecoveryShares {
|
||||
resp.RecoveryKeys = append(resp.RecoveryKeys, hex.EncodeToString(k))
|
||||
}
|
||||
}
|
||||
|
||||
respondOk(w, resp)
|
||||
}
|
||||
|
||||
type InitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
RecoveryShares int `json:"recovery_shares"`
|
||||
RecoveryThreshold int `json:"recovery_threshold"`
|
||||
RecoveryPGPKeys []string `json:"recovery_pgp_keys"`
|
||||
}
|
||||
|
||||
type InitResponse struct {
|
||||
Keys []string `json:"keys"`
|
||||
RootToken string `json:"root_token"`
|
||||
Keys []string `json:"keys"`
|
||||
RecoveryKeys []string `json:"recovery_keys,omitempty"`
|
||||
RootToken string `json:"root_token"`
|
||||
}
|
||||
|
||||
type InitStatusResponse struct {
|
||||
|
|
|
@ -10,55 +10,61 @@ import (
|
|||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
func handleSysRekeyInit(core *vault.Core) http.Handler {
|
||||
func handleSysRekeyInit(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
handleSysRekeyInitGet(core, w, r)
|
||||
case "POST", "PUT":
|
||||
handleSysRekeyInitPut(core, w, r)
|
||||
case "DELETE":
|
||||
handleSysRekeyInitDelete(core, w, r)
|
||||
switch {
|
||||
case recovery && !core.SealAccess().RecoveryKeySupported():
|
||||
respondError(w, http.StatusBadRequest, fmt.Errorf("recovery rekeying not supported"))
|
||||
case r.Method == "GET":
|
||||
handleSysRekeyInitGet(core, recovery, w, r)
|
||||
case r.Method == "POST" || r.Method == "PUT":
|
||||
handleSysRekeyInitPut(core, recovery, w, r)
|
||||
case r.Method == "DELETE":
|
||||
handleSysRekeyInitDelete(core, recovery, w, r)
|
||||
default:
|
||||
respondError(w, http.StatusMethodNotAllowed, nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func handleSysRekeyInitGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
// Get the current configuration
|
||||
sealConfig, err := core.SealConfig()
|
||||
func handleSysRekeyInitGet(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
barrierConfig, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if sealConfig == nil {
|
||||
if barrierConfig == nil {
|
||||
respondError(w, http.StatusBadRequest, fmt.Errorf(
|
||||
"server is not yet initialized"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the rekey configuration
|
||||
rekeyConf, err := core.RekeyConfig()
|
||||
rekeyConf, err := core.RekeyConfig(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the progress
|
||||
progress, err := core.RekeyProgress()
|
||||
progress, err := core.RekeyProgress(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
sealThreshold, err := core.RekeyThreshold(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
// Format the status
|
||||
status := &RekeyStatusResponse{
|
||||
Started: false,
|
||||
T: 0,
|
||||
N: 0,
|
||||
Progress: progress,
|
||||
Required: sealConfig.SecretThreshold,
|
||||
Required: sealThreshold,
|
||||
}
|
||||
if rekeyConf != nil {
|
||||
status.Nonce = rekeyConf.Nonce
|
||||
|
@ -77,7 +83,7 @@ func handleSysRekeyInitGet(core *vault.Core, w http.ResponseWriter, r *http.Requ
|
|||
respondOk(w, status)
|
||||
}
|
||||
|
||||
func handleSysRekeyInitPut(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
func handleSysRekeyInitPut(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the request
|
||||
var req RekeyRequest
|
||||
if err := parseRequest(r, &req); err != nil {
|
||||
|
@ -93,19 +99,20 @@ func handleSysRekeyInitPut(core *vault.Core, w http.ResponseWriter, r *http.Requ
|
|||
err := core.RekeyInit(&vault.SealConfig{
|
||||
SecretShares: req.SecretShares,
|
||||
SecretThreshold: req.SecretThreshold,
|
||||
StoredShares: req.StoredShares,
|
||||
PGPKeys: req.PGPKeys,
|
||||
Backup: req.Backup,
|
||||
})
|
||||
}, recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
handleSysRekeyInitGet(core, w, r)
|
||||
handleSysRekeyInitGet(core, recovery, w, r)
|
||||
}
|
||||
|
||||
func handleSysRekeyInitDelete(core *vault.Core, w http.ResponseWriter, r *http.Request) {
|
||||
err := core.RekeyCancel()
|
||||
func handleSysRekeyInitDelete(core *vault.Core, recovery bool, w http.ResponseWriter, r *http.Request) {
|
||||
err := core.RekeyCancel(recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
|
@ -113,7 +120,7 @@ func handleSysRekeyInitDelete(core *vault.Core, w http.ResponseWriter, r *http.R
|
|||
respondOk(w, nil)
|
||||
}
|
||||
|
||||
func handleSysRekeyUpdate(core *vault.Core) http.Handler {
|
||||
func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the request
|
||||
var req RekeyUpdateRequest
|
||||
|
@ -138,7 +145,7 @@ func handleSysRekeyUpdate(core *vault.Core) http.Handler {
|
|||
}
|
||||
|
||||
// Use the key to make progress on rekey
|
||||
result, err := core.RekeyUpdate(key, req.Nonce)
|
||||
result, err := core.RekeyUpdate(key, req.Nonce, recovery)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
|
@ -167,6 +174,7 @@ func handleSysRekeyUpdate(core *vault.Core) http.Handler {
|
|||
type RekeyRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
Backup bool `json:"backup"`
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ func handleSysSealStatusRaw(core *vault.Core, w http.ResponseWriter, r *http.Req
|
|||
return
|
||||
}
|
||||
|
||||
sealConfig, err := core.SealConfig()
|
||||
sealConfig, err := core.SealAccess().BarrierConfig()
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
|
|
|
@ -146,7 +146,7 @@ func Test(t TestT, c TestCase) {
|
|||
init, err := core.Initialize(&vault.SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal("error initializing core: ", err)
|
||||
}
|
||||
|
|
311
vault/core.go
311
vault/core.go
|
@ -2,9 +2,6 @@ package vault
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
@ -15,28 +12,18 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
"github.com/hashicorp/vault/helper/mlock"
|
||||
"github.com/hashicorp/vault/helper/pgpkeys"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
"github.com/hashicorp/vault/shamir"
|
||||
)
|
||||
|
||||
const (
|
||||
// coreSealConfigPath is the path used to store our seal configuration.
|
||||
// This value is stored in plaintext, since we must be able to read
|
||||
// it even with the Vault sealed. This is required so that we know
|
||||
// how many secret parts must be used to reconstruct the master key.
|
||||
coreSealConfigPath = "core/seal-config"
|
||||
|
||||
// coreLockPath is the path used to acquire a coordinating lock
|
||||
// for a highly-available deploy.
|
||||
coreLockPath = "core/lock"
|
||||
|
@ -45,11 +32,6 @@ const (
|
|||
// the currently elected leader.
|
||||
coreLeaderPrefix = "core/leader/"
|
||||
|
||||
// coreUnsealKeysBackupPath is the path used to back upencrypted unseal
|
||||
// keys if specified during a rekey operation. This is outside of the
|
||||
// barrier.
|
||||
coreUnsealKeysBackupPath = "core/unseal-keys-backup"
|
||||
|
||||
// lockRetryInterval is the interval we re-attempt to acquire the
|
||||
// HA lock if an error is encountered
|
||||
lockRetryInterval = 10 * time.Second
|
||||
|
@ -97,76 +79,18 @@ var (
|
|||
ErrHANotEnabled = errors.New("Vault is not configured for highly-available mode")
|
||||
)
|
||||
|
||||
// SealConfig is used to describe the seal configuration
|
||||
type SealConfig struct {
|
||||
// SecretShares is the number of shares the secret is
|
||||
// split into. This is the N value of Shamir.
|
||||
SecretShares int `json:"secret_shares"`
|
||||
|
||||
// PGPKeys is the array of public PGP keys used,
|
||||
// if requested, to encrypt the output unseal tokens. If
|
||||
// provided, it sets the value of SecretShares. Ordering
|
||||
// is important.
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
|
||||
// SecretThreshold is the number of parts required
|
||||
// to open the vault. This is the T value of Shamir
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
|
||||
// Nonce is a nonce generated by Vault used to ensure that when unseal keys
|
||||
// are submitted for a rekey operation, the rekey operation itself is the
|
||||
// one intended. This prevents hijacking of the rekey operation, since it
|
||||
// is unauthenticated.
|
||||
Nonce string `json:"nonce"`
|
||||
|
||||
// Backup indicates whether or not a backup of PGP-encrypted unseal keys
|
||||
// should be stored at coreUnsealKeysBackupPath after successful rekeying.
|
||||
Backup bool `json:"backup"`
|
||||
// NonFatalError is an error that can be returned during NewCore that should be
|
||||
// displayed but not cause a program exit
|
||||
type NonFatalError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Validate is used to sanity check the seal configuration
|
||||
func (s *SealConfig) Validate() error {
|
||||
if s.SecretShares < 1 {
|
||||
return fmt.Errorf("secret shares must be at least one")
|
||||
}
|
||||
if s.SecretThreshold < 1 {
|
||||
return fmt.Errorf("secret threshold must be at least one")
|
||||
}
|
||||
if s.SecretShares > 1 && s.SecretThreshold == 1 {
|
||||
return fmt.Errorf("secret threshold must be greater than one for multiple shares")
|
||||
}
|
||||
if s.SecretShares > 255 {
|
||||
return fmt.Errorf("secret shares must be less than 256")
|
||||
}
|
||||
if s.SecretThreshold > 255 {
|
||||
return fmt.Errorf("secret threshold must be less than 256")
|
||||
}
|
||||
if s.SecretThreshold > s.SecretShares {
|
||||
return fmt.Errorf("secret threshold cannot be larger than secret shares")
|
||||
}
|
||||
if len(s.PGPKeys) > 0 && len(s.PGPKeys) != s.SecretShares {
|
||||
return fmt.Errorf("count mismatch between number of provided PGP keys and number of shares")
|
||||
}
|
||||
if len(s.PGPKeys) > 0 {
|
||||
for _, keystring := range s.PGPKeys {
|
||||
data, err := base64.StdEncoding.DecodeString(keystring)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error decoding given PGP key: %s", err)
|
||||
}
|
||||
_, err = openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing given PGP key: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
func (e *NonFatalError) WrappedErrors() []error {
|
||||
return []error{e.Err}
|
||||
}
|
||||
|
||||
// InitResult is used to provide the key parts back after
|
||||
// they are generated as part of the initialization.
|
||||
type InitResult struct {
|
||||
SecretShares [][]byte
|
||||
RootToken string
|
||||
func (e *NonFatalError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// ErrInvalidKey is returned if there is an error with a
|
||||
|
@ -192,6 +116,9 @@ type Core struct {
|
|||
// physical backend is the un-trusted backend with durable data
|
||||
physical physical.Backend
|
||||
|
||||
// Our Seal, for seal configuration information
|
||||
seal Seal
|
||||
|
||||
// barrier is the security barrier wrapping the physical backend
|
||||
barrier SecurityBarrier
|
||||
|
||||
|
@ -226,11 +153,14 @@ type Core struct {
|
|||
generateRootProgress [][]byte
|
||||
generateRootLock sync.Mutex
|
||||
|
||||
// rekeyProgress holds the shares we have until we reach enough
|
||||
// to verify the master key.
|
||||
rekeyConfig *SealConfig
|
||||
rekeyProgress [][]byte
|
||||
rekeyLock sync.Mutex
|
||||
// These variables holds the config and shares we have until we reach
|
||||
// enough to verify the appropriate master key. Note that the same lock is
|
||||
// used; this isn't time-critical so this shouldn't be a problem.
|
||||
barrierRekeyConfig *SealConfig
|
||||
barrierRekeyProgress [][]byte
|
||||
recoveryRekeyConfig *SealConfig
|
||||
recoveryRekeyProgress [][]byte
|
||||
rekeyLock sync.RWMutex
|
||||
|
||||
// mounts is loaded after unseal since it is a protected
|
||||
// configuration
|
||||
|
@ -296,6 +226,7 @@ type CoreConfig struct {
|
|||
AuditBackends map[string]audit.Factory
|
||||
Physical physical.Backend
|
||||
HAPhysical physical.HABackend // May be nil, which disables HA operations
|
||||
Seal Seal
|
||||
Logger *log.Logger
|
||||
DisableCache bool // Disables the LRU cache on the physical backend
|
||||
DisableMlock bool // Disables mlock syscall
|
||||
|
@ -375,6 +306,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||
ha: conf.HAPhysical,
|
||||
advertiseAddr: conf.AdvertiseAddr,
|
||||
physical: conf.Physical,
|
||||
seal: conf.Seal,
|
||||
barrier: barrier,
|
||||
router: NewRouter(),
|
||||
sealed: true,
|
||||
|
@ -413,7 +345,17 @@ func NewCore(conf *CoreConfig) (*Core, error) {
|
|||
auditBackends[k] = f
|
||||
}
|
||||
c.auditBackends = auditBackends
|
||||
return c, nil
|
||||
|
||||
if c.seal == nil {
|
||||
c.seal = &DefaultSeal{}
|
||||
}
|
||||
c.seal.SetCore(c)
|
||||
|
||||
// Attempt unsealing with stored keys; if there are no stored keys this
|
||||
// returns nil, otherwise returns nil or an error
|
||||
storedKeyErr := c.unsealWithStoredKeys()
|
||||
|
||||
return c, storedKeyErr
|
||||
}
|
||||
|
||||
// Shutdown is invoked when the Vault instance is about to be terminated. It
|
||||
|
@ -819,148 +761,6 @@ func (c *Core) checkToken(req *logical.Request) (*logical.Auth, *TokenEntry, err
|
|||
return auth, te, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return false, nil
|
||||
}
|
||||
if !init {
|
||||
c.logger.Printf("[INFO] core: security barrier not initialized")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Verify the seal configuration
|
||||
sealConf, err := c.SealConfig()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if sealConf == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Initialize is used to initialize the Vault with the given
|
||||
// configurations.
|
||||
func (c *Core) Initialize(config *SealConfig) (*InitResult, error) {
|
||||
// Check if the seal configuraiton is valid
|
||||
if err := config.Validate(); err != nil {
|
||||
c.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("invalid seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Encode the seal configuration
|
||||
buf, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Store the seal configuration
|
||||
pe := &physical.Entry{
|
||||
Key: coreSealConfigPath,
|
||||
Value: buf,
|
||||
}
|
||||
if err := c.physical.Put(pe); err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to write seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to write seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Generate a master key
|
||||
masterKey, err := c.barrier.GenerateKey()
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to generate master key: %v", err)
|
||||
return nil, fmt.Errorf("master key generation failed: %v", err)
|
||||
}
|
||||
|
||||
// Return the master key if only a single key part is used
|
||||
results := new(InitResult)
|
||||
if config.SecretShares == 1 {
|
||||
results.SecretShares = append(results.SecretShares, masterKey)
|
||||
} else {
|
||||
// Split the master key using the Shamir algorithm
|
||||
shares, err := shamir.Split(masterKey, config.SecretShares, config.SecretThreshold)
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to generate shares: %v", err)
|
||||
return nil, fmt.Errorf("failed to generate shares: %v", err)
|
||||
}
|
||||
results.SecretShares = shares
|
||||
}
|
||||
|
||||
if len(config.PGPKeys) > 0 {
|
||||
hexEncodedShares := make([][]byte, len(results.SecretShares))
|
||||
for i, _ := range results.SecretShares {
|
||||
hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i]))
|
||||
}
|
||||
_, encryptedShares, err := pgpkeys.EncryptShares(hexEncodedShares, config.PGPKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.SecretShares = encryptedShares
|
||||
}
|
||||
|
||||
// Initialize the barrier
|
||||
if err := c.barrier.Initialize(masterKey); 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)",
|
||||
config.SecretShares, config.SecretThreshold)
|
||||
|
||||
// Unseal the barrier
|
||||
if err := c.barrier.Unseal(masterKey); 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
|
||||
}
|
||||
|
||||
// Sealed checks if the Vault is current sealed
|
||||
func (c *Core) Sealed() (bool, error) {
|
||||
c.stateLock.RLock()
|
||||
|
@ -1023,39 +823,6 @@ func (c *Core) Leader() (bool, string, error) {
|
|||
return false, string(entry.Value), nil
|
||||
}
|
||||
|
||||
// SealConfiguration is used to return information
|
||||
// about the configuration of the Vault and it's current
|
||||
// status.
|
||||
func (c *Core) SealConfig() (*SealConfig, error) {
|
||||
// Fetch the core configuration
|
||||
pe, err := c.physical.Get(coreSealConfigPath)
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to read seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to check seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// If the seal configuration is missing, we are not initialized
|
||||
if pe == nil {
|
||||
c.logger.Printf("[INFO] core: seal configuration missing, not initialized")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Decode the barrier entry
|
||||
var conf SealConfig
|
||||
if err := json.Unmarshal(pe.Value, &conf); err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to decode seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to decode seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Check for a valid seal configuration
|
||||
if err := conf.Validate(); err != nil {
|
||||
c.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("seal validation failed: %v", err)
|
||||
}
|
||||
|
||||
return &conf, nil
|
||||
}
|
||||
|
||||
// SecretProgress returns the number of keys provided so far
|
||||
func (c *Core) SecretProgress() int {
|
||||
c.stateLock.RLock()
|
||||
|
@ -1093,7 +860,7 @@ func (c *Core) Unseal(key []byte) (bool, error) {
|
|||
}
|
||||
|
||||
// Get the seal configuration
|
||||
config, err := c.SealConfig()
|
||||
config, err := c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -1385,8 +1152,10 @@ func (c *Core) preSeal() error {
|
|||
c.logger.Printf("[INFO] core: pre-seal teardown starting")
|
||||
|
||||
// Clear any rekey progress
|
||||
c.rekeyConfig = nil
|
||||
c.rekeyProgress = nil
|
||||
c.barrierRekeyConfig = nil
|
||||
c.barrierRekeyProgress = nil
|
||||
c.recoveryRekeyConfig = nil
|
||||
c.recoveryRekeyProgress = nil
|
||||
|
||||
if c.metricsCh != nil {
|
||||
close(c.metricsCh)
|
||||
|
@ -1662,3 +1431,9 @@ func (c *Core) emitMetrics(stopCh chan struct{}) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Core) SealAccess() *SealAccess {
|
||||
sa := &SealAccess{}
|
||||
sa.SetSeal(c.seal)
|
||||
return sa
|
||||
}
|
||||
|
|
|
@ -39,135 +39,6 @@ func TestSealConfig_Invalid(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCore_Init(t *testing.T) {
|
||||
inm := physical.NewInmem()
|
||||
conf := &CoreConfig{
|
||||
Physical: inm,
|
||||
DisableMlock: true,
|
||||
LogicalBackends: map[string]logical.Factory{
|
||||
"generic": LeasedPassthroughBackendFactory,
|
||||
},
|
||||
}
|
||||
c, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err := c.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if init {
|
||||
t.Fatalf("should not be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err := c.SealConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if outConf != nil {
|
||||
t.Fatalf("bad: %v", outConf)
|
||||
}
|
||||
|
||||
sealConf := &SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
}
|
||||
res, err := c.Initialize(sealConf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(res.SecretShares) != 1 {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
if res.RootToken == "" {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
|
||||
_, err = c.Initialize(sealConf)
|
||||
if err != ErrAlreadyInit {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err = c.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !init {
|
||||
t.Fatalf("should be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err = c.SealConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outConf, sealConf) {
|
||||
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
||||
}
|
||||
|
||||
// New Core, same backend
|
||||
c2, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
_, err = c2.Initialize(sealConf)
|
||||
if err != ErrAlreadyInit {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err = c2.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !init {
|
||||
t.Fatalf("should be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err = c2.SealConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outConf, sealConf) {
|
||||
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_Init_MultiShare(t *testing.T) {
|
||||
c := TestCore(t)
|
||||
sealConf := &SealConfig{
|
||||
SecretShares: 5,
|
||||
SecretThreshold: 3,
|
||||
}
|
||||
res, err := c.Initialize(sealConf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(res.SecretShares) != 5 {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
if res.RootToken == "" {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err := c.SealConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outConf, sealConf) {
|
||||
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_Unseal_MultiShare(t *testing.T) {
|
||||
c := TestCore(t)
|
||||
|
||||
|
@ -180,7 +51,7 @@ func TestCore_Unseal_MultiShare(t *testing.T) {
|
|||
SecretShares: 5,
|
||||
SecretThreshold: 3,
|
||||
}
|
||||
res, err := c.Initialize(sealConf)
|
||||
res, err := c.Initialize(sealConf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -265,7 +136,7 @@ func TestCore_Unseal_Single(t *testing.T) {
|
|||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
}
|
||||
res, err := c.Initialize(sealConf)
|
||||
res, err := c.Initialize(sealConf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -320,7 +191,7 @@ func TestCore_Route_Sealed(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
res, err := c.Initialize(sealConf)
|
||||
res, err := c.Initialize(sealConf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2125,96 +1996,3 @@ func TestCore_Standby_Rotate(t *testing.T) {
|
|||
t.Fatalf("bad: %#v", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_Standby_Rekey(t *testing.T) {
|
||||
// Create the first core and initialize it
|
||||
inm := physical.NewInmem()
|
||||
inmha := physical.NewInmemHA()
|
||||
advertiseOriginal := "http://127.0.0.1:8200"
|
||||
core, err := NewCore(&CoreConfig{
|
||||
Physical: inm,
|
||||
HAPhysical: inmha,
|
||||
AdvertiseAddr: advertiseOriginal,
|
||||
DisableMlock: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
key, root := TestCoreInit(t, core)
|
||||
if _, err := core.Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
}
|
||||
|
||||
// Wait for core to become active
|
||||
testWaitActive(t, core)
|
||||
|
||||
// Create a second core, attached to same in-memory store
|
||||
advertiseOriginal2 := "http://127.0.0.1:8500"
|
||||
core2, err := NewCore(&CoreConfig{
|
||||
Physical: inm,
|
||||
HAPhysical: inmha,
|
||||
AdvertiseAddr: advertiseOriginal2,
|
||||
DisableMlock: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if _, err := core2.Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
}
|
||||
|
||||
// Rekey the master key
|
||||
newConf := &SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
}
|
||||
err = core.RekeyInit(newConf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err := core.RekeyConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if rkconf == nil {
|
||||
t.Fatalf("bad: no rekey config received")
|
||||
}
|
||||
result, err := core.RekeyUpdate(key, rkconf.Nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("rekey failed")
|
||||
}
|
||||
|
||||
// Seal the first core, should step down
|
||||
err = core.Seal(root)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Wait for core2 to become active
|
||||
testWaitActive(t, core2)
|
||||
|
||||
// Rekey the master key again
|
||||
err = core2.RekeyInit(newConf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err = core2.RekeyConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if rkconf == nil {
|
||||
t.Fatalf("bad: no rekey config received")
|
||||
}
|
||||
result, err = core2.RekeyUpdate(result.SecretShares[0], rkconf.Nonce)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("rekey failed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,9 +146,18 @@ func (c *Core) GenerateRootUpdate(key []byte, nonce string) (*GenerateRootResult
|
|||
}
|
||||
|
||||
// Get the seal configuration
|
||||
config, err := c.SealConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var config *SealConfig
|
||||
var err error
|
||||
if c.seal.RecoveryKeySupported() {
|
||||
config, err = c.seal.RecoveryConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
config, err = c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the barrier is initialized
|
||||
|
@ -214,9 +223,16 @@ func (c *Core) GenerateRootUpdate(key []byte, nonce string) (*GenerateRootResult
|
|||
}
|
||||
|
||||
// Verify the master key
|
||||
if err := c.barrier.VerifyMaster(masterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: root generation aborted, master key verification failed: %v", err)
|
||||
return nil, err
|
||||
if c.seal.RecoveryKeySupported() {
|
||||
if err := c.seal.VerifyRecoveryKey(masterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: root generation aborted, recovery key verification failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := c.barrier.VerifyMaster(masterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: root generation aborted, master key verification failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
te, err := c.tokenStore.rootToken()
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
||||
func TestCore_Init(t *testing.T) {
|
||||
inm := physical.NewInmem()
|
||||
conf := &CoreConfig{
|
||||
Physical: inm,
|
||||
DisableMlock: true,
|
||||
LogicalBackends: map[string]logical.Factory{
|
||||
"generic": LeasedPassthroughBackendFactory,
|
||||
},
|
||||
}
|
||||
c, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err := c.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if init {
|
||||
t.Fatalf("should not be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err := c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if outConf != nil {
|
||||
t.Fatalf("bad: %v", outConf)
|
||||
}
|
||||
|
||||
sealConf := &SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
}
|
||||
res, err := c.Initialize(sealConf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(res.SecretShares) != 1 {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
if res.RootToken == "" {
|
||||
t.Fatalf("Bad: %v", res)
|
||||
}
|
||||
|
||||
_, err = c.Initialize(sealConf, nil)
|
||||
if err != ErrAlreadyInit {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err = c.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !init {
|
||||
t.Fatalf("should be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err = c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outConf, sealConf) {
|
||||
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
||||
}
|
||||
|
||||
// New Core, same backend
|
||||
c2, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
_, err = c2.Initialize(sealConf, nil)
|
||||
if err != ErrAlreadyInit {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
init, err = c2.Initialized()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !init {
|
||||
t.Fatalf("should be init")
|
||||
}
|
||||
|
||||
// Check the seal configuration
|
||||
outConf, err = c2.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(outConf, sealConf) {
|
||||
t.Fatalf("bad: %v expect: %v", outConf, sealConf)
|
||||
}
|
||||
}
|
|
@ -111,8 +111,22 @@ func NewSystemBackend(core *Core, config *logical.BackendConfig) logical.Backend
|
|||
Fields: map[string]*framework.FieldSchema{},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.handleRekeyRetrieve,
|
||||
logical.DeleteOperation: b.handleRekeyDelete,
|
||||
logical.ReadOperation: b.handleRekeyRetrieveBarrier,
|
||||
logical.DeleteOperation: b.handleRekeyDeleteBarrier,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]),
|
||||
HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]),
|
||||
},
|
||||
|
||||
&framework.Path{
|
||||
Pattern: "rekey/recovery-key-backup$",
|
||||
|
||||
Fields: map[string]*framework.FieldSchema{},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.handleRekeyRetrieveRecovery,
|
||||
logical.DeleteOperation: b.handleRekeyDeleteRecovery,
|
||||
},
|
||||
|
||||
HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]),
|
||||
|
@ -522,8 +536,10 @@ func (b *SystemBackend) handleCapabilitiesAccessor(req *logical.Request, d *fram
|
|||
// handleRekeyRetrieve returns backed-up, PGP-encrypted unseal keys from a
|
||||
// rekey operation
|
||||
func (b *SystemBackend) handleRekeyRetrieve(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
backup, err := b.Core.RekeyRetrieveBackup()
|
||||
req *logical.Request,
|
||||
data *framework.FieldData,
|
||||
recovery bool) (*logical.Response, error) {
|
||||
backup, err := b.Core.RekeyRetrieveBackup(recovery)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to look up backed-up keys: %v", err)
|
||||
}
|
||||
|
@ -542,17 +558,38 @@ func (b *SystemBackend) handleRekeyRetrieve(
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleRekeyRetrieveBarrier(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
return b.handleRekeyRetrieve(req, data, false)
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleRekeyRetrieveRecovery(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
return b.handleRekeyRetrieve(req, data, true)
|
||||
}
|
||||
|
||||
// handleRekeyDelete deletes backed-up, PGP-encrypted unseal keys from a rekey
|
||||
// operation
|
||||
func (b *SystemBackend) handleRekeyDelete(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
err := b.Core.RekeyDeleteBackup()
|
||||
req *logical.Request,
|
||||
data *framework.FieldData,
|
||||
recovery bool) (*logical.Response, error) {
|
||||
err := b.Core.RekeyDeleteBackup(recovery)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error during deletion of backed-up keys: %v", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
func (b *SystemBackend) handleRekeyDeleteBarrier(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
return b.handleRekeyDelete(req, data, false)
|
||||
}
|
||||
|
||||
func (b *SystemBackend) handleRekeyDeleteRecovery(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
return b.handleRekeyDelete(req, data, true)
|
||||
}
|
||||
|
||||
// handleMountTable handles the "mounts" endpoint to provide the mount table
|
||||
func (b *SystemBackend) handleMountTable(
|
||||
|
|
513
vault/rekey.go
513
vault/rekey.go
|
@ -12,12 +12,25 @@ import (
|
|||
"github.com/hashicorp/vault/shamir"
|
||||
)
|
||||
|
||||
const (
|
||||
// coreUnsealKeysBackupPath is the path used to back upencrypted unseal
|
||||
// keys if specified during a rekey operation. This is outside of the
|
||||
// barrier.
|
||||
coreBarrierUnsealKeysBackupPath = "core/unseal-keys-backup"
|
||||
|
||||
// coreRecoveryUnsealKeysBackupPath is the path used to back upencrypted
|
||||
// recovery keys if specified during a rekey operation. This is outside of
|
||||
// the barrier.
|
||||
coreRecoveryUnsealKeysBackupPath = "core/recovery-keys-backup"
|
||||
)
|
||||
|
||||
// RekeyResult is used to provide the key parts back after
|
||||
// they are generated as part of the rekey.
|
||||
type RekeyResult struct {
|
||||
SecretShares [][]byte
|
||||
PGPFingerprints []string
|
||||
Backup bool
|
||||
RecoveryKey bool
|
||||
}
|
||||
|
||||
// RekeyBackup stores the backup copy of PGP-encrypted keys
|
||||
|
@ -26,8 +39,7 @@ type RekeyBackup struct {
|
|||
Keys map[string][]string
|
||||
}
|
||||
|
||||
// RekeyProgress is used to return the rekey progress (num shares)
|
||||
func (c *Core) RekeyProgress() (int, error) {
|
||||
func (c *Core) RekeyThreshold(recovery bool) (int, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
@ -37,13 +49,45 @@ func (c *Core) RekeyProgress() (int, error) {
|
|||
return 0, ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
return len(c.rekeyProgress), nil
|
||||
c.rekeyLock.RLock()
|
||||
defer c.rekeyLock.RUnlock()
|
||||
|
||||
var config *SealConfig
|
||||
var err error
|
||||
if recovery {
|
||||
config, err = c.seal.RecoveryConfig()
|
||||
} else {
|
||||
config, err = c.seal.BarrierConfig()
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return config.SecretThreshold, nil
|
||||
}
|
||||
|
||||
// RekeyProgress is used to return the rekey progress (num shares)
|
||||
func (c *Core) RekeyProgress(recovery bool) (int, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return 0, ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return 0, ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.RLock()
|
||||
defer c.rekeyLock.RUnlock()
|
||||
|
||||
if recovery {
|
||||
return len(c.recoveryRekeyProgress), nil
|
||||
}
|
||||
return len(c.barrierRekeyProgress), nil
|
||||
}
|
||||
|
||||
// RekeyConfig is used to read the rekey configuration
|
||||
func (c *Core) RekeyConfig() (*SealConfig, error) {
|
||||
func (c *Core) RekeyConfig(recovery bool) (*SealConfig, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
@ -58,15 +102,47 @@ func (c *Core) RekeyConfig() (*SealConfig, error) {
|
|||
|
||||
// Copy the seal config if any
|
||||
var conf *SealConfig
|
||||
if c.rekeyConfig != nil {
|
||||
conf = new(SealConfig)
|
||||
*conf = *c.rekeyConfig
|
||||
if recovery {
|
||||
if c.recoveryRekeyConfig != nil {
|
||||
conf = c.recoveryRekeyConfig.Clone()
|
||||
}
|
||||
} else {
|
||||
if c.barrierRekeyConfig != nil {
|
||||
conf = c.barrierRekeyConfig.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// RekeyInit is used to initialize the rekey settings
|
||||
func (c *Core) RekeyInit(config *SealConfig) error {
|
||||
func (c *Core) RekeyInit(config *SealConfig, recovery bool) error {
|
||||
if recovery {
|
||||
return c.RecoveryRekeyInit(config)
|
||||
}
|
||||
return c.BarrierRekeyInit(config)
|
||||
}
|
||||
|
||||
// BarrierRekeyInit is used to initialize the rekey settings for the barrier key
|
||||
func (c *Core) BarrierRekeyInit(config *SealConfig) error {
|
||||
// Right now we don't support this, but the rest of the code is ready for
|
||||
// when we do, hence the check below for this to be false if
|
||||
// config.StoredShares is greater than zero
|
||||
if c.seal.StoredKeysSupported() {
|
||||
return fmt.Errorf("rekeying of barrier not supported when stored key support is available")
|
||||
}
|
||||
|
||||
if config.StoredShares > 0 {
|
||||
if !c.seal.StoredKeysSupported() {
|
||||
return fmt.Errorf("storing keys not supported by barrier seal")
|
||||
}
|
||||
if len(config.PGPKeys) > 0 {
|
||||
return fmt.Errorf("PGP key encryption not supported when using stored keys")
|
||||
}
|
||||
if config.Backup {
|
||||
return fmt.Errorf("key backup not supported when using stored keys")
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the seal configuraiton is valid
|
||||
if err := config.Validate(); err != nil {
|
||||
c.logger.Printf("[ERR] core: invalid rekey seal configuration: %v", err)
|
||||
|
@ -82,51 +158,88 @@ func (c *Core) RekeyInit(config *SealConfig) error {
|
|||
return ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Prevent multiple concurrent re-keys
|
||||
if c.rekeyConfig != nil {
|
||||
if c.barrierRekeyConfig != nil {
|
||||
return fmt.Errorf("rekey already in progress")
|
||||
}
|
||||
|
||||
// Copy the configuration
|
||||
c.rekeyConfig = new(SealConfig)
|
||||
*c.rekeyConfig = *config
|
||||
c.barrierRekeyConfig = config.Clone()
|
||||
|
||||
// Initialize the nonce
|
||||
nonce, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
c.rekeyConfig = nil
|
||||
c.barrierRekeyConfig = nil
|
||||
return err
|
||||
}
|
||||
c.rekeyConfig.Nonce = nonce
|
||||
c.barrierRekeyConfig.Nonce = nonce
|
||||
|
||||
c.logger.Printf("[INFO] core: rekey initialized (nonce: %s, shares: %d, threshold: %d)",
|
||||
c.rekeyConfig.Nonce, c.rekeyConfig.SecretShares, c.rekeyConfig.SecretThreshold)
|
||||
c.barrierRekeyConfig.Nonce, c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RekeyUpdate is used to provide a new key part
|
||||
func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
||||
// Verify the key length
|
||||
min, max := c.barrier.KeyLength()
|
||||
max += shamir.ShareOverhead
|
||||
if len(key) < min {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
|
||||
}
|
||||
if len(key) > max {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
|
||||
// RecoveryRekeyInit is used to initialize the rekey settings for the recovery key
|
||||
func (c *Core) RecoveryRekeyInit(config *SealConfig) error {
|
||||
if config.StoredShares > 0 {
|
||||
return fmt.Errorf("stored shares not supported by recovery key")
|
||||
}
|
||||
|
||||
// Get the seal configuration
|
||||
config, err := c.SealConfig()
|
||||
// Check if the seal configuraiton is valid
|
||||
if err := config.Validate(); err != nil {
|
||||
c.logger.Printf("[ERR] core: invalid recovery configuration: %v", err)
|
||||
return fmt.Errorf("invalid recovery configuration: %v", err)
|
||||
}
|
||||
|
||||
if !c.seal.RecoveryKeySupported() {
|
||||
return fmt.Errorf("recovery keys not supported")
|
||||
}
|
||||
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Prevent multiple concurrent re-keys
|
||||
if c.recoveryRekeyConfig != nil {
|
||||
return fmt.Errorf("rekey already in progress")
|
||||
}
|
||||
|
||||
// Copy the configuration
|
||||
c.recoveryRekeyConfig = config.Clone()
|
||||
|
||||
// Initialize the nonce
|
||||
nonce, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
c.recoveryRekeyConfig = nil
|
||||
return err
|
||||
}
|
||||
c.recoveryRekeyConfig.Nonce = nonce
|
||||
|
||||
// Ensure the barrier is initialized
|
||||
if config == nil {
|
||||
return nil, ErrNotInit
|
||||
c.logger.Printf("[INFO] core: rekey initialized (nonce: %s, shares: %d, threshold: %d)",
|
||||
c.recoveryRekeyConfig.Nonce, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Core) RekeyUpdate(key []byte, nonce string, recovery bool) (*RekeyResult, error) {
|
||||
if recovery {
|
||||
return c.RecoveryRekeyUpdate(key, nonce)
|
||||
}
|
||||
return c.BarrierRekeyUpdate(key, nonce)
|
||||
}
|
||||
|
||||
// BarrierRekeyUpdate is used to provide a new key part
|
||||
func (c *Core) BarrierRekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
||||
// Ensure we are already unsealed
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
|
@ -137,49 +250,69 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
return nil, ErrStandby
|
||||
}
|
||||
|
||||
// Verify the key length
|
||||
min, max := c.barrier.KeyLength()
|
||||
max += shamir.ShareOverhead
|
||||
if len(key) < min {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
|
||||
}
|
||||
if len(key) > max {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Get the seal configuration
|
||||
existingConfig, err := c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the barrier is initialized
|
||||
if existingConfig == nil {
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
|
||||
// Ensure a rekey is in progress
|
||||
if c.rekeyConfig == nil {
|
||||
if c.barrierRekeyConfig == nil {
|
||||
return nil, fmt.Errorf("no rekey in progress")
|
||||
}
|
||||
|
||||
if nonce != c.rekeyConfig.Nonce {
|
||||
return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %s", c.rekeyConfig.Nonce)
|
||||
if nonce != c.barrierRekeyConfig.Nonce {
|
||||
return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %s", c.barrierRekeyConfig.Nonce)
|
||||
}
|
||||
|
||||
// Check if we already have this piece
|
||||
for _, existing := range c.rekeyProgress {
|
||||
for _, existing := range c.barrierRekeyProgress {
|
||||
if bytes.Equal(existing, key) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Store this key
|
||||
c.rekeyProgress = append(c.rekeyProgress, key)
|
||||
c.barrierRekeyProgress = append(c.barrierRekeyProgress, key)
|
||||
|
||||
// Check if we don't have enough keys to unlock
|
||||
if len(c.rekeyProgress) < config.SecretThreshold {
|
||||
if len(c.barrierRekeyProgress) < existingConfig.SecretThreshold {
|
||||
c.logger.Printf("[DEBUG] core: cannot rekey, have %d of %d keys",
|
||||
len(c.rekeyProgress), config.SecretThreshold)
|
||||
len(c.barrierRekeyProgress), existingConfig.SecretThreshold)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Recover the master key
|
||||
var masterKey []byte
|
||||
if config.SecretThreshold == 1 {
|
||||
masterKey = c.rekeyProgress[0]
|
||||
c.rekeyProgress = nil
|
||||
if existingConfig.SecretThreshold == 1 {
|
||||
masterKey = c.barrierRekeyProgress[0]
|
||||
c.barrierRekeyProgress = nil
|
||||
} else {
|
||||
masterKey, err = shamir.Combine(c.rekeyProgress)
|
||||
c.rekeyProgress = nil
|
||||
masterKey, err = shamir.Combine(c.barrierRekeyProgress)
|
||||
c.barrierRekeyProgress = nil
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compute master key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the master key
|
||||
if err := c.barrier.VerifyMaster(masterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: rekey aborted, master key verification failed: %v", err)
|
||||
return nil, err
|
||||
|
@ -194,14 +327,14 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
|
||||
// Return the master key if only a single key part is used
|
||||
results := &RekeyResult{
|
||||
Backup: c.rekeyConfig.Backup,
|
||||
Backup: c.barrierRekeyConfig.Backup,
|
||||
}
|
||||
|
||||
if c.rekeyConfig.SecretShares == 1 {
|
||||
if c.barrierRekeyConfig.SecretShares == 1 {
|
||||
results.SecretShares = append(results.SecretShares, newMasterKey)
|
||||
} else {
|
||||
// Split the master key using the Shamir algorithm
|
||||
shares, err := shamir.Split(newMasterKey, c.rekeyConfig.SecretShares, c.rekeyConfig.SecretThreshold)
|
||||
shares, err := shamir.Split(newMasterKey, c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold)
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to generate shares: %v", err)
|
||||
return nil, fmt.Errorf("failed to generate shares: %v", err)
|
||||
|
@ -209,17 +342,27 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
results.SecretShares = shares
|
||||
}
|
||||
|
||||
if len(c.rekeyConfig.PGPKeys) > 0 {
|
||||
// If we are storing any shares, add them to the shares to store and remove
|
||||
// from the returned keys
|
||||
var keysToStore [][]byte
|
||||
if c.barrierRekeyConfig.StoredShares > 0 {
|
||||
for i := 0; i < c.barrierRekeyConfig.StoredShares; i++ {
|
||||
keysToStore = append(keysToStore, results.SecretShares[0])
|
||||
results.SecretShares = results.SecretShares[1:]
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.barrierRekeyConfig.PGPKeys) > 0 {
|
||||
hexEncodedShares := make([][]byte, len(results.SecretShares))
|
||||
for i, _ := range results.SecretShares {
|
||||
hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i]))
|
||||
}
|
||||
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.rekeyConfig.PGPKeys)
|
||||
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.barrierRekeyConfig.PGPKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.rekeyConfig.Backup {
|
||||
if c.barrierRekeyConfig.Backup {
|
||||
backupInfo := map[string][]string{}
|
||||
for i := 0; i < len(results.PGPFingerprints); i++ {
|
||||
encShare := bytes.NewBuffer(results.SecretShares[i])
|
||||
|
@ -231,7 +374,7 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
}
|
||||
|
||||
backupVals := &RekeyBackup{
|
||||
Nonce: c.rekeyConfig.Nonce,
|
||||
Nonce: c.barrierRekeyConfig.Nonce,
|
||||
Keys: backupInfo,
|
||||
}
|
||||
buf, err := json.Marshal(backupVals)
|
||||
|
@ -240,7 +383,7 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
return nil, fmt.Errorf("failed to marshal unseal key backup: %v", err)
|
||||
}
|
||||
pe := &physical.Entry{
|
||||
Key: coreUnsealKeysBackupPath,
|
||||
Key: coreBarrierUnsealKeysBackupPath,
|
||||
Value: buf,
|
||||
}
|
||||
if err = c.physical.Put(pe); err != nil {
|
||||
|
@ -250,10 +393,11 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Encode the seal configuration
|
||||
buf, err := json.Marshal(c.rekeyConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode seal configuration: %v", err)
|
||||
if keysToStore != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Rekey the barrier
|
||||
|
@ -262,44 +406,22 @@ func (c *Core) RekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
|||
return nil, fmt.Errorf("failed to rekey barrier: %v", err)
|
||||
}
|
||||
c.logger.Printf("[INFO] core: security barrier rekeyed (shares: %d, threshold: %d)",
|
||||
c.rekeyConfig.SecretShares, c.rekeyConfig.SecretThreshold)
|
||||
c.barrierRekeyConfig.SecretShares, c.barrierRekeyConfig.SecretThreshold)
|
||||
|
||||
// Store the seal configuration
|
||||
pe := &physical.Entry{
|
||||
Key: coreSealConfigPath,
|
||||
Value: buf,
|
||||
}
|
||||
if err := c.physical.Put(pe); err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to update seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to update seal configuration: %v", err)
|
||||
if err := c.seal.SetBarrierConfig(c.barrierRekeyConfig); err != nil {
|
||||
c.logger.Printf("[ERR] core: error saving rekey seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to save rekey seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Done!
|
||||
c.rekeyProgress = nil
|
||||
c.rekeyConfig = nil
|
||||
c.barrierRekeyProgress = nil
|
||||
c.barrierRekeyConfig = nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// RekeyCancel is used to cancel an inprogress rekey
|
||||
func (c *Core) RekeyCancel() error {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return ErrStandby
|
||||
}
|
||||
|
||||
// Clear any progress or config
|
||||
c.rekeyConfig = nil
|
||||
c.rekeyProgress = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
|
||||
// keys
|
||||
func (c *Core) RekeyRetrieveBackup() (*RekeyBackup, error) {
|
||||
// RecoveryRekeyUpdate is used to provide a new key part
|
||||
func (c *Core) RecoveryRekeyUpdate(key []byte, nonce string) (*RekeyResult, error) {
|
||||
// Ensure we are already unsealed
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
@ -309,7 +431,208 @@ func (c *Core) RekeyRetrieveBackup() (*RekeyBackup, error) {
|
|||
return nil, ErrStandby
|
||||
}
|
||||
|
||||
entry, err := c.physical.Get(coreUnsealKeysBackupPath)
|
||||
// Verify the key length
|
||||
min, max := c.barrier.KeyLength()
|
||||
max += shamir.ShareOverhead
|
||||
if len(key) < min {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
|
||||
}
|
||||
if len(key) > max {
|
||||
return nil, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Get the seal configuration
|
||||
barrierConfig, err := c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the barrier is initialized
|
||||
if barrierConfig == nil {
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
|
||||
existingConfig, err := c.seal.RecoveryConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure a rekey is in progress
|
||||
if c.recoveryRekeyConfig == nil {
|
||||
return nil, fmt.Errorf("no rekey in progress")
|
||||
}
|
||||
|
||||
if nonce != c.recoveryRekeyConfig.Nonce {
|
||||
return nil, fmt.Errorf("incorrect nonce supplied; nonce for this rekey operation is %s", c.recoveryRekeyConfig.Nonce)
|
||||
}
|
||||
|
||||
// Check if we already have this piece
|
||||
for _, existing := range c.recoveryRekeyProgress {
|
||||
if bytes.Equal(existing, key) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Store this key
|
||||
c.recoveryRekeyProgress = append(c.recoveryRekeyProgress, key)
|
||||
|
||||
// Check if we don't have enough keys to unlock
|
||||
if len(c.recoveryRekeyProgress) < existingConfig.SecretThreshold {
|
||||
c.logger.Printf("[DEBUG] core: cannot rekey, have %d of %d keys",
|
||||
len(c.recoveryRekeyProgress), existingConfig.SecretThreshold)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Recover the master key
|
||||
var masterKey []byte
|
||||
if existingConfig.SecretThreshold == 1 {
|
||||
masterKey = c.recoveryRekeyProgress[0]
|
||||
c.recoveryRekeyProgress = nil
|
||||
} else {
|
||||
masterKey, err = shamir.Combine(c.recoveryRekeyProgress)
|
||||
c.recoveryRekeyProgress = nil
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compute recovery key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the recovery key
|
||||
if err := c.seal.VerifyRecoveryKey(masterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: rekey aborted, recovery key verification failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate a new master key
|
||||
newMasterKey, err := c.barrier.GenerateKey()
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to generate recovery key: %v", err)
|
||||
return nil, fmt.Errorf("recovery key generation failed: %v", err)
|
||||
}
|
||||
|
||||
// Return the master key if only a single key part is used
|
||||
results := &RekeyResult{
|
||||
Backup: c.recoveryRekeyConfig.Backup,
|
||||
}
|
||||
|
||||
if c.recoveryRekeyConfig.SecretShares == 1 {
|
||||
results.SecretShares = append(results.SecretShares, newMasterKey)
|
||||
} else {
|
||||
// Split the master key using the Shamir algorithm
|
||||
shares, err := shamir.Split(newMasterKey, c.recoveryRekeyConfig.SecretShares, c.recoveryRekeyConfig.SecretThreshold)
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to generate shares: %v", err)
|
||||
return nil, fmt.Errorf("failed to generate shares: %v", err)
|
||||
}
|
||||
results.SecretShares = shares
|
||||
}
|
||||
|
||||
if len(c.recoveryRekeyConfig.PGPKeys) > 0 {
|
||||
hexEncodedShares := make([][]byte, len(results.SecretShares))
|
||||
for i, _ := range results.SecretShares {
|
||||
hexEncodedShares[i] = []byte(hex.EncodeToString(results.SecretShares[i]))
|
||||
}
|
||||
results.PGPFingerprints, results.SecretShares, err = pgpkeys.EncryptShares(hexEncodedShares, c.recoveryRekeyConfig.PGPKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.recoveryRekeyConfig.Backup {
|
||||
backupInfo := map[string][]string{}
|
||||
for i := 0; i < len(results.PGPFingerprints); i++ {
|
||||
encShare := bytes.NewBuffer(results.SecretShares[i])
|
||||
if backupInfo[results.PGPFingerprints[i]] == nil {
|
||||
backupInfo[results.PGPFingerprints[i]] = []string{hex.EncodeToString(encShare.Bytes())}
|
||||
} else {
|
||||
backupInfo[results.PGPFingerprints[i]] = append(backupInfo[results.PGPFingerprints[i]], hex.EncodeToString(encShare.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
backupVals := &RekeyBackup{
|
||||
Nonce: c.recoveryRekeyConfig.Nonce,
|
||||
Keys: backupInfo,
|
||||
}
|
||||
buf, err := json.Marshal(backupVals)
|
||||
if err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to marshal recovery key backup: %v", err)
|
||||
return nil, fmt.Errorf("failed to marshal recovery key backup: %v", err)
|
||||
}
|
||||
pe := &physical.Entry{
|
||||
Key: coreRecoveryUnsealKeysBackupPath,
|
||||
Value: buf,
|
||||
}
|
||||
if err = c.physical.Put(pe); err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to save unseal key backup: %v", err)
|
||||
return nil, fmt.Errorf("failed to save unseal key backup: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.seal.SetRecoveryKey(newMasterKey); err != nil {
|
||||
c.logger.Printf("[ERR] core: failed to set recovery key: %v", err)
|
||||
return nil, fmt.Errorf("failed to set recovery key: %v", err)
|
||||
}
|
||||
|
||||
if err := c.seal.SetRecoveryConfig(c.recoveryRekeyConfig); err != nil {
|
||||
c.logger.Printf("[ERR] core: error saving rekey seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to save rekey seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Done!
|
||||
c.recoveryRekeyProgress = nil
|
||||
c.recoveryRekeyConfig = nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// RekeyCancel is used to cancel an inprogress rekey
|
||||
func (c *Core) RekeyCancel(recovery bool) error {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
// Clear any progress or config
|
||||
if recovery {
|
||||
c.recoveryRekeyConfig = nil
|
||||
c.recoveryRekeyProgress = nil
|
||||
} else {
|
||||
c.barrierRekeyConfig = nil
|
||||
c.barrierRekeyProgress = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RekeyRetrieveBackup is used to retrieve any backed-up PGP-encrypted unseal
|
||||
// keys
|
||||
func (c *Core) RekeyRetrieveBackup(recovery bool) (*RekeyBackup, error) {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
return nil, ErrSealed
|
||||
}
|
||||
if c.standby {
|
||||
return nil, ErrStandby
|
||||
}
|
||||
|
||||
c.rekeyLock.RLock()
|
||||
defer c.rekeyLock.RUnlock()
|
||||
|
||||
var entry *physical.Entry
|
||||
var err error
|
||||
if recovery {
|
||||
entry, err = c.physical.Get(coreRecoveryUnsealKeysBackupPath)
|
||||
} else {
|
||||
entry, err = c.physical.Get(coreBarrierUnsealKeysBackupPath)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -327,7 +650,7 @@ func (c *Core) RekeyRetrieveBackup() (*RekeyBackup, error) {
|
|||
}
|
||||
|
||||
// RekeyDeleteBackup is used to delete any backed-up PGP-encrypted unseal keys
|
||||
func (c *Core) RekeyDeleteBackup() error {
|
||||
func (c *Core) RekeyDeleteBackup(recovery bool) error {
|
||||
c.stateLock.RLock()
|
||||
defer c.stateLock.RUnlock()
|
||||
if c.sealed {
|
||||
|
@ -337,5 +660,11 @@ func (c *Core) RekeyDeleteBackup() error {
|
|||
return ErrStandby
|
||||
}
|
||||
|
||||
return c.physical.Delete(coreUnsealKeysBackupPath)
|
||||
c.rekeyLock.Lock()
|
||||
defer c.rekeyLock.Unlock()
|
||||
|
||||
if recovery {
|
||||
return c.physical.Delete(coreRecoveryUnsealKeysBackupPath)
|
||||
}
|
||||
return c.physical.Delete(coreBarrierUnsealKeysBackupPath)
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@ package vault
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
||||
func TestCore_Rekey_Lifecycle(t *testing.T) {
|
||||
c, master, _ := TestCoreUnsealed(t)
|
||||
|
||||
// Verify update not allowed
|
||||
if _, err := c.RekeyUpdate(master, ""); err == nil {
|
||||
if _, err := c.RekeyUpdate(master, "", false); err == nil {
|
||||
t.Fatalf("no rekey in progress")
|
||||
}
|
||||
|
||||
// Should be no progress
|
||||
num, err := c.RekeyProgress()
|
||||
num, err := c.RekeyProgress(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -23,7 +25,7 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
|
|||
}
|
||||
|
||||
// Should be no config
|
||||
conf, err := c.RekeyConfig()
|
||||
conf, err := c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -32,7 +34,7 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
|
|||
}
|
||||
|
||||
// Cancel should be idempotent
|
||||
err = c.RekeyCancel()
|
||||
err = c.RekeyCancel(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -42,13 +44,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
SecretShares: 5,
|
||||
}
|
||||
err = c.RekeyInit(newConf)
|
||||
err = c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Should get config
|
||||
conf, err = c.RekeyConfig()
|
||||
conf, err = c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -58,13 +60,13 @@ func TestCore_Rekey_Lifecycle(t *testing.T) {
|
|||
}
|
||||
|
||||
// Cancel should be clear
|
||||
err = c.RekeyCancel()
|
||||
err = c.RekeyCancel(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Should be no config
|
||||
conf, err = c.RekeyConfig()
|
||||
conf, err = c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -81,7 +83,7 @@ func TestCore_Rekey_Init(t *testing.T) {
|
|||
SecretThreshold: 5,
|
||||
SecretShares: 1,
|
||||
}
|
||||
err := c.RekeyInit(badConf)
|
||||
err := c.RekeyInit(badConf, false)
|
||||
if err == nil {
|
||||
t.Fatalf("should fail")
|
||||
}
|
||||
|
@ -91,13 +93,13 @@ func TestCore_Rekey_Init(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
SecretShares: 5,
|
||||
}
|
||||
err = c.RekeyInit(newConf)
|
||||
err = c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Second should fail
|
||||
err = c.RekeyInit(newConf)
|
||||
err = c.RekeyInit(newConf, false)
|
||||
if err == nil {
|
||||
t.Fatalf("should fail")
|
||||
}
|
||||
|
@ -111,13 +113,13 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
SecretShares: 5,
|
||||
}
|
||||
err := c.RekeyInit(newConf)
|
||||
err := c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err := c.RekeyConfig()
|
||||
rkconf, err := c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -126,7 +128,7 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
// Provide the master
|
||||
result, err := c.RekeyUpdate(master, rkconf.Nonce)
|
||||
result, err := c.RekeyUpdate(master, rkconf.Nonce, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -135,7 +137,7 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
// Should be no progress
|
||||
num, err := c.RekeyProgress()
|
||||
num, err := c.RekeyProgress(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -144,7 +146,7 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
// Should be no config
|
||||
conf, err := c.RekeyConfig()
|
||||
conf, err := c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -153,14 +155,17 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
// SealConfig should update
|
||||
conf, err = c.SealConfig()
|
||||
sealConf, err := c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if sealConf == nil {
|
||||
t.Fatal("seal configuration is nil")
|
||||
}
|
||||
|
||||
newConf.Nonce = rkconf.Nonce
|
||||
if !reflect.DeepEqual(conf, newConf) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v\n", conf, newConf)
|
||||
if !reflect.DeepEqual(sealConf, newConf) {
|
||||
t.Fatalf("\nexpected: %#v\nactual: %#v\n", sealConf, newConf)
|
||||
}
|
||||
|
||||
// Attempt unseal
|
||||
|
@ -183,13 +188,13 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
SecretThreshold: 1,
|
||||
SecretShares: 1,
|
||||
}
|
||||
err = c.RekeyInit(newConf)
|
||||
err = c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err = c.RekeyConfig()
|
||||
rkconf, err = c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -200,13 +205,13 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
// Provide the parts master
|
||||
oldResult := result
|
||||
for i := 0; i < 3; i++ {
|
||||
result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce)
|
||||
result, err = c.RekeyUpdate(oldResult.SecretShares[i], rkconf.Nonce, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Should be progress
|
||||
num, err := c.RekeyProgress()
|
||||
num, err := c.RekeyProgress(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -232,13 +237,14 @@ func TestCore_Rekey_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
// SealConfig should update
|
||||
conf, err = c.SealConfig()
|
||||
sealConf, err = c.seal.BarrierConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
newConf.Nonce = rkconf.Nonce
|
||||
if !reflect.DeepEqual(conf, newConf) {
|
||||
t.Fatalf("bad: %#v", conf)
|
||||
if !reflect.DeepEqual(sealConf, newConf) {
|
||||
t.Fatalf("bad: %#v", sealConf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,13 +256,13 @@ func TestCore_Rekey_InvalidMaster(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
SecretShares: 5,
|
||||
}
|
||||
err := c.RekeyInit(newConf)
|
||||
err := c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err := c.RekeyConfig()
|
||||
rkconf, err := c.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -266,7 +272,7 @@ func TestCore_Rekey_InvalidMaster(t *testing.T) {
|
|||
|
||||
// Provide the master (invalid)
|
||||
master[0]++
|
||||
_, err = c.RekeyUpdate(master, rkconf.Nonce)
|
||||
_, err = c.RekeyUpdate(master, rkconf.Nonce, false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
|
@ -280,14 +286,107 @@ func TestCore_Rekey_InvalidNonce(t *testing.T) {
|
|||
SecretThreshold: 3,
|
||||
SecretShares: 5,
|
||||
}
|
||||
err := c.RekeyInit(newConf)
|
||||
err := c.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Provide the nonce (invalid)
|
||||
_, err = c.RekeyUpdate(master, "abcd")
|
||||
_, err = c.RekeyUpdate(master, "abcd", false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_Standby_Rekey(t *testing.T) {
|
||||
// Create the first core and initialize it
|
||||
inm := physical.NewInmem()
|
||||
inmha := physical.NewInmemHA()
|
||||
advertiseOriginal := "http://127.0.0.1:8200"
|
||||
core, err := NewCore(&CoreConfig{
|
||||
Physical: inm,
|
||||
HAPhysical: inmha,
|
||||
AdvertiseAddr: advertiseOriginal,
|
||||
DisableMlock: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
key, root := TestCoreInit(t, core)
|
||||
if _, err := core.Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
}
|
||||
|
||||
// Wait for core to become active
|
||||
testWaitActive(t, core)
|
||||
|
||||
// Create a second core, attached to same in-memory store
|
||||
advertiseOriginal2 := "http://127.0.0.1:8500"
|
||||
core2, err := NewCore(&CoreConfig{
|
||||
Physical: inm,
|
||||
HAPhysical: inmha,
|
||||
AdvertiseAddr: advertiseOriginal2,
|
||||
DisableMlock: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if _, err := core2.Unseal(TestKeyCopy(key)); err != nil {
|
||||
t.Fatalf("unseal err: %s", err)
|
||||
}
|
||||
|
||||
// Rekey the master key
|
||||
newConf := &SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
}
|
||||
err = core.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err := core.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if rkconf == nil {
|
||||
t.Fatalf("bad: no rekey config received")
|
||||
}
|
||||
result, err := core.RekeyUpdate(key, rkconf.Nonce, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("rekey failed")
|
||||
}
|
||||
|
||||
// Seal the first core, should step down
|
||||
err = core.Seal(root)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Wait for core2 to become active
|
||||
testWaitActive(t, core2)
|
||||
|
||||
// Rekey the master key again
|
||||
err = core2.RekeyInit(newConf, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// Fetch new config with generated nonce
|
||||
rkconf, err = core2.RekeyConfig(false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if rkconf == nil {
|
||||
t.Fatalf("bad: no rekey config received")
|
||||
}
|
||||
result, err = core2.RekeyUpdate(result.SecretShares[0], rkconf.Nonce, false)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("rekey failed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
package vault
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/vault/physical"
|
||||
|
||||
"golang.org/x/crypto/openpgp"
|
||||
"golang.org/x/crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
const (
|
||||
// barrierSealConfigPath is the path used to store our seal configuration.
|
||||
// This value is stored in plaintext, since we must be able to read it even
|
||||
// with the Vault sealed. This is required so that we know how many secret
|
||||
// parts must be used to reconstruct the master key.
|
||||
barrierSealConfigPath = "core/seal-config"
|
||||
|
||||
// recoverySealConfigPath is the path to the recovery key seal
|
||||
// configuration. It is inside the barrier.
|
||||
recoverySealConfigPath = "core/recovery-seal-config"
|
||||
|
||||
// recoveryKeyPath is the path to the recovery key
|
||||
recoveryKeyPath = "core/recovery-key"
|
||||
)
|
||||
|
||||
type Seal interface {
|
||||
SetCore(*Core)
|
||||
Init() error
|
||||
|
||||
StoredKeysSupported() bool
|
||||
SetStoredKeys([][]byte) error
|
||||
GetStoredKeys() ([][]byte, error)
|
||||
|
||||
BarrierConfig() (*SealConfig, error)
|
||||
SetBarrierConfig(*SealConfig) error
|
||||
|
||||
RecoveryKeySupported() bool
|
||||
RecoveryConfig() (*SealConfig, error)
|
||||
SetRecoveryConfig(*SealConfig) error
|
||||
SetRecoveryKey([]byte) error
|
||||
VerifyRecoveryKey([]byte) error
|
||||
}
|
||||
|
||||
type DefaultSeal struct {
|
||||
config *SealConfig
|
||||
core *Core
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) checkCore() error {
|
||||
if d.core == nil {
|
||||
return fmt.Errorf("seal does not have a core set")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) SetCore(core *Core) {
|
||||
d.core = core
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) StoredKeysSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) RecoveryKeySupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) SetStoredKeys(keys [][]byte) error {
|
||||
return fmt.Errorf("[ERR] core: stored keys are not supported")
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) GetStoredKeys() ([][]byte, error) {
|
||||
return nil, fmt.Errorf("[ERR] core: stored keys are not supported")
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) BarrierConfig() (*SealConfig, error) {
|
||||
if d.config != nil {
|
||||
return d.config.Clone(), nil
|
||||
}
|
||||
|
||||
if err := d.checkCore(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetch the core configuration
|
||||
pe, err := d.core.physical.Get(barrierSealConfigPath)
|
||||
if err != nil {
|
||||
d.core.logger.Printf("[ERR] core: failed to read seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to check seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// If the seal configuration is missing, we are not initialized
|
||||
if pe == nil {
|
||||
d.core.logger.Printf("[INFO] core: seal configuration missing, not initialized")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var conf SealConfig
|
||||
|
||||
// Decode the barrier entry
|
||||
if err := json.Unmarshal(pe.Value, &conf); err != nil {
|
||||
d.core.logger.Printf("[ERR] core: failed to decode seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("failed to decode seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Check for a valid seal configuration
|
||||
if err := conf.Validate(); err != nil {
|
||||
d.core.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
|
||||
return nil, fmt.Errorf("seal validation failed: %v", err)
|
||||
}
|
||||
|
||||
d.config = &conf
|
||||
return d.config.Clone(), nil
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) SetBarrierConfig(config *SealConfig) error {
|
||||
if err := d.checkCore(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Encode the seal configuration
|
||||
buf, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode seal configuration: %v", err)
|
||||
}
|
||||
|
||||
// Store the seal configuration
|
||||
pe := &physical.Entry{
|
||||
Key: barrierSealConfigPath,
|
||||
Value: buf,
|
||||
}
|
||||
|
||||
if err := d.core.physical.Put(pe); err != nil {
|
||||
d.core.logger.Printf("[ERR] core: failed to write seal configuration: %v", err)
|
||||
return fmt.Errorf("failed to write seal configuration: %v", err)
|
||||
}
|
||||
|
||||
d.config = config.Clone()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) RecoveryConfig() (*SealConfig, error) {
|
||||
return nil, fmt.Errorf("recovery not supported")
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) SetRecoveryConfig(config *SealConfig) error {
|
||||
return fmt.Errorf("recovery not supported")
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) VerifyRecoveryKey([]byte) error {
|
||||
return fmt.Errorf("recovery not supported")
|
||||
}
|
||||
|
||||
func (d *DefaultSeal) SetRecoveryKey(key []byte) error {
|
||||
return fmt.Errorf("recovery not supported")
|
||||
}
|
||||
|
||||
// SealConfig is used to describe the seal configuration
|
||||
type SealConfig struct {
|
||||
// SecretShares is the number of shares the secret is split into. This is
|
||||
// the N value of Shamir.
|
||||
SecretShares int `json:"secret_shares"`
|
||||
|
||||
// SecretThreshold is the number of parts required to open the vault. This
|
||||
// is the T value of Shamir.
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
|
||||
// PGPKeys is the array of public PGP keys used, if requested, to encrypt
|
||||
// the output unseal tokens. If provided, it sets the value of
|
||||
// SecretShares. Ordering is important.
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
|
||||
// Nonce is a nonce generated by Vault used to ensure that when unseal keys
|
||||
// are submitted for a rekey operation, the rekey operation itself is the
|
||||
// one intended. This prevents hijacking of the rekey operation, since it
|
||||
// is unauthenticated.
|
||||
Nonce string `json:"nonce"`
|
||||
|
||||
// Backup indicates whether or not a backup of PGP-encrypted unseal keys
|
||||
// should be stored at coreUnsealKeysBackupPath after successful rekeying.
|
||||
Backup bool `json:"backup"`
|
||||
|
||||
// How many keys to store, for seals that support storage.
|
||||
StoredShares int `json:"stored_shares"`
|
||||
}
|
||||
|
||||
// Validate is used to sanity check the seal configuration
|
||||
func (s *SealConfig) Validate() error {
|
||||
if s.SecretShares < 1 {
|
||||
return fmt.Errorf("shares must be at least one")
|
||||
}
|
||||
if s.SecretThreshold < 1 {
|
||||
return fmt.Errorf("threshold must be at least one")
|
||||
}
|
||||
if s.SecretShares > 1 && s.SecretThreshold == 1 {
|
||||
return fmt.Errorf("threshold must be greater than one for multiple shares")
|
||||
}
|
||||
if s.SecretShares > 255 {
|
||||
return fmt.Errorf("shares must be less than 256")
|
||||
}
|
||||
if s.SecretThreshold > 255 {
|
||||
return fmt.Errorf("threshold must be less than 256")
|
||||
}
|
||||
if s.SecretThreshold > s.SecretShares {
|
||||
return fmt.Errorf("threshold cannot be larger than shares")
|
||||
}
|
||||
if s.StoredShares > s.SecretShares {
|
||||
return fmt.Errorf("stored keys cannot be larger than shares")
|
||||
}
|
||||
if len(s.PGPKeys) > 0 && len(s.PGPKeys) != s.SecretShares-s.StoredShares {
|
||||
return fmt.Errorf("count mismatch between number of provided PGP keys and number of shares")
|
||||
}
|
||||
if len(s.PGPKeys) > 0 {
|
||||
for _, keystring := range s.PGPKeys {
|
||||
data, err := base64.StdEncoding.DecodeString(keystring)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error decoding given PGP key: %s", err)
|
||||
}
|
||||
_, err = openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing given PGP key: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SealConfig) Clone() *SealConfig {
|
||||
ret := &SealConfig{
|
||||
SecretShares: s.SecretShares,
|
||||
SecretThreshold: s.SecretThreshold,
|
||||
Nonce: s.Nonce,
|
||||
Backup: s.Backup,
|
||||
StoredShares: s.StoredShares,
|
||||
}
|
||||
if len(s.PGPKeys) > 0 {
|
||||
ret.PGPKeys = make([]string, len(s.PGPKeys))
|
||||
copy(ret.PGPKeys, s.PGPKeys)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type SealAccess struct {
|
||||
seal Seal
|
||||
}
|
||||
|
||||
func (s *SealAccess) SetSeal(seal Seal) {
|
||||
s.seal = seal
|
||||
}
|
||||
|
||||
func (s *SealAccess) StoredKeysSupported() bool {
|
||||
return s.seal.StoredKeysSupported()
|
||||
}
|
||||
|
||||
func (s *SealAccess) BarrierConfig() (*SealConfig, error) {
|
||||
return s.seal.BarrierConfig()
|
||||
}
|
||||
|
||||
func (s *SealAccess) RecoveryKeySupported() bool {
|
||||
return s.seal.RecoveryKeySupported()
|
||||
}
|
||||
|
||||
func (s *SealAccess) RecoveryConfig() (*SealConfig, error) {
|
||||
return s.seal.RecoveryConfig()
|
||||
}
|
|
@ -121,7 +121,7 @@ func TestCoreInit(t *testing.T, core *Core) ([]byte, string) {
|
|||
result, err := core.Initialize(&SealConfig{
|
||||
SecretShares: 1,
|
||||
SecretThreshold: 1,
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue