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

Cherry-pick #16365 to 7.x: Refactor keystore interface into 3 different interfaces #17435

Merged
merged 1 commit into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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