Skip to content

Commit

Permalink
Allow a keystore to be send with functionbeat files (#10263)
Browse files Browse the repository at this point in the history
* Allow a keystore to be send with functionbeat files

Add a new interface in the keystore package named Packager, this allow a
keystore to return the raw bytes of the keys and values store in it.

This is used with core.MakeZip() to create the package that will be send
to the lambda function.

Fixes: #9009
  • Loading branch information
ph committed Jan 31, 2019
1 parent 22f530a commit db8a647
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d

- Correctly normalize Cloudformation resource name. {issue}10087[10087]
- Functionbeat can now deploy a function for Kinesis. {10116}10116[10116]
- Allow functionbeat to use the keystore. {issue}9009[9009]

==== Bugfixes

Expand Down
13 changes: 8 additions & 5 deletions libbeat/cmd/instance/beat.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"math/big"
"math/rand"
"os"
"path/filepath"
"runtime"
"strings"
"time"
Expand Down Expand Up @@ -547,10 +546,7 @@ func (b *Beat) configure(settings Settings) error {

// 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)
store, err := LoadKeystore(cfg, b.Info.Beat)
if err != nil {
return fmt.Errorf("could not initialize the keystore: %v", err)
}
Expand Down Expand Up @@ -974,3 +970,10 @@ func obfuscateConfigOpts() []ucfg.Option {
ucfg.ResolveNOOP,
}
}

// LoadKeystore returns the appropriate keystore based on the configuration.
func LoadKeystore(cfg *common.Config, name string) (keystore.Keystore, error) {
keystoreCfg, _ := cfg.Child("keystore", -1)
defaultPathConfig := paths.Resolve(paths.Data, fmt.Sprintf("%s.keystore", name))
return keystore.Factory(keystoreCfg, defaultPathConfig)
}
45 changes: 32 additions & 13 deletions libbeat/keystore/file_keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,41 +234,53 @@ func (k *FileKeystore) doSave(override bool) error {
return nil
}

func (k *FileKeystore) load() error {
k.Lock()
defer k.Unlock()

func (k *FileKeystore) loadRaw() ([]byte, error) {
f, err := os.OpenFile(k.Path, os.O_RDONLY, filePermission)
if err != nil {
if os.IsNotExist(err) {
return nil
return nil, nil
}
return err
return nil, err
}
defer f.Close()

if common.IsStrictPerms() {
if err := k.checkPermissions(k.Path); err != nil {
return err
return nil, err
}
}

raw, err := ioutil.ReadAll(f)
if err != nil {
return err
return nil, err
}

v := raw[0:len(version)]
if !bytes.Equal(v, version) {
return fmt.Errorf("keystore format doesn't match expected version: '%s' got '%s'", version, v)
return nil, fmt.Errorf("keystore format doesn't match expected version: '%s' got '%s'", version, v)
}

base64Content := raw[len(version):]
if len(base64Content) == 0 {
return fmt.Errorf("corrupt or empty keystore")
if len(raw) <= len(version) {
return nil, fmt.Errorf("corrupt or empty keystore")
}

base64Decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(base64Content))
return raw, nil
}

func (k *FileKeystore) load() error {
k.Lock()
defer k.Unlock()

raw, err := k.loadRaw()
if err != nil {
return err
}

if len(raw) == 0 {
return nil
}

base64Decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(raw[len(version):]))
plaintext, err := k.decrypt(base64Decoder)
if err != nil {
return fmt.Errorf("could not decrypt the keystore: %v", err)
Expand Down Expand Up @@ -396,6 +408,13 @@ func (k *FileKeystore) checkPermissions(f string) error {
return nil
}

// Package returns the bytes of the encrypted keystore.
func (k *FileKeystore) Package() ([]byte, error) {
k.Lock()
defer k.Unlock()
return k.loadRaw()
}

func (k *FileKeystore) hashPassword(password, salt []byte) []byte {
return pbkdf2.Key(password, salt, iterationsCount, keyLength, sha512.New)
}
5 changes: 5 additions & 0 deletions libbeat/keystore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ type Keystore interface {
Save() error
}

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

// Factory Create the right keystore with the configured options.
func Factory(cfg *common.Config, defaultPath string) (Keystore, error) {
config := defaultConfig
Expand Down
49 changes: 46 additions & 3 deletions x-pack/functionbeat/core/makezip.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@
package core

import (
"fmt"

yaml "gopkg.in/yaml.v2"

"github.com/pkg/errors"

"github.com/elastic/beats/libbeat/cfgfile"
"github.com/elastic/beats/libbeat/cmd/instance"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/keystore"
"github.com/elastic/beats/x-pack/functionbeat/config"
"github.com/elastic/beats/x-pack/functionbeat/core/bundle"
)
Expand Down Expand Up @@ -43,16 +50,52 @@ func MakeZip() ([]byte, error) {
if err != nil {
return nil, err
}

resources := []bundle.Resource{
&bundle.MemoryFile{Path: "functionbeat.yml", Raw: rawConfig, FileMode: 0766},
&bundle.LocalFile{Path: "pkg/functionbeat", FileMode: 0755},
}

rawKeystore, err := keystoreRaw()
if err != nil {
return nil, err
}

if len(rawKeystore) > 0 {
resources = append(resources, &bundle.MemoryFile{
Path: "data/functionbeat.keystore",
Raw: rawKeystore,
FileMode: 0600,
})
}

bundle := bundle.NewZipWithLimits(
packageUncompressedLimit,
packageCompressedLimit,
&bundle.MemoryFile{Path: "functionbeat.yml", Raw: rawConfig, FileMode: 0766},
&bundle.LocalFile{Path: "pkg/functionbeat", FileMode: 0755},
)
resources...)

content, err := bundle.Bytes()
if err != nil {
return nil, err
}
return content, nil
}

func keystoreRaw() ([]byte, error) {
cfg, err := cfgfile.Load("", common.NewConfig())
if err != nil {
return nil, fmt.Errorf("error loading config file: %v", err)
}

store, err := instance.LoadKeystore(cfg, "functionbeat")
if err != nil {
return nil, errors.Wrapf(err, "cannot load the keystore for packaging")
}

packager, ok := store.(keystore.Packager)
if !ok {
return nil, fmt.Errorf("the configured keystore cannot be packaged")
}

return packager.Package()
}

0 comments on commit db8a647

Please sign in to comment.