From 7823bbbaba1bb7ddb7acefa3d5527502804d3e5e Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Fri, 7 Jan 2022 19:32:31 -0300 Subject: [PATCH] Fix orbit and osqueryd logging on Windows (#3521) * Fix orbit and osqueryd logging on Windows * Add functionality to test the change and use systemprofile app data * Add centos syslog to README.md * Add wait on orbit for osquery extension socket to exist (#3571) * Wait for osquery extension socket to exist * Amend changes * Fix lint * Restore timeout --- ...sue-3100-add-windows-orbit-osquery-logging | 1 + ...e-3522-increase-extension-register-timeout | 1 + orbit/README.md | 27 ++++++++++++ orbit/cmd/orbit/orbit.go | 43 ++++++++++++++----- orbit/cmd/orbit/shell.go | 1 - orbit/pkg/osquery/osquery.go | 11 ++++- orbit/pkg/packaging/macos.go | 9 ---- orbit/pkg/packaging/packaging.go | 4 ++ orbit/pkg/packaging/windows_templates.go | 2 +- orbit/pkg/table/extension.go | 32 ++++++++++++++ orbit/pkg/update/update.go | 43 +++++++++++++++++++ 11 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 changes/issue-3100-add-windows-orbit-osquery-logging create mode 100644 changes/issue-3522-increase-extension-register-timeout diff --git a/changes/issue-3100-add-windows-orbit-osquery-logging b/changes/issue-3100-add-windows-orbit-osquery-logging new file mode 100644 index 0000000000..f7b28cf3b5 --- /dev/null +++ b/changes/issue-3100-add-windows-orbit-osquery-logging @@ -0,0 +1 @@ +* Fix logging of orbit and osqueryd on Windows. diff --git a/changes/issue-3522-increase-extension-register-timeout b/changes/issue-3522-increase-extension-register-timeout new file mode 100644 index 0000000000..8c919972ea --- /dev/null +++ b/changes/issue-3522-increase-extension-register-timeout @@ -0,0 +1 @@ +* Add wait on orbit for osquery extension socket. diff --git a/orbit/README.md b/orbit/README.md index 12e4209be3..08ec2dcff2 100644 --- a/orbit/README.md +++ b/orbit/README.md @@ -167,6 +167,33 @@ This process may take several minutes to complete as the Notarization process co After successful notarization, the generated "ticket" is automatically stapled to the package. +#### Orbit Development + +For ease of development of Orbit, `fleetctl package` allows the generation of a package with a +custom orbit executable using the `FLEETCTL_ORBIT_DEV_BUILD_PATH` environment variable: +```sh +FLEETCTL_ORBIT_DEV_BUILD_PATH=$(pwd)/orbit.exe ./build/fleetctl package --type=msi --fleet-url=https://localhost:8080 --enroll-secret=the_secret_value +Generating your osquery installer... +2022/01/03 20:31:10 root pinning is not supported in Spec 1.0.19 +WARNING: You are attempting to override orbit with a dev build. +Press Enter to continue, or Control-c to exit. +[...] +``` + +### Troubleshooting + +#### Logs + +Orbit captures and streams osqueryd's stdout/stderr into its own stdout/stderr output. +Following are the destination of logs for each platform (to access such locations the user will need administrative permissions on the host): +- Linux: Orbit and osqueryd stdout/stderr output is sent to syslog (`/var/log/syslog` on Debian systems and `/var/log/messages` on CentOS). +- macOS: `/private/var/log/orbit/orbit.std{out|err}.log`. +- Windows: `C:\Windows\system32\config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.lg` (the log file is rotated). + +#### Debug + +You can use the `--debug` option in `fleetctl package` to generate installers in "debug mode". Such mode increases the verbosity of logging for orbit and osqueryd (log DEBUG level). + ### Uninstall #### Windows diff --git a/orbit/cmd/orbit/orbit.go b/orbit/cmd/orbit/orbit.go index 53fd93490c..49477c4c32 100644 --- a/orbit/cmd/orbit/orbit.go +++ b/orbit/cmd/orbit/orbit.go @@ -4,11 +4,13 @@ import ( "context" "errors" "fmt" + "io" "io/fs" "io/ioutil" "net/url" "os" "path/filepath" + "runtime" "strings" "time" @@ -25,6 +27,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" + "gopkg.in/natefinch/lumberjack.v2" ) var ( @@ -112,17 +115,33 @@ func main() { return nil } - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true}) - if logfile := c.String("log-file"); logfile != "" { - f, err := secure.OpenFile(logfile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) - if err != nil { - return fmt.Errorf("open logfile: %w", err) + var logFile io.Writer + if logf := c.String("log-file"); logf != "" { + if logDir := filepath.Dir(logf); logDir != "." { + if err := secure.MkdirAll(logDir, constant.DefaultDirMode); err != nil { + panic(err) + } } - log.Logger = log.Output(zerolog.MultiLevelWriter( - zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true}, - zerolog.ConsoleWriter{Out: f, TimeFormat: time.RFC3339Nano, NoColor: true}, - )) + logFile = &lumberjack.Logger{ + Filename: logf, + MaxSize: 25, // megabytes + MaxBackups: 3, + MaxAge: 28, // days + } + if runtime.GOOS == "windows" { + // On Windows, Orbit runs as a "Windows Service", which fails to write to os.Stderr with + // "write /dev/stderr: The handle is invalid" (see #3100). Thus, we log to the logFile only. + log.Logger = log.Output(zerolog.ConsoleWriter{Out: logFile, TimeFormat: time.RFC3339Nano, NoColor: true}) + } else { + log.Logger = log.Output(zerolog.MultiLevelWriter( + zerolog.ConsoleWriter{Out: logFile, TimeFormat: time.RFC3339Nano, NoColor: true}, + zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true}, + )) + } + } else { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339Nano, NoColor: true}) } + zerolog.SetGlobalLevel(zerolog.InfoLevel) if c.Bool("debug") { @@ -210,6 +229,11 @@ func main() { var options []func(*osquery.Runner) error options = append(options, osquery.WithDataPath(c.String("root-dir"))) + if logFile != nil { + // If set, redirect osqueryd's stderr to the logFile. + options = append(options, osquery.WithStderr(logFile)) + } + fleetURL := c.String("fleet-url") if !strings.HasPrefix(fleetURL, "http") { fleetURL = "https://" + fleetURL @@ -342,7 +366,6 @@ func main() { r, _ := osquery.NewRunner(osquerydPath, options...) g.Add(r.Execute, r.Interrupt) - // Extension tables not yet supported on Windows. ext := table.NewRunner(r.ExtensionSocketPath()) g.Add(ext.Execute, ext.Interrupt) diff --git a/orbit/cmd/orbit/shell.go b/orbit/cmd/orbit/shell.go index f1fc5c9fbf..e0962913b2 100644 --- a/orbit/cmd/orbit/shell.go +++ b/orbit/cmd/orbit/shell.go @@ -79,7 +79,6 @@ var shellCommand = &cli.Command{ ) g.Add(r.Execute, r.Interrupt) - // Extension tables not yet supported on Windows. ext := table.NewRunner(r.ExtensionSocketPath()) g.Add(ext.Execute, ext.Interrupt) diff --git a/orbit/pkg/osquery/osquery.go b/orbit/pkg/osquery/osquery.go index 46547b155c..a4b80f8821 100644 --- a/orbit/pkg/osquery/osquery.go +++ b/orbit/pkg/osquery/osquery.go @@ -4,6 +4,7 @@ package osquery import ( "context" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -81,7 +82,7 @@ func WithDataPath(path string) func(*Runner) error { return func(r *Runner) error { r.dataPath = path - if err := secure.MkdirAll(filepath.Join(path, "logs"), constant.DefaultDirMode); err != nil { + if err := secure.MkdirAll(path, constant.DefaultDirMode); err != nil { return fmt.Errorf("initialize osquery data path: %w", err) } @@ -94,6 +95,14 @@ func WithDataPath(path string) func(*Runner) error { } } +// 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 + } +} + func WithLogPath(path string) func(*Runner) error { return func(r *Runner) error { if err := secure.MkdirAll(path, constant.DefaultDirMode); err != nil { diff --git a/orbit/pkg/packaging/macos.go b/orbit/pkg/packaging/macos.go index 024310c679..162361d294 100644 --- a/orbit/pkg/packaging/macos.go +++ b/orbit/pkg/packaging/macos.go @@ -85,15 +85,6 @@ func BuildPkg(opt Options) (string, error) { } } - // TODO gate behind a flag and allow copying a local orbit - // if err := file.Copy( - // "./orbit", - // filepath.Join(orbitRoot, "bin", "orbit", "macos", "current", "orbit"), - // 0755, - // ); err != nil { - // return errors.Wrap(err, "write orbit") - // } - // Build package if err := xarBom(opt, tmpDir); err != nil { diff --git a/orbit/pkg/packaging/packaging.go b/orbit/pkg/packaging/packaging.go index a472d4fe51..c9409db461 100644 --- a/orbit/pkg/packaging/packaging.go +++ b/orbit/pkg/packaging/packaging.go @@ -94,6 +94,10 @@ func InitializeUpdates(updateOpt update.Options) error { } log.Debug().Str("path", orbitPath).Msg("got orbit") + if devBuildPath := os.Getenv("FLEETCTL_ORBIT_DEV_BUILD_PATH"); devBuildPath != "" { + updater.CopyDevBuild("orbit", updateOpt.OrbitChannel, devBuildPath) + } + return nil } diff --git a/orbit/pkg/packaging/windows_templates.go b/orbit/pkg/packaging/windows_templates.go index 3cdd9872e5..fd246a3a14 100644 --- a/orbit/pkg/packaging/windows_templates.go +++ b/orbit/pkg/packaging/windows_templates.go @@ -52,7 +52,7 @@ var windowsWixTemplate = template.Must(template.New("").Option("missingkey=error ErrorControl="ignore" Start="auto" Type="ownProcess" - Arguments='--root-dir "[ORBITROOT]." --log-file "[ORBITROOT]orbit-log.txt" {{ if .FleetURL }}--fleet-url "{{ .FleetURL }}"{{ end }} {{ if .FleetCertificate }}--fleet-certificate "[ORBITROOT]fleet.pem"{{ end }} {{ if .EnrollSecret }}--enroll-secret-path "[ORBITROOT]secret.txt"{{ end }} {{if .Insecure }}--insecure{{ end }} {{ if .UpdateURL }}--update-url "{{ .UpdateURL }}" {{ end }} --orbit-channel "{{ .OrbitChannel }}" --osqueryd-channel "{{ .OsquerydChannel }}"' + Arguments='--root-dir "[ORBITROOT]." --log-file "[System64Folder]config\systemprofile\AppData\Local\FleetDM\Orbit\Logs\orbit-osquery.log" {{ if .FleetURL }}--fleet-url "{{ .FleetURL }}"{{ end }} {{ if .FleetCertificate }}--fleet-certificate "[ORBITROOT]fleet.pem"{{ end }} {{ if .EnrollSecret }}--enroll-secret-path "[ORBITROOT]secret.txt"{{ end }} {{if .Insecure }}--insecure{{ end }} {{ if .Debug }}--debug{{ end }} {{ if .UpdateURL }}--update-url "{{ .UpdateURL }}" {{ end }} --orbit-channel "{{ .OrbitChannel }}" --osqueryd-channel "{{ .OsquerydChannel }}"' >