Skip to content

Commit

Permalink
make keyring non-interactive
Browse files Browse the repository at this point in the history
  • Loading branch information
fredcarle committed Sep 17, 2024
1 parent b477313 commit a91136a
Show file tree
Hide file tree
Showing 20 changed files with 93 additions and 96 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ The following keys are loaded from the keyring on start:
- `peer-key` Ed25519 private key (required)
- `encryption-key` AES-128, AES-192, or AES-256 key (optional)

To randomly generate the required keys, run the following command:
A secret to unlock the keyring is required on start and must be provided via the `DEFRADB_KEYRING_SECRET` environment variable. If a `.env` file is available at the root of the project, the secret can be stored there.

The keys will be randomly generated on the inital start of the node if they are not found.

Alternatively, to randomly generate the required keys, run the following command:

```
defradb keyring generate
Expand Down
5 changes: 5 additions & 0 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"path/filepath"
"strings"

"github.com/joho/godotenv"
"github.com/sourcenetwork/corelog"
"github.com/spf13/pflag"
"github.com/spf13/viper"
Expand Down Expand Up @@ -63,6 +64,7 @@ var configFlags = map[string]string{
"keyring-backend": "keyring.backend",
"keyring-path": "keyring.path",
"no-keyring": "keyring.disabled",
"no-encryption-key": "keyring.noencryptionkey",
"source-hub-address": "acp.sourceHub.address",
}

Expand Down Expand Up @@ -94,6 +96,9 @@ var configDefaults = map[string]any{

// defaultConfig returns a new config with default values.
func defaultConfig() *viper.Viper {
// load environment variables from .env file if one exists
_ = godotenv.Load()

cfg := viper.New()

cfg.AutomaticEnv()
Expand Down
13 changes: 1 addition & 12 deletions cli/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ import (
"github.com/sourcenetwork/defradb/errors"
)

const errKeyringHelp = `%w
Did you forget to initialize the keyring?
Use the following command to generate the required keys:
defradb keyring generate
`

const (
errInvalidLensConfig string = "invalid lens configuration"
errSchemaVersionNotOfSchema string = "the given schema version is from a different schema"
Expand All @@ -40,6 +32,7 @@ var (
ErrSchemaVersionNotOfSchema = errors.New(errSchemaVersionNotOfSchema)
ErrViewAddMissingArgs = errors.New("please provide a base query and output SDL for this view")
ErrPolicyFileArgCanNotBeEmpty = errors.New("policy file argument can not be empty")
ErrMissingKeyringSecret = errors.New("missing keyring secret")
)

func NewErrRequiredFlagEmpty(longName string, shortName string) error {
Expand All @@ -61,7 +54,3 @@ func NewErrSchemaVersionNotOfSchema(schemaRoot string, schemaVersionID string) e
errors.NewKV("SchemaVersionID", schemaVersionID),
)
}

func NewErrKeyringHelp(inner error) error {
return fmt.Errorf(errKeyringHelp, inner)
}
3 changes: 3 additions & 0 deletions cli/keyring_export.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ func MakeKeyringExportCommand() *cobra.Command {
Long: `Export a private key.
Prints the hexadecimal representation of a private key.
The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.
Example:
defradb keyring export encryption-key`,
Args: cobra.ExactArgs(1),
Expand Down
7 changes: 3 additions & 4 deletions cli/keyring_export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,20 @@ package cli
import (
"bytes"
"encoding/hex"
"os"
"strings"
"testing"

"github.com/sourcenetwork/defradb/crypto"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestKeyringExport(t *testing.T) {
rootdir := t.TempDir()
readPassword = func(_ *cobra.Command, _ string) ([]byte, error) {
return []byte("secret"), nil
}
err := os.Setenv("DEFRA_KEYRING_SECRET", "password")
require.NoError(t, err)

keyBytes, err := crypto.GenerateAES256()
require.NoError(t, err)
Expand Down
3 changes: 3 additions & 0 deletions cli/keyring_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ func MakeKeyringGenerateCommand() *cobra.Command {
Randomly generate and store private keys in the keyring.
By default peer and encryption keys will be generated.
The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.
WARNING: This will overwrite existing keys in the keyring.
Example:
Expand Down
23 changes: 10 additions & 13 deletions cli/keyring_generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,23 @@
package cli

import (
"os"
"path/filepath"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestKeyringGenerate(t *testing.T) {
rootdir := t.TempDir()
readPassword = func(_ *cobra.Command, _ string) ([]byte, error) {
return []byte("secret"), nil
}
err := os.Setenv("DEFRA_KEYRING_SECRET", "password")
require.NoError(t, err)

cmd := NewDefraCommand()
cmd.SetArgs([]string{"keyring", "generate", "--rootdir", rootdir})

err := cmd.Execute()
err = cmd.Execute()
require.NoError(t, err)

assert.FileExists(t, filepath.Join(rootdir, "keys", encryptionKeyName))
Expand All @@ -37,14 +36,13 @@ func TestKeyringGenerate(t *testing.T) {

func TestKeyringGenerateNoEncryptionKey(t *testing.T) {
rootdir := t.TempDir()
readPassword = func(_ *cobra.Command, _ string) ([]byte, error) {
return []byte("secret"), nil
}
err := os.Setenv("DEFRA_KEYRING_SECRET", "password")
require.NoError(t, err)

cmd := NewDefraCommand()
cmd.SetArgs([]string{"keyring", "generate", "--no-encryption-key", "--rootdir", rootdir})

err := cmd.Execute()
err = cmd.Execute()
require.NoError(t, err)

assert.NoFileExists(t, filepath.Join(rootdir, "keys", encryptionKeyName))
Expand All @@ -53,14 +51,13 @@ func TestKeyringGenerateNoEncryptionKey(t *testing.T) {

func TestKeyringGenerateNoPeerKey(t *testing.T) {
rootdir := t.TempDir()
readPassword = func(_ *cobra.Command, _ string) ([]byte, error) {
return []byte("secret"), nil
}
err := os.Setenv("DEFRA_KEYRING_SECRET", "password")
require.NoError(t, err)

cmd := NewDefraCommand()
cmd.SetArgs([]string{"keyring", "generate", "--no-peer-key", "--rootdir", rootdir})

err := cmd.Execute()
err = cmd.Execute()
require.NoError(t, err)

assert.FileExists(t, filepath.Join(rootdir, "keys", encryptionKeyName))
Expand Down
3 changes: 3 additions & 0 deletions cli/keyring_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ func MakeKeyringImportCommand() *cobra.Command {
Long: `Import a private key.
Store an externally generated key in the keyring.
The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.
Example:
defradb keyring import encryption-key 0000000000000000`,
Args: cobra.ExactArgs(2),
Expand Down
7 changes: 3 additions & 4 deletions cli/keyring_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@ package cli

import (
"encoding/hex"
"os"
"path/filepath"
"testing"

"github.com/sourcenetwork/defradb/crypto"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestKeyringImport(t *testing.T) {
rootdir := t.TempDir()
readPassword = func(_ *cobra.Command, _ string) ([]byte, error) {
return []byte("secret"), nil
}
err := os.Setenv("DEFRA_KEYRING_SECRET", "password")
require.NoError(t, err)

keyBytes, err := crypto.GenerateAES256()
require.NoError(t, err)
Expand Down
37 changes: 31 additions & 6 deletions cli/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/sourcenetwork/immutable"
"github.com/spf13/cobra"

"github.com/sourcenetwork/defradb/crypto"
"github.com/sourcenetwork/defradb/errors"
"github.com/sourcenetwork/defradb/http"
"github.com/sourcenetwork/defradb/internal/db"
Expand Down Expand Up @@ -78,20 +79,40 @@ func MakeStartCommand() *cobra.Command {
if !cfg.GetBool("keyring.disabled") {
kr, err := openKeyring(cmd)
if err != nil {
return NewErrKeyringHelp(err)
return err

Check warning on line 82 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L82

Added line #L82 was not covered by tests
}
// load the required peer key
// load the required peer key or generate one if it doesn't exist
peerKey, err := kr.Get(peerKeyName)
if err != nil {
return NewErrKeyringHelp(err)
if err != nil && errors.Is(err, keyring.ErrNotFound) {
peerKey, err = crypto.GenerateEd25519()
if err != nil {
return err

Check warning on line 89 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L86-L89

Added lines #L86 - L89 were not covered by tests
}
err = kr.Set(peerKeyName, peerKey)
if err != nil {
return err

Check warning on line 93 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L91-L93

Added lines #L91 - L93 were not covered by tests
}
log.Info("generated peer key")
} else if err != nil {
return err

Check warning on line 97 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L95-L97

Added lines #L95 - L97 were not covered by tests
}
opts = append(opts, net.WithPrivateKey(peerKey))

// load the optional encryption key
encryptionKey, err := kr.Get(encryptionKeyName)
if err != nil && !errors.Is(err, keyring.ErrNotFound) {
if err != nil && errors.Is(err, keyring.ErrNotFound) && !cfg.GetBool("keyring.noencryptionkey") {
encryptionKey, err = crypto.GenerateAES256()
if err != nil {
return err

Check warning on line 106 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L103-L106

Added lines #L103 - L106 were not covered by tests
}
err = kr.Set(encryptionKeyName, encryptionKey)
if err != nil {
return err

Check warning on line 110 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L108-L110

Added lines #L108 - L110 were not covered by tests
}
log.Info("generated encryption key")
} else if err != nil && !errors.Is(err, keyring.ErrNotFound) {

Check warning on line 113 in cli/start.go

View check run for this annotation

Codecov / codecov/patch

cli/start.go#L112-L113

Added lines #L112 - L113 were not covered by tests
return err
}

opts = append(opts, node.WithBadgerEncryptionKey(encryptionKey))

sourceHubKeyName := cfg.GetString("acp.sourceHub.KeyName")
Expand Down Expand Up @@ -185,5 +206,9 @@ func MakeStartCommand() *cobra.Command {
cfg.GetString(configFlags["privkeypath"]),
"Path to the private key for tls",
)
cmd.Flags().Bool(
"no-encryption-key",
cfg.GetBool(configFlags["no-encryption-key"]),
"Skip generating an encryption key. Encryption at rest will be disabled. WARNING: This cannot be undone.")
return cmd
}
19 changes: 5 additions & 14 deletions cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ import (
"encoding/json"
"os"
"path/filepath"
"syscall"
"time"

"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/sourcenetwork/immutable"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.org/x/term"

acpIdentity "github.com/sourcenetwork/defradb/acp/identity"
"github.com/sourcenetwork/defradb/client"
Expand Down Expand Up @@ -58,14 +56,6 @@ const (
authTokenExpiration = time.Minute * 15
)

// readPassword reads a user input password without echoing it to the terminal.
var readPassword = func(cmd *cobra.Command, msg string) ([]byte, error) {
cmd.Print(msg)
pass, err := term.ReadPassword(int(syscall.Stdin))
cmd.Println("")
return pass, err
}

// mustGetContextDB returns the db for the current command context.
//
// If a db is not set in the current context this function panics.
Expand Down Expand Up @@ -214,10 +204,11 @@ func openKeyring(cmd *cobra.Command) (keyring.Keyring, error) {
if err := os.MkdirAll(path, 0755); err != nil {
return nil, err
}
prompt := keyring.PromptFunc(func(s string) ([]byte, error) {
return readPassword(cmd, s)
})
return keyring.OpenFileKeyring(path, prompt)
secret := []byte(cfg.GetString("keyring.secret"))
if len(secret) == 0 {
return nil, ErrMissingKeyringSecret

Check warning on line 209 in cli/utils.go

View check run for this annotation

Codecov / codecov/patch

cli/utils.go#L209

Added line #L209 was not covered by tests
}
return keyring.OpenFileKeyring(path, secret)
}

func writeJSON(cmd *cobra.Command, out any) error {
Expand Down
3 changes: 3 additions & 0 deletions docs/website/references/cli/defradb_keyring_export.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Export a private key
Export a private key.
Prints the hexadecimal representation of a private key.

The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.

Example:
defradb keyring export encryption-key

Expand Down
3 changes: 3 additions & 0 deletions docs/website/references/cli/defradb_keyring_generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Generate private keys.
Randomly generate and store private keys in the keyring.
By default peer and encryption keys will be generated.

The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.

WARNING: This will overwrite existing keys in the keyring.

Example:
Expand Down
3 changes: 3 additions & 0 deletions docs/website/references/cli/defradb_keyring_import.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Import a private key
Import a private key.
Store an externally generated key in the keyring.

The DEFRA_KEYRING_SECRET environment variable must be set to unlock the keyring.
This can also be done with a .env file in the root directory.

Example:
defradb keyring import encryption-key 0000000000000000

Expand Down
1 change: 1 addition & 0 deletions docs/website/references/cli/defradb_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defradb start [flags]
--allowed-origins stringArray List of origins to allow for CORS requests
-h, --help help for start
--max-txn-retries int Specify the maximum number of retries per transaction (default 5)
--no-encryption-key Skip generating an encryption key. Encryption at rest will be disabled. WARNING: This cannot be undone.
--no-p2p Disable the peer-to-peer network synchronization system
--p2paddr strings Listen addresses for the p2p network (formatted as a libp2p MultiAddr) (default [/ip4/127.0.0.1/tcp/9171])
--peers stringArray List of peers to connect to
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20240322071758-198d7dba8fb8
github.com/ipld/go-ipld-prime/storage/bsrvadapter v0.0.0-20240322071758-198d7dba8fb8
github.com/jbenet/goprocess v0.1.4
github.com/joho/godotenv v1.5.1
github.com/lens-vm/lens/host-go v0.0.0-20231127204031-8d858ed2926c
github.com/lestrrat-go/jwx/v2 v2.1.1
github.com/libp2p/go-libp2p v0.36.3
Expand Down Expand Up @@ -62,7 +63,6 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.30.0
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/term v0.24.0
google.golang.org/grpc v1.66.2
google.golang.org/protobuf v1.34.2
)
Expand Down Expand Up @@ -365,6 +365,7 @@ require (
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.24.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jorrizza/ed2curve25519 v0.1.0 h1:P58ZEiVKW4vknYuGyOXuskMm82rTJyGhgRGrMRcCE8E=
github.com/jorrizza/ed2curve25519 v0.1.0/go.mod h1:27VPNk2FnNqLQNvvVymiX41VE/nokPyn5HHP7gtfYlo=
Expand Down
Loading

0 comments on commit a91136a

Please sign in to comment.