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

Osquerybeat: Add install verification for osquerybeat #30388

Merged
merged 1 commit into from
Feb 15, 2022
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
2 changes: 1 addition & 1 deletion x-pack/elastic-agent/pkg/agent/program/supported.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions x-pack/elastic-agent/spec/osquerybeat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ artifact: beats/osquerybeat
action_input_types:
- osquery

check_install:
- exec_file:
path: "osquerybeat"
args:
- "verify"
timeout: 10

rules:
- fix_stream: {}
- inject_index:
Expand Down
24 changes: 24 additions & 0 deletions x-pack/osquerybeat/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ import (
cmd "github.com/elastic/beats/v7/libbeat/cmd"
"github.com/elastic/beats/v7/libbeat/cmd/instance"
"github.com/elastic/beats/v7/libbeat/common"
"github.com/elastic/beats/v7/libbeat/common/cli"
"github.com/elastic/beats/v7/libbeat/ecs"
"github.com/elastic/beats/v7/libbeat/logp"
"github.com/elastic/beats/v7/libbeat/publisher/processing"

"github.com/spf13/cobra"

_ "github.com/elastic/beats/v7/x-pack/libbeat/include"
"github.com/elastic/beats/v7/x-pack/osquerybeat/beater"
"github.com/elastic/beats/v7/x-pack/osquerybeat/internal/install"
)

// Name of this beat
Expand All @@ -37,5 +42,24 @@ func Osquerybeat() *cmd.BeatsRootCmd {
}
command := cmd.GenRootCmdWithSettings(beater.New, settings)

// Add verify command
command.AddCommand(genVerifyCmd(settings))

return command
}

func genVerifyCmd(settings instance.Settings) *cobra.Command {
return &cobra.Command{
Use: "verify",
Short: "Verify installation",
Run: cli.RunWith(
func(_ *cobra.Command, args []string) error {
log := logp.NewLogger("osquerybeat")
err := install.VerifyWithExecutableDirectory(log)
if err != nil {
return err
}
return nil
}),
}
}
90 changes: 90 additions & 0 deletions x-pack/osquerybeat/internal/install/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package install

import (
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/elastic/beats/v7/libbeat/logp"
"github.com/elastic/beats/v7/x-pack/osquerybeat/internal/distro"
"github.com/elastic/beats/v7/x-pack/osquerybeat/internal/fileutil"
"github.com/elastic/beats/v7/x-pack/osquerybeat/internal/osqd"
)

func execDir() (exedir string, err error) {
exefp, err := os.Executable()
if err != nil {
return exedir, nil
}
exedir = filepath.Dir(exefp)
return exedir, nil
}

// VerifyWithExecutableDirectory verifies installation with the current executable directory
func VerifyWithExecutableDirectory(log *logp.Logger) error {
exedir, err := execDir()
if err != nil {
return err
}

return Verify(runtime.GOOS, exedir, log)
}

// Verify verifies installation in the given executable directory
func Verify(goos, dir string, log *logp.Logger) error {
log.Infof("Install verification for %s", dir)
// For darwin expect installer PKG or unpackages osqueryd
if goos == "darwin" {
pkgFile := filepath.Join(dir, distro.OsquerydDistroPlatformFilename(goos))
pkgExists, err := fileExistsLogged(log, pkgFile)
if err != nil {
return err
}
if pkgExists {
return nil
}
}

// Verify osqueryd or osqueryd.exe exists
osqFile := osqd.QsquerydPathForPlatform(goos, dir)
osqExists, err := fileExistsLogged(log, osqFile)
if err != nil {
return err
}
if !osqExists {
return fmt.Errorf("%w: %v", os.ErrNotExist, osqFile)
}

// Verify extension file exists
extFileName := "osquery-extension.ext"
if goos == "windows" {
extFileName = "osquery-extension.exe"
}
extFile := filepath.Join(dir, extFileName)
osqExtExists, err := fileExistsLogged(log, extFile)
if err != nil {
return err
}

if !osqExtExists {
return fmt.Errorf("%w: %v", os.ErrNotExist, extFileName)
}
return nil
}

func fileExistsLogged(log *logp.Logger, fp string) (bool, error) {
log.Infof("Check if file exists %s:", fp)
fpExists, err := fileutil.FileExists(fp)
if err != nil {
log.Infof("File exists check failed for %s", fp)
}
if !fpExists {
log.Infof("File %s doesn't exists", fp)
}
return fpExists, err
}
148 changes: 148 additions & 0 deletions x-pack/osquerybeat/internal/install/verify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package install

import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/elastic/beats/v7/libbeat/logp"
)

// setupFiles helper function that creates subdirectory with a given set of files
// The verification currently checks for the file presence only
func setupFiles(testdataBaseDir string, files []string) (string, error) {
testdir, err := ioutil.TempDir(testdataBaseDir, "")
if err != nil {
return "", err
}

for _, f := range files {
fp := filepath.Join(testdir, f)

dir := filepath.Dir(fp)
err = os.MkdirAll(dir, 0750)
if err != nil {
return "", err
}

err = ioutil.WriteFile(fp, nil, 0750)
if err != nil {
return "", err
}
}

return testdir, nil
}

func TestVerify(t *testing.T) {
log := logp.NewLogger("verify_test")
tests := []struct {
name string
goos string
files []string
err error
}{
{
name: "darwin no files",
goos: "darwin",
err: os.ErrNotExist,
},
{
name: "linux no files",
goos: "linux",
err: os.ErrNotExist,
},
{
name: "windows no files",
goos: "windows",
err: os.ErrNotExist,
},
{
name: "darwin extension file missing",
goos: "darwin",
files: []string{"osquery.app/Contents/MacOS/osqueryd"},
err: os.ErrNotExist,
},
{
name: "darwin osqueryd missing",
goos: "darwin",
files: []string{"osquery-extension.ext"},
err: os.ErrNotExist,
},
{
name: "darwin valid install",
goos: "darwin",
files: []string{"osquery.app/Contents/MacOS/osqueryd", "osquery-extension.ext"},
},
{
name: "linux extension file missing",
goos: "linux",
files: []string{"osqueryd"},
err: os.ErrNotExist,
},
{
name: "linux osqueryd missing",
goos: "linux",
files: []string{"osquery-extension.ext"},
err: os.ErrNotExist,
},
{
name: "linux valid install",
goos: "linux",
files: []string{"osqueryd", "osquery-extension.ext"},
},
{
name: "windows extension file missing",
goos: "windows",
files: []string{"osqueryd.exe"},
err: os.ErrNotExist,
},
{
name: "windows osqueryd missing",
goos: "windows",
files: []string{"osquery-extension.exe"},
err: os.ErrNotExist,
},
{
name: "windows valid install",
goos: "windows",
files: []string{"osqueryd.exe", "osquery-extension.exe"},
},
}

// Setup test data
testdataBaseDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
dir, err := setupFiles(testdataBaseDir, tc.files)
if err != nil {
t.Fatal(err)
}

err = Verify(tc.goos, dir, log)
// check for matching error if tc exppect error
if err != nil {
if tc.err != nil {
if !errors.Is(err, tc.err) {
t.Fatalf("want error: %v, got: %v", tc.err, err)
}
} else {
t.Fatalf("want error: nil, got: %v", err)
}
} else if tc.err != nil {
t.Fatalf("want error: %v, got: nil", tc.err)
}
})
}

}
18 changes: 15 additions & 3 deletions x-pack/osquerybeat/internal/osqd/osqueryd.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,11 +417,23 @@ func (q *OSQueryD) isVerbose() bool {
}

func osquerydPath(dir string) string {
if runtime.GOOS == "darwin" {
return filepath.Join(dir, osqueryDarwinAppBundlePath, osquerydFilename())
return QsquerydPathForPlatform(runtime.GOOS, dir)
}

// QsquerydPathForPlatform returns the full path to osqueryd binary for platform
func QsquerydPathForPlatform(platform, dir string) string {
if platform == "darwin" {
return filepath.Join(dir, osqueryDarwinAppBundlePath, osquerydFilename(platform))

}
return filepath.Join(dir, osquerydFilename(platform))
}

func osquerydFilename(platform string) string {
if platform == "windows" {
return osqueryDName + ".exe"
}
return filepath.Join(dir, osquerydFilename())
return osqueryDName
}

func osqueryExtensionPath(dir string) string {
Expand Down
4 changes: 0 additions & 4 deletions x-pack/osquerybeat/internal/osqd/osqueryd_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,3 @@ func killProcessGroup(cmd *exec.Cmd) error {
err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
return errors.Wrapf(err, "kill process group %d", cmd.Process.Pid)
}

func osquerydFilename() string {
return osqueryDName
}
4 changes: 0 additions & 4 deletions x-pack/osquerybeat/internal/osqd/osqueryd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,3 @@ func killProcessGroup(cmd *exec.Cmd) error {
exec.Command("taskkill", "/F", "/T", "/PID", fmt.Sprint(cmd.Process.Pid)).Run()
return nil
}

func osquerydFilename() string {
return osqueryDName + ".exe"
}