diff --git a/cmd/package/package.go b/cmd/package/package.go
index 355a0e274f..945334be17 100644
--- a/cmd/package/package.go
+++ b/cmd/package/package.go
@@ -55,6 +55,12 @@ func main() {
Usage: "Disable TLS certificate verification",
Destination: &opt.Insecure,
},
+ &cli.BoolFlag{
+ Name: "service",
+ Usage: "Install orbit/osquery with a persistence service (launchd, systemd, etc.)",
+ Value: true,
+ Destination: &opt.StartService,
+ },
&cli.BoolFlag{
Name: "debug",
Usage: "Enable debug logging",
@@ -68,8 +74,6 @@ func main() {
}
app.Action = func(c *cli.Context) error {
if opt.FleetURL != "" || opt.EnrollSecret != "" {
- opt.StartService = true
-
if opt.FleetURL == "" || opt.EnrollSecret == "" {
return errors.New("--enroll-secret and --fleet-url must be provided together")
}
diff --git a/pkg/packaging/deb.go b/pkg/packaging/deb.go
index 49e43466dc..13b09bc0c5 100644
--- a/pkg/packaging/deb.go
+++ b/pkg/packaging/deb.go
@@ -36,21 +36,6 @@ func BuildDeb(opt Options) error {
return errors.Wrap(err, "create orbit dir")
}
- // Write files
-
- if err := writeSystemdUnit(opt, filesystemRoot); err != nil {
- return errors.Wrap(err, "write systemd unit")
- }
-
- if err := writeEnvFile(opt, filesystemRoot); err != nil {
- return errors.Wrap(err, "write env file")
- }
-
- postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
- if err := writePostInstall(opt, postInstallPath); err != nil {
- return errors.Wrap(err, "write postinstall script")
- }
-
// Initialize autoupdate metadata
localStore, err := filestore.New(filepath.Join(orbitRoot, "tuf-metadata.json"))
@@ -76,6 +61,21 @@ func BuildDeb(opt Options) error {
}
log.Debug().Str("path", osquerydPath).Msg("got osqueryd")
+ // Write files
+
+ if err := writeSystemdUnit(opt, filesystemRoot); err != nil {
+ return errors.Wrap(err, "write systemd unit")
+ }
+
+ if err := writeEnvFile(opt, filesystemRoot); err != nil {
+ return errors.Wrap(err, "write env file")
+ }
+
+ postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
+ if err := writePostInstall(opt, postInstallPath); err != nil {
+ return errors.Wrap(err, "write postinstall script")
+ }
+
// Pick up all file contents
contents := files.Contents{
diff --git a/pkg/packaging/macos.go b/pkg/packaging/macos.go
index 294e9e3ccc..9c458e74e0 100644
--- a/pkg/packaging/macos.go
+++ b/pkg/packaging/macos.go
@@ -27,34 +27,18 @@ func BuildPkg(opt Options) error {
if err != nil {
return errors.Wrap(err, "failed to create temp dir")
}
- // TODO reenable
- //defer os.RemoveAll(tmpDir)
+ defer os.RemoveAll(tmpDir)
log.Debug().Str("path", tmpDir).Msg("created temp dir")
filesystemRoot := filepath.Join(tmpDir, "root")
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create root dir")
}
- // if err := os.MkdirAll(
- // filepath.Join(filesystemRoot, "Resources", "en.lproj"),
- // constant.DefaultDirMode,
- // ); err != nil {
- // return errors.Wrap(err, "create resources dir")
- // }
orbitRoot := filepath.Join(filesystemRoot, "var", "lib", "fleet", "orbit")
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create orbit dir")
}
- // Write files
-
- if err := writePackageInfo(opt, tmpDir); err != nil {
- return errors.Wrap(err, "write PackageInfo")
- }
- if err := writeDistribution(opt, tmpDir); err != nil {
- return errors.Wrap(err, "write Distribution")
- }
-
// Initialize autoupdate metadata
localStore, err := filestore.New(filepath.Join(orbitRoot, "tuf-metadata.json"))
@@ -80,6 +64,30 @@ func BuildPkg(opt Options) error {
}
log.Debug().Str("path", osquerydPath).Msg("got osqueryd")
+ // Write files
+
+ if err := writePackageInfo(opt, tmpDir); err != nil {
+ return errors.Wrap(err, "write PackageInfo")
+ }
+ if err := writeDistribution(opt, tmpDir); err != nil {
+ return errors.Wrap(err, "write Distribution")
+ }
+ if err := writeScripts(opt, tmpDir); err != nil {
+ return errors.Wrap(err, "write postinstall")
+ }
+ if opt.StartService {
+ if err := writeLaunchd(opt, filesystemRoot); err != nil {
+ return errors.Wrap(err, "write launchd")
+ }
+ }
+ if err := copyFile(
+ "./orbit",
+ filepath.Join(filesystemRoot, "var", "lib", "fleet", "orbit", "orbit"),
+ 0755,
+ ); err != nil {
+ return errors.Wrap(err, "write orbit")
+ }
+
// Build package
if err := xarBom(opt, tmpDir); err != nil {
return errors.Wrap(err, "build pkg")
@@ -95,6 +103,7 @@ func BuildPkg(opt Options) error {
}
func writePackageInfo(opt Options, rootPath string) error {
+ // PackageInfo is metadata for the pkg
path := filepath.Join(rootPath, "flat", "base.pkg", "PackageInfo")
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "mkdir")
@@ -112,7 +121,46 @@ func writePackageInfo(opt Options, rootPath string) error {
return nil
}
+func writeScripts(opt Options, rootPath string) error {
+ // Postinstall script
+ path := filepath.Join(rootPath, "scripts", "postinstall")
+ if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
+ return errors.Wrap(err, "mkdir")
+ }
+
+ var contents bytes.Buffer
+ if err := macosPostinstallTemplate.Execute(&contents, opt); err != nil {
+ return errors.Wrap(err, "execute template")
+ }
+
+ if err := ioutil.WriteFile(path, contents.Bytes(), 0744); err != nil {
+ return errors.Wrap(err, "write file")
+ }
+
+ return nil
+}
+
+func writeLaunchd(opt Options, rootPath string) error {
+ // launchd is the service mechanism on macOS
+ path := filepath.Join(rootPath, "Library", "LaunchDaemons", "com.fleetdm.orbit.plist")
+ if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
+ return errors.Wrap(err, "mkdir")
+ }
+
+ var contents bytes.Buffer
+ if err := macosLaunchdTemplate.Execute(&contents, opt); err != nil {
+ return errors.Wrap(err, "execute template")
+ }
+
+ if err := ioutil.WriteFile(path, contents.Bytes(), 0644); err != nil {
+ return errors.Wrap(err, "write file")
+ }
+
+ return nil
+}
+
func writeDistribution(opt Options, rootPath string) error {
+ // Distribution file is metadata for the pkg
path := filepath.Join(rootPath, "flat", "Distribution")
if err := os.MkdirAll(filepath.Dir(path), constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "mkdir")
@@ -136,52 +184,18 @@ func xarBom(opt Options, rootPath string) error {
// Adapted from BSD licensed
// https://github.com/go-flutter-desktop/hover/blob/v0.46.2/cmd/packaging/darwin-pkg.go
- // Copy payload
- payload, err := os.OpenFile(filepath.Join(rootPath, "flat", "base.pkg", "Payload"), os.O_RDWR|os.O_CREATE, 0755)
- if err != nil {
- return errors.Wrap(err, "open payload")
+ // Copy payload/scripts
+ if err := cpio(
+ filepath.Join(rootPath, "root"),
+ filepath.Join(rootPath, "flat", "base.pkg", "Payload"),
+ ); err != nil {
+ return errors.Wrap(err, "cpio Payload")
}
-
- cmdFind := exec.Command("find", ".")
- cmdFind.Dir = filepath.Join(rootPath, "root")
- cmdCpio := exec.Command("cpio", "-o", "--format", "odc", "-R", "0:80")
- cmdCpio.Dir = filepath.Join(rootPath, "root")
- cmdGzip := exec.Command("gzip", "-c")
-
- // Pipes like this: find | cpio | gzip > Payload
- cmdCpio.Stdin, err = cmdFind.StdoutPipe()
- if err != nil {
- return errors.Wrap(err, "pipe cpio")
- }
- cmdGzip.Stdin, err = cmdCpio.StdoutPipe()
- if err != nil {
- return errors.Wrap(err, "pipe gzip")
- }
- cmdGzip.Stdout = payload
-
- err = cmdGzip.Start()
- if err != nil {
- return errors.Wrap(err, "start gzip")
- }
- err = cmdCpio.Start()
- if err != nil {
- return errors.Wrap(err, "start cpio")
- }
- err = cmdFind.Run()
- if err != nil {
- return errors.Wrap(err, "run find")
- }
- err = cmdCpio.Wait()
- if err != nil {
- return errors.Wrap(err, "wait cpio")
- }
- err = cmdGzip.Wait()
- if err != nil {
- return errors.Wrap(err, "wait gzip")
- }
- err = payload.Close()
- if err != nil {
- return errors.Wrap(err, "close payload")
+ if err := cpio(
+ filepath.Join(rootPath, "scripts"),
+ filepath.Join(rootPath, "flat", "base.pkg", "Scripts"),
+ ); err != nil {
+ return errors.Wrap(err, "cpio Scripts")
}
// Make bom
@@ -224,3 +238,56 @@ func xarBom(opt Options, rootPath string) error {
return nil
}
+
+func cpio(srcPath, dstPath string) error {
+ // This is the compression routine that is expected for pkg files.
+ dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE, 0755)
+ if err != nil {
+ return errors.Wrap(err, "open dst")
+ }
+ defer dst.Close()
+
+ cmdFind := exec.Command("find", ".")
+ cmdFind.Dir = srcPath
+ cmdCpio := exec.Command("cpio", "-o", "--format", "odc", "-R", "0:80")
+ cmdCpio.Dir = srcPath
+ cmdGzip := exec.Command("gzip", "-c")
+
+ // Pipes like this: find | cpio | gzip > dstPath
+ cmdCpio.Stdin, err = cmdFind.StdoutPipe()
+ if err != nil {
+ return errors.Wrap(err, "pipe cpio")
+ }
+ cmdGzip.Stdin, err = cmdCpio.StdoutPipe()
+ if err != nil {
+ return errors.Wrap(err, "pipe gzip")
+ }
+ cmdGzip.Stdout = dst
+
+ err = cmdGzip.Start()
+ if err != nil {
+ return errors.Wrap(err, "start gzip")
+ }
+ err = cmdCpio.Start()
+ if err != nil {
+ return errors.Wrap(err, "start cpio")
+ }
+ err = cmdFind.Run()
+ if err != nil {
+ return errors.Wrap(err, "run find")
+ }
+ err = cmdCpio.Wait()
+ if err != nil {
+ return errors.Wrap(err, "wait cpio")
+ }
+ err = cmdGzip.Wait()
+ if err != nil {
+ return errors.Wrap(err, "wait gzip")
+ }
+ err = dst.Close()
+ if err != nil {
+ return errors.Wrap(err, "close dst")
+ }
+
+ return nil
+}
diff --git a/pkg/packaging/macos_templates.go b/pkg/packaging/macos_templates.go
index 415749e115..c70b7fe8f3 100644
--- a/pkg/packaging/macos_templates.go
+++ b/pkg/packaging/macos_templates.go
@@ -6,8 +6,11 @@ import "text/template"
// http://s.sudre.free.fr/Stuff/Ivanhoe/FLAT.html
var macosPackageInfoTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
`
-
-
+
+
+
+
+
`))
@@ -26,3 +29,48 @@ var macosDistributionTemplate = template.Must(template.New("").Option("missingke
#base.pkg
`))
+
+var macosPostinstallTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
+ `#!/bin/bash
+
+ln -sf /var/lib/fleet/orbit/orbit /usr/local/bin/orbit
+
+{{ if .StartService -}}
+launchctl stop com.fleetdm.orbit
+
+sleep 3
+
+launchctl unload /Library/LaunchDaemons/com.fleetdm.orbit.plist
+launchctl load /Library/LaunchDaemons/com.fleetdm.orbit.plist
+{{- end }}
+`))
+
+// TODO set Nice?
+var macosLaunchdTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
+ `
+
+
+
+ Label
+ com.fleetdm.orbit
+ ProgramArguments
+
+ /var/lib/fleet/orbit/orbit
+
+ StandardOutPath
+ /var/log/orbit/orbit.stdout.log
+ StandardErrorPath
+ /var/log/orbit/orbit.stderr.log
+ EnvironmentVariables
+
+ {{ if .Insecure }}ORBIT_INSECUREtrue{{ end }}
+ {{ if .FleetURL }}ORBIT_FLEET_URL{{.FleetURL}}{{ end }}
+ {{ if .EnrollSecret }}ORBIT_ENROLL_SECRET{{.EnrollSecret}}{{ end }}
+
+ KeepAlive
+ RunAtLoad
+ ThrottleInterval
+ 10
+
+
+`))
diff --git a/pkg/packaging/packaging.go b/pkg/packaging/packaging.go
index f482085d59..a2d1c84639 100644
--- a/pkg/packaging/packaging.go
+++ b/pkg/packaging/packaging.go
@@ -1,6 +1,14 @@
// package packaging provides tools for buildin Orbit installation packages.
package packaging
+import (
+ "io"
+ "os"
+ "path/filepath"
+
+ "github.com/pkg/errors"
+)
+
// Options are the configurable options provided for the package.
type Options struct {
// FleetURL is the URL to the Fleet server.
@@ -18,3 +26,27 @@ type Options struct {
// Insecure enables insecure TLS connections for the generated package.
Insecure bool
}
+
+func copyFile(srcPath, dstPath string, perm os.FileMode) error {
+ src, err := os.Open(srcPath)
+ if err != nil {
+ return errors.Wrap(err, "open src for copy")
+ }
+ defer src.Close()
+
+ if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
+ return errors.Wrap(err, "create dst dir for copy")
+ }
+
+ dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE, perm)
+ if err != nil {
+ return errors.Wrap(err, "open dst for copy")
+ }
+ defer dst.Close()
+
+ if _, err := io.Copy(dst, src); err != nil {
+ return errors.Wrap(err, "copy src to dst")
+ }
+
+ return nil
+}