Skip to content

Commit

Permalink
Refactor keystore interface into 3 different interfaces (elastic#16365)
Browse files Browse the repository at this point in the history
(cherry picked from commit d99dde2)
  • Loading branch information
ChrsMark committed Apr 2, 2020
1 parent e5b35b8 commit 3c58ec6
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 91 deletions.
35 changes: 27 additions & 8 deletions libbeat/cmd/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ func createKeystore(settings instance.Settings, force bool) error {
return err
}

writableKeystore, err := keystore.AsWritableKeystore(store)
if err != nil {
return fmt.Errorf("error creating the keystore: %s", err)
}

if store.IsPersisted() == true && force == false {
response := terminal.PromptYesNo("A keystore already exists, Overwrite?", false)
if response == true {
err := store.Create(true)
err := writableKeystore.Create(true)
if err != nil {
return fmt.Errorf("error creating the keystore: %s", err)
}
Expand All @@ -142,7 +147,7 @@ func createKeystore(settings instance.Settings, force bool) error {
return nil
}
} else {
err := store.Create(true)
err := writableKeystore.Create(true)
if err != nil {
return fmt.Errorf("Error creating the keystore: %s", err)
}
Expand All @@ -160,14 +165,19 @@ func addKey(store keystore.Keystore, keys []string, force, stdin bool) error {
return fmt.Errorf("could not create secret for: %s, you can only provide one key per invocation", keys)
}

writableKeystore, err := keystore.AsWritableKeystore(store)
if err != nil {
return fmt.Errorf("error creating the keystore: %s", err)
}

if store.IsPersisted() == false {
if force == false {
answer := terminal.PromptYesNo("The keystore does not exist. Do you want to create it?", false)
if answer == false {
return errors.New("exiting without creating keystore")
}
}
err := store.Create(true)
err := writableKeystore.Create(true)
if err != nil {
return fmt.Errorf("could not create keystore, error: %s", err)
}
Expand Down Expand Up @@ -202,10 +212,10 @@ func addKey(store keystore.Keystore, keys []string, force, stdin bool) error {
return fmt.Errorf("could not read value from the input, error: %s", err)
}
}
if err = store.Store(key, keyValue); err != nil {
if err = writableKeystore.Store(key, keyValue); err != nil {
return fmt.Errorf("could not add the key in the keystore, error: %s", err)
}
if err = store.Save(); err != nil {
if err = writableKeystore.Save(); err != nil {
return fmt.Errorf("fail to save the keystore: %s", err)
} else {
fmt.Println("Successfully updated the keystore")
Expand All @@ -218,6 +228,11 @@ func removeKey(store keystore.Keystore, keys []string) error {
return errors.New("you must supply at least one key to remove")
}

writableKeystore, err := keystore.AsWritableKeystore(store)
if err != nil {
return fmt.Errorf("error deleting the keystore: %s", err)
}

if store.IsPersisted() == false {
return errors.New("the keystore doesn't exist. Use the 'create' command to create one")
}
Expand All @@ -229,8 +244,8 @@ func removeKey(store keystore.Keystore, keys []string) error {
return fmt.Errorf("could not find key '%v' in the keystore", key)
}

store.Delete(key)
err = store.Save()
writableKeystore.Delete(key)
err = writableKeystore.Save()
if err != nil {
return fmt.Errorf("could not update the keystore with the changes, key: %s, error: %v", key, err)
}
Expand All @@ -240,7 +255,11 @@ func removeKey(store keystore.Keystore, keys []string) error {
}

func list(store keystore.Keystore) error {
keys, err := store.List()
listingKeystore, err := keystore.AsListingKeystore(store)
if err != nil {
return fmt.Errorf("error listing the keystore: %s", err)
}
keys, err := listingKeystore.List()
if err != nil {
return fmt.Errorf("could not read values from the keystore, error: %s", err)
}
Expand Down
27 changes: 27 additions & 0 deletions libbeat/keystore/file_keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ const (
// Version of the keystore format, will be added at the beginning of the file.
var version = []byte("v1")

// Packager defines a keystore that we can read the raw bytes and be packaged in an artifact.
type Packager interface {
Package() ([]byte, error)
ConfiguredPath() string
}

// FileKeystore Allows to store key / secrets pair securely into an encrypted local file.
type FileKeystore struct {
sync.RWMutex
Expand All @@ -66,6 +72,27 @@ type serializableSecureString struct {
Value []byte `json:"value"`
}

// Factory Create the right keystore with the configured options.
func Factory(cfg *common.Config, defaultPath string) (Keystore, error) {
config := defaultConfig

if cfg == nil {
cfg = common.NewConfig()
}
err := cfg.Unpack(&config)

if err != nil {
return nil, fmt.Errorf("could not read keystore configuration, err: %v", err)
}

if config.Path == "" {
config.Path = defaultPath
}

keystore, err := NewFileKeystore(config.Path)
return keystore, err
}

// NewFileKeystore returns an new File based keystore or an error, currently users cannot set their
// own password on the keystore, the default password will be an empty string. When the keystore
// is initialized the secrets are automatically loaded into memory.
Expand Down
93 changes: 63 additions & 30 deletions libbeat/keystore/file_keystore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ func TestCanCreateAKeyStore(t *testing.T) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore, err := NewFileKeystore(path)
keyStore, err := NewFileKeystore(path)
assert.NoError(t, err)
assert.Nil(t, keystore.Store(keyValue, secretValue))
assert.Nil(t, keystore.Save())

writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

assert.Nil(t, writableKeystore.Store(keyValue, secretValue))
assert.Nil(t, writableKeystore.Save())
}

func TestCanReadAnExistingKeyStoreWithEmptyString(t *testing.T) {
Expand All @@ -66,15 +70,18 @@ func TestCanDeleteAKeyFromTheStoreAndPersistChanges(t *testing.T) {

CreateAnExistingKeystore(path)

keystore, _ := NewFileKeystore(path)
_, err := keystore.Retrieve(keyValue)
keyStore, _ := NewFileKeystore(path)
_, err := keyStore.Retrieve(keyValue)
assert.NoError(t, err)

keystore.Delete(keyValue)
_, err = keystore.Retrieve(keyValue)
writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

writableKeystore.Delete(keyValue)
_, err = keyStore.Retrieve(keyValue)
assert.Error(t, err)

_ = keystore.Save()
_ = writableKeystore.Save()
newKeystore, err := NewFileKeystore(path)
_, err = newKeystore.Retrieve(keyValue)
assert.Error(t, err)
Expand Down Expand Up @@ -113,10 +120,14 @@ func TestFilePermissionOnUpdate(t *testing.T) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore := CreateAnExistingKeystore(path)
err := keystore.Store("newkey", []byte("newsecret"))
keyStore := CreateAnExistingKeystore(path)

writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)
err = keystore.Save()

err = writableKeystore.Store("newkey", []byte("newsecret"))
assert.NoError(t, err)
err = writableKeystore.Save()
assert.NoError(t, err)
stats, err := os.Stat(path)
assert.NoError(t, err)
Expand Down Expand Up @@ -152,9 +163,12 @@ func TestReturnsUsedKeysInTheStore(t *testing.T) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore := CreateAnExistingKeystore(path)
keyStore := CreateAnExistingKeystore(path)

listingKeystore, err := AsListingKeystore(keyStore)
assert.NoError(t, err)

keys, err := keystore.List()
keys, err := listingKeystore.List()

assert.NoError(t, err)
assert.Equal(t, len(keys), 1)
Expand All @@ -165,9 +179,13 @@ func TestCannotDecryptKeyStoreWithWrongPassword(t *testing.T) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore, err := NewFileKeystoreWithPassword(path, NewSecureString([]byte("password")))
keystore.Store("hello", []byte("world"))
keystore.Save()
keyStore, err := NewFileKeystoreWithPassword(path, NewSecureString([]byte("password")))

writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

writableKeystore.Store("hello", []byte("world"))
writableKeystore.Save()

_, err = NewFileKeystoreWithPassword(path, NewSecureString([]byte("wrongpassword")))
if assert.Error(t, err, "should fail to decrypt the keystore") {
Expand Down Expand Up @@ -199,13 +217,16 @@ func TestGetConfig(t *testing.T) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore := CreateAnExistingKeystore(path)
keyStore := CreateAnExistingKeystore(path)

writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

// Add a bit more data of different type
keystore.Store("super.nested", []byte("hello"))
keystore.Save()
writableKeystore.Store("super.nested", []byte("hello"))
writableKeystore.Save()

cfg, err := keystore.GetConfig()
cfg, err := keyStore.GetConfig()
assert.NotNil(t, cfg)
assert.NoError(t, err)

Expand Down Expand Up @@ -258,11 +279,14 @@ func createAndReadKeystoreSecret(t *testing.T, password []byte, key string, valu
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
keyStore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
assert.Nil(t, err)

keystore.Store(key, value)
keystore.Save()
writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

writableKeystore.Store(key, value)
writableKeystore.Save()

newStore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
s, _ := newStore.Retrieve(key)
Expand All @@ -274,11 +298,14 @@ func createAndReadKeystoreWithPassword(t *testing.T, password []byte) {
path := GetTemporaryKeystoreFile()
defer os.Remove(path)

keystore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
keyStore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
assert.NoError(t, err)

writableKeystore, err := AsWritableKeystore(keyStore)
assert.NoError(t, err)

keystore.Store("hello", []byte("world"))
keystore.Save()
writableKeystore.Store("hello", []byte("world"))
writableKeystore.Save()

newStore, err := NewFileKeystoreWithPassword(path, NewSecureString(password))
s, _ := newStore.Retrieve("hello")
Expand All @@ -290,14 +317,20 @@ func createAndReadKeystoreWithPassword(t *testing.T, password []byte) {
// CreateAnExistingKeystore creates a keystore with an existing key
/// `output.elasticsearch.password` with the value `secret`.
func CreateAnExistingKeystore(path string) Keystore {
keystore, err := NewFileKeystore(path)
keyStore, err := NewFileKeystore(path)
// Fail fast in the test suite
if err != nil {
panic(err)
}
keystore.Store(keyValue, secretValue)
keystore.Save()
return keystore

writableKeystore, err := AsWritableKeystore(keyStore)
if err != nil {
panic(err)
}

writableKeystore.Store(keyValue, secretValue)
writableKeystore.Save()
return keyStore
}

// GetTemporaryKeystoreFile create a temporary file on disk to save the keystore.
Expand Down
Loading

0 comments on commit 3c58ec6

Please sign in to comment.