2021-01-15 02:31:02 +00:00
|
|
|
// package osquery implements a runtime for osqueryd.
|
2020-12-17 03:24:23 +00:00
|
|
|
package osquery
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-11-22 14:13:26 +00:00
|
|
|
"fmt"
|
2022-01-07 22:32:31 +00:00
|
|
|
"io"
|
2020-12-17 03:24:23 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2021-01-26 03:53:51 +00:00
|
|
|
"path/filepath"
|
2021-11-18 00:34:31 +00:00
|
|
|
"runtime"
|
2020-12-17 03:24:23 +00:00
|
|
|
"time"
|
|
|
|
|
2021-08-11 14:02:22 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/constant"
|
|
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/process"
|
2021-08-24 12:50:03 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/pkg/secure"
|
2021-01-14 02:21:25 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2020-12-17 03:24:23 +00:00
|
|
|
)
|
|
|
|
|
2021-11-18 00:34:31 +00:00
|
|
|
const (
|
|
|
|
extensionSocketName = "orbit-osquery.em"
|
|
|
|
windowsExtensionSocketPath = `\\.\pipe\orbit-osquery-extension`
|
|
|
|
)
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// Runner is a specialized runner for osquery. It is designed with Execute and
|
|
|
|
// Interrupt functions to be compatible with oklog/run.
|
2020-12-17 03:24:23 +00:00
|
|
|
type Runner struct {
|
2021-11-18 00:34:31 +00:00
|
|
|
proc *process.Process
|
|
|
|
cmd *exec.Cmd
|
|
|
|
dataPath string
|
|
|
|
cancel func()
|
2020-12-17 03:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// NewRunner creates a new osquery runner given the provided functional options.
|
2021-01-26 03:53:51 +00:00
|
|
|
func NewRunner(path string, options ...func(*Runner) error) (*Runner, error) {
|
2020-12-17 03:24:23 +00:00
|
|
|
r := &Runner{}
|
|
|
|
|
2021-01-26 03:53:51 +00:00
|
|
|
cmd := exec.Command(path)
|
2020-12-17 03:24:23 +00:00
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
r.cmd = cmd
|
|
|
|
r.proc = process.NewWithCmd(cmd)
|
|
|
|
|
2020-12-17 22:48:12 +00:00
|
|
|
for _, option := range options {
|
|
|
|
err := option(r)
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, fmt.Errorf("apply option: %w", err)
|
2020-12-17 22:48:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 03:24:23 +00:00
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// WithFlags adds additional flags to the osqueryd invocation.
|
2020-12-17 22:48:12 +00:00
|
|
|
func WithFlags(flags []string) func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
|
|
|
r.cmd.Args = append(r.cmd.Args, flags...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// WithEnv adds additional environment variables to the osqueryd invocation.
|
|
|
|
// Inputs should be in the form "KEY=VAL".
|
2020-12-18 17:36:06 +00:00
|
|
|
func WithEnv(env []string) func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
|
|
|
r.cmd.Env = append(r.cmd.Env, env...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 01:00:46 +00:00
|
|
|
// WithShell adds the -S flag to run an osqueryi shell.
|
|
|
|
func WithShell() func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
|
|
|
r.cmd.Args = append(r.cmd.Args, "-S")
|
|
|
|
r.cmd.Stdin = os.Stdin
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 03:53:51 +00:00
|
|
|
func WithDataPath(path string) func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
2021-11-18 00:34:31 +00:00
|
|
|
r.dataPath = path
|
|
|
|
|
2022-01-07 22:32:31 +00:00
|
|
|
if err := secure.MkdirAll(path, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("initialize osquery data path: %w", err)
|
2021-01-26 03:53:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r.cmd.Args = append(r.cmd.Args,
|
|
|
|
"--pidfile="+filepath.Join(path, "osquery.pid"),
|
|
|
|
"--database_path="+filepath.Join(path, "osquery.db"),
|
2021-11-18 00:34:31 +00:00
|
|
|
"--extensions_socket="+r.ExtensionSocketPath(),
|
2021-01-26 03:53:51 +00:00
|
|
|
)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 22:32:31 +00:00
|
|
|
// WithStderr sets the runner's cmd's stderr to the given writer.
|
|
|
|
func WithStderr(w io.Writer) func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
|
|
|
r.cmd.Stderr = w
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 03:53:51 +00:00
|
|
|
func WithLogPath(path string) func(*Runner) error {
|
|
|
|
return func(r *Runner) error {
|
2021-08-11 14:02:22 +00:00
|
|
|
if err := secure.MkdirAll(path, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("initialize osquery log path: %w", err)
|
2021-01-26 03:53:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
r.cmd.Args = append(r.cmd.Args,
|
|
|
|
"--logger_path="+path,
|
|
|
|
)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// Execute begins running osqueryd and returns when the process exits. The
|
|
|
|
// process may not be restarted after exit. Instead create a new one with
|
|
|
|
// NewRunner.
|
2020-12-17 03:24:23 +00:00
|
|
|
func (r *Runner) Execute() error {
|
2022-01-14 00:54:28 +00:00
|
|
|
log.Info().Str("cmd", r.cmd.String()).Msg("start osqueryd")
|
2021-01-14 02:21:25 +00:00
|
|
|
|
2021-01-13 03:00:16 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
r.cancel = cancel
|
|
|
|
|
2020-12-17 03:24:23 +00:00
|
|
|
if err := r.proc.Start(); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("start osqueryd: %w", err)
|
2020-12-17 03:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:40:33 +00:00
|
|
|
if err := r.proc.WaitOrKill(ctx, 10*time.Second); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("osqueryd exited with error: %w", err)
|
2020-12-17 03:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:40:33 +00:00
|
|
|
return nil
|
2020-12-17 03:24:23 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 02:31:02 +00:00
|
|
|
// Runner interrupts the running osquery process.
|
2020-12-17 03:24:23 +00:00
|
|
|
func (r *Runner) Interrupt(err error) {
|
2022-01-14 00:54:28 +00:00
|
|
|
log.Debug().Err(err).Msg("interrupt osquery")
|
2020-12-17 03:24:23 +00:00
|
|
|
r.cancel()
|
|
|
|
}
|
2021-11-18 00:34:31 +00:00
|
|
|
|
|
|
|
func (r *Runner) ExtensionSocketPath() string {
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
return windowsExtensionSocketPath
|
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Join(r.dataPath, extensionSocketName)
|
|
|
|
}
|