-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generate database encryption keys in bootstrap tooling #1340
Changes from 4 commits
5f17a97
7890470
a19f966
7bf42cb
52e1baf
f958aea
7a05ab1
63ca09f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"path" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/onflow/flow-go/cmd/bootstrap/utils" | ||
model "github.com/onflow/flow-go/model/bootstrap" | ||
) | ||
|
||
// dbEncryptionKyCmd adds a command to the bootstrap utility which generates an | ||
// AES-256 key for encrypting the secrets database, and writes it to the default | ||
// path. | ||
var dbEncryptionKyCmd = &cobra.Command{ | ||
Use: "db-encryption-key", | ||
Short: "Generates encryption key for secrets database and writes it to the default path within the bootstrap directory", | ||
Run: dbEncryptionKeyRun, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(dbEncryptionKyCmd) | ||
} | ||
|
||
func dbEncryptionKeyRun(_ *cobra.Command, _ []string) { | ||
|
||
// read nodeID written to boostrap dir by `bootstrap key` | ||
nodeID, err := readNodeID() | ||
if err != nil { | ||
log.Fatal().Err(err).Msg("could not read node id") | ||
} | ||
|
||
// check if the key already exists | ||
dbEncryptionKeyPath := fmt.Sprintf(model.PathSecretsEncryptionKey, nodeID) | ||
exists, err := pathExists(path.Join(flagOutdir, dbEncryptionKeyPath)) | ||
if err != nil { | ||
log.Fatal().Err(err).Msg("could not check if db encryption key already exists") | ||
} | ||
if exists { | ||
log.Warn().Msg("DB encryption key already exists, exiting...") | ||
return | ||
} | ||
|
||
dbEncryptionKey, err := utils.GenerateSecretsDBEncryptionKey() | ||
if err != nil { | ||
log.Fatal().Err(err).Msg("could not generate db encryption key") | ||
} | ||
log.Info().Msg("generated db encryption key") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better to print path |
||
|
||
writeText(dbEncryptionKeyPath, dbEncryptionKey) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/onflow/flow-go/model/bootstrap" | ||
ioutils "github.com/onflow/flow-go/utils/io" | ||
"github.com/onflow/flow-go/utils/unittest" | ||
) | ||
|
||
// Test that attempting to generate a db encryption key is a no-op if a | ||
// key file already exists. | ||
func TestDBEncryptionKeyFileExists(t *testing.T) { | ||
unittest.RunWithTempDir(t, func(bootDir string) { | ||
|
||
var keyFileExistsRegex = regexp.MustCompile(`^DB encryption key already exists`) | ||
|
||
flagOutdir = bootDir | ||
flagRole = "verification" | ||
flagAddress = "189.123.123.42:3869" | ||
|
||
// generate all bootstrapping files | ||
keyCmdRun(nil, nil) | ||
require.DirExists(t, filepath.Join(flagOutdir, bootstrap.DirnamePublicBootstrap)) | ||
require.DirExists(t, filepath.Join(flagOutdir, bootstrap.DirPrivateRoot)) | ||
|
||
nodeIDPath := filepath.Join(flagOutdir, bootstrap.PathNodeID) | ||
require.FileExists(t, nodeIDPath) | ||
b, err := ioutils.ReadFile(nodeIDPath) | ||
require.NoError(t, err) | ||
nodeID := strings.TrimSpace(string(b)) | ||
|
||
// make sure file exists | ||
encryptionKeyPath := filepath.Join(flagOutdir, fmt.Sprintf(bootstrap.PathSecretsEncryptionKey, nodeID)) | ||
require.FileExists(t, encryptionKeyPath) | ||
|
||
// read the file | ||
keyFileBefore, err := ioutils.ReadFile(encryptionKeyPath) | ||
require.NoError(t, err) | ||
|
||
// create a hooked logger | ||
var hook unittest.LoggerHook | ||
log, hook = unittest.HookedLogger() | ||
|
||
// run the encryption key gen tool | ||
dbEncryptionKeyRun(nil, nil) | ||
|
||
// ensure regex matches | ||
require.Regexp(t, keyFileExistsRegex, hook.Logs()) | ||
|
||
// ensure the existing file is unmodified | ||
keyFileAfter, err := ioutils.ReadFile(encryptionKeyPath) | ||
require.NoError(t, err) | ||
assert.Equal(t, keyFileBefore, keyFileAfter) | ||
}) | ||
} | ||
|
||
// Test that we can generate a key file if none exists. | ||
func TestDBEncryptionKeyFileCreated(t *testing.T) { | ||
unittest.RunWithTempDir(t, func(bootDir string) { | ||
|
||
var keyFileCreatedRegex = `^generated db encryption key` + | ||
`wrote file ` + bootDir + `/private-root-information/private-node-info_\S+/secretsdb-key` | ||
|
||
flagOutdir = bootDir | ||
flagRole = "verification" | ||
flagAddress = "189.123.123.42:3869" | ||
|
||
// generate all bootstrapping files | ||
keyCmdRun(nil, nil) | ||
require.DirExists(t, filepath.Join(flagOutdir, bootstrap.DirnamePublicBootstrap)) | ||
require.DirExists(t, filepath.Join(flagOutdir, bootstrap.DirPrivateRoot)) | ||
|
||
nodeIDPath := filepath.Join(flagOutdir, bootstrap.PathNodeID) | ||
require.FileExists(t, nodeIDPath) | ||
b, err := ioutils.ReadFile(nodeIDPath) | ||
require.NoError(t, err) | ||
nodeID := strings.TrimSpace(string(b)) | ||
|
||
// delete db encryption key file | ||
dbEncryptionKeyPath := filepath.Join(flagOutdir, fmt.Sprintf(bootstrap.PathSecretsEncryptionKey, nodeID)) | ||
err = os.Remove(dbEncryptionKeyPath) | ||
require.NoError(t, err) | ||
|
||
// confirm file was removed | ||
require.NoFileExists(t, dbEncryptionKeyPath) | ||
|
||
// create a hooked logger | ||
var hook unittest.LoggerHook | ||
log, hook = unittest.HookedLogger() | ||
|
||
// run the encryption key gen tool | ||
dbEncryptionKeyRun(nil, nil) | ||
|
||
// ensure regex matches | ||
require.Regexp(t, keyFileCreatedRegex, hook.Logs()) | ||
|
||
// make sure file exists (regex checks this too) | ||
require.FileExists(t, dbEncryptionKeyPath) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -67,7 +67,7 @@ func keyCmdRun(_ *cobra.Command, _ []string) { | |||||
validateAddressFormat(flagAddress) | ||||||
|
||||||
// generate staking and network keys | ||||||
networkKey, stakingKey, err := generateKeys() | ||||||
networkKey, stakingKey, secretsDBKey, err := generateKeys() | ||||||
if err != nil { | ||||||
log.Fatal().Err(err).Msg("could not generate staking or network keys") | ||||||
} | ||||||
|
@@ -88,6 +88,7 @@ func keyCmdRun(_ *cobra.Command, _ []string) { | |||||
// write files | ||||||
writeText(model.PathNodeID, []byte(nodeInfo.NodeID.String())) | ||||||
writeJSON(fmt.Sprintf(model.PathNodeInfoPriv, nodeInfo.NodeID), private) | ||||||
writeText(fmt.Sprintf(model.PathSecretsEncryptionKey, nodeInfo.NodeID), secretsDBKey) | ||||||
writeJSON(fmt.Sprintf(model.PathNodeInfoPub, nodeInfo.NodeID), nodeInfo.Public()) | ||||||
|
||||||
// write machine account info | ||||||
|
@@ -106,25 +107,32 @@ func keyCmdRun(_ *cobra.Command, _ []string) { | |||||
} | ||||||
} | ||||||
|
||||||
func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, error) { | ||||||
func generateKeys() (crypto.PrivateKey, crypto.PrivateKey, []byte, error) { | ||||||
|
||||||
log.Debug().Msg("will generate networking key") | ||||||
networkSeed := validateSeed(flagNetworkSeed) | ||||||
networkKey, err := utils.GenerateNetworkingKey(networkSeed) | ||||||
if err != nil { | ||||||
return nil, nil, fmt.Errorf("could not generate networking key: %w", err) | ||||||
return nil, nil, nil, fmt.Errorf("could not generate networking key: %w", err) | ||||||
} | ||||||
log.Info().Msg("generated networking key") | ||||||
|
||||||
log.Debug().Msg("will generate staking key") | ||||||
stakingSeed := validateSeed(flagStakingSeed) | ||||||
stakingKey, err := utils.GenerateStakingKey(stakingSeed) | ||||||
if err != nil { | ||||||
return nil, nil, fmt.Errorf("could not generate staking key: %w", err) | ||||||
return nil, nil, nil, fmt.Errorf("could not generate staking key: %w", err) | ||||||
} | ||||||
log.Info().Msg("generated staking key") | ||||||
|
||||||
return networkKey, stakingKey, nil | ||||||
log.Debug().Msg("will generate db encryption key") | ||||||
secretsDBKey, err := utils.GenerateSecretsDBEncryptionKey() | ||||||
if err != nil { | ||||||
return nil, nil, nil, fmt.Errorf("could not generate secrets db encryption key: %w", err) | ||||||
} | ||||||
log.Info().Msg("generated db encryption key") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
otherwise, we are printing the same log twice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The two logs are for two separate commands. One is for setting up all of your keys at once ( |
||||||
|
||||||
return networkKey, stakingKey, secretsDBKey, nil | ||||||
} | ||||||
|
||||||
func generateMachineAccountKey() (crypto.PrivateKey, error) { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to print the path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added it to the logger f958aea