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

Allow user to locally obfuscate secret in a keystore #5687

Merged
merged 2 commits into from
Dec 22, 2017
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
coverage.out
.python-version
beat.db
*.keystore

# Editor swap files
*.swp
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ https://github.com/elastic/beats/compare/v6.0.0-beta2...master[Check the HEAD di
- Addition and update of the following libraries: pbkdf2, terminal, windows, unix {pull}5735[5735]
- Update the command line library cobra and add support for zsh completion {pull}5761[5761]
- The log format may differ due to logging library changes. {pull}5901[5901]
- Adding a local keystore to allow user to obfuscate password {pull}5687[5687]

*Auditbeat*

Expand Down
5 changes: 5 additions & 0 deletions filebeat/tests/system/config/filebeat.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,8 @@ output.file:
path:
data: {{path_data}}
{% endif %}

{% if keystore_path %}
#================================ keystore =====================================
keystore.path: {{keystore_path}}
{% endif %}
74 changes: 74 additions & 0 deletions filebeat/tests/system/test_keystore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import os
from os import path

from filebeat import BaseTest
from beat.beat import Proc


class TestKeystore(BaseTest):
"""
Test Keystore variable replacement
"""

def setUp(self):
super(BaseTest, self).setUp()
self.keystore_path = self.working_dir + "/data/keystore"

def test_keystore_with_present_key(self):
"""
Test that we correctly do string replacement with values from the keystore
"""

key = "elasticsearch_host"
secret = "myeleasticsearchsecrethost"

self.render_config_template(keystore_path=self.keystore_path, elasticsearch={
'host': "${%s}:9200" % key
})

exit_code = self.run_beat(extra_args=["keystore", "create"])
assert exit_code == 0

self.add_secret(key, secret, True)
proc = self.start_beat()
self.wait_until(lambda: self.log_contains("myeleasticsearchsecrethost"))
assert self.log_contains(secret)
proc.kill_and_wait()

def test_keystore_with_key_not_present(self):
"""
Test that we return the template key when the key doesn't exist
"""
key = "do_not_exist_elasticsearch_host"

self.render_config_template(keystore_path=self.keystore_path, elasticsearch={
'host': "${%s}:9200" % key
})

exit_code = self.run_beat()
assert self.log_contains(
"missing field accessing 'output.elasticsearch.hosts.0'")
assert exit_code == 1

def add_secret(self, key, value="hello world\n", force=False):
"""
Add new secret using the --stdin option
"""
args = [self.test_binary,
"-systemTest",
"-test.coverprofile",
os.path.join(self.working_dir, "coverage.cov"),
"-c", os.path.join(self.working_dir, self.beat_name + ".yml"),
"-e", "-v", "-d", "*",
"keystore", "add", key, "--stdin",
]

if force:
args.append("--force")

proc = Proc(args, os.path.join(self.working_dir, self.beat_name + ".log"))

os.write(proc.stdin_write, value)
os.close(proc.stdin_write)

return proc.start().wait()
22 changes: 22 additions & 0 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"math/big"
"math/rand"
"os"
"path/filepath"
"runtime"
"strings"
"time"
Expand All @@ -23,6 +24,7 @@ import (
"github.com/elastic/beats/libbeat/common/cfgwarn"
"github.com/elastic/beats/libbeat/common/file"
"github.com/elastic/beats/libbeat/dashboards"
"github.com/elastic/beats/libbeat/keystore"
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/logp/configure"
"github.com/elastic/beats/libbeat/monitoring"
Expand Down Expand Up @@ -59,6 +61,7 @@ type Beat struct {

Config beatConfig
RawConfig *common.Config // Raw config that can be unpacked to get Beat specific config data.
keystore keystore.Keystore
}

type beatConfig struct {
Expand All @@ -75,6 +78,7 @@ type beatConfig struct {
Path paths.Path `config:"path"`
Logging *common.Config `config:"logging"`
MetricLogging *common.Config `config:"logging.metrics"`
Keystore *common.Config `config:"keystore"`

// output/publishing related configurations
Pipeline pipeline.Config `config:",inline"`
Expand Down Expand Up @@ -192,6 +196,11 @@ func (b *Beat) BeatConfig() (*common.Config, error) {
return common.NewConfig(), nil
}

// Keystore return the configured keystore for this beat
func (b *Beat) Keystore() keystore.Keystore {
return b.keystore
}

// create and return the beater, this method also initializes all needed items,
// including template registering, publisher, xpack monitoring
func (b *Beat) createBeater(bt beat.Creator) (beat.Beater, error) {
Expand Down Expand Up @@ -405,6 +414,19 @@ func (b *Beat) configure() error {
return fmt.Errorf("error loading config file: %v", err)
}

// We have to initialize the keystore before any unpack or merging the cloud
// options.
keystoreCfg, _ := cfg.Child("keystore", -1)
defaultPathConfig, _ := cfg.String("path.config", -1)
defaultPathConfig = filepath.Join(defaultPathConfig, fmt.Sprintf("%s.keystore", b.Info.Beat))
store, err := keystore.Factory(keystoreCfg, defaultPathConfig)
if err != nil {
return fmt.Errorf("could not initialize the keystore: %v", err)
}

// TODO: Allow the options to be more flexible for dynamic changes
common.OverwriteConfigOpts(keystore.ConfigOpts(store))
b.keystore = store
err = cloudid.OverwriteSettings(cfg)
if err != nil {
return err
Expand Down
Loading