Skip to content
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

Refactor keystore interface into 3 different interfaces #16365

Merged
merged 11 commits into from
Apr 2, 2020
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)
}
urso marked this conversation as resolved.
Show resolved Hide resolved

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