2021-03-03 01:19:24 +00:00
|
|
|
package packaging
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2023-04-27 11:44:39 +00:00
|
|
|
"crypto/tls"
|
2023-11-22 19:04:38 +00:00
|
|
|
"errors"
|
2021-11-22 14:13:26 +00:00
|
|
|
"fmt"
|
2021-03-03 01:19:24 +00:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"text/template"
|
|
|
|
|
|
2022-05-03 19:46:02 +00:00
|
|
|
"github.com/Masterminds/semver"
|
2021-08-11 14:02:22 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/constant"
|
|
|
|
|
"github.com/fleetdm/fleet/v4/orbit/pkg/update"
|
2021-08-24 12:50:03 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/pkg/secure"
|
2021-03-03 01:19:24 +00:00
|
|
|
"github.com/goreleaser/nfpm/v2"
|
2025-09-18 21:55:31 +00:00
|
|
|
"github.com/goreleaser/nfpm/v2/arch"
|
2021-03-03 01:19:24 +00:00
|
|
|
"github.com/goreleaser/nfpm/v2/files"
|
2024-06-10 19:49:45 +00:00
|
|
|
"github.com/goreleaser/nfpm/v2/rpm"
|
2021-03-03 01:19:24 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
)
|
|
|
|
|
|
[orbit/packaging] delay restart for in-band pkg upgrade on Linux (#31286)
Admins _should_ be upgrading orbit/osqueryd/fleet-desktop using TUF, but
there's no official path for pushing updates to the orbit environment
file (`/etc/default/orbit`).
Knowing that this file is installed by fleet-osquery, I naively pushed a
new fleet-osquery package to a user's machine, thinking that would be
fine installing over the existing package. Instead it actually broke
orbit entirely on the host, rendering it unreachable until the user
manually reinstalled the package. This is because the pre-removal script
unconditionally stops orbit even if it's being upgraded, and when orbit
is stopped it terminates any processes underneath it, including the
ongoing package installation.
To workaround this, we replace the simple
`systemctl restart orbit.service` with a check for the `INSTALLER_PATH`
environment variable that orbit sets during software installations. If
the variable is present, `systemd-run` is used to schedule the service
restart 60 seconds in the future, which is assumed to be more than
enough time for the package manager to finish and exit. Unfortunately,
this bugfix cannot be made retroactive, because the broken version of
the prerm script is called before the new package starts to be
unpacked/installed.
Although there are other ways of doing anything an administrator might
be trying to accomplish by pushing a new fleet-osquery package, bricking
an endpoint simply by pushing this package through the Software page
seems like a pretty massive footgun that is easy to protect against.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for safe in-band upgrades of DEB and RPM packages
generated by the `fleetctl package` command via the Software page, after
an initial manual update.
* **Documentation**
* Added a note clarifying the new upgrade process and the need for a
one-time manual update before using in-band upgrades.
* **Bug Fixes**
* Improved upgrade scripts to prevent the Orbit service from stopping
unexpectedly during package upgrades, ensuring smoother and safer
updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Dan Fuhry <dan@fuhry.com>
2025-07-28 13:55:44 +00:00
|
|
|
// Reusable snippet to conditionally wait to restart orbit upon an in-band upgrade
|
|
|
|
|
// (orbit installing an update to itself). Without this, the maintainer script will
|
|
|
|
|
// terminate orbit along with the package installation, aborting the install and
|
|
|
|
|
// potentially leaving the host in an unreachable state.
|
|
|
|
|
const postInstallSafeRestart = `
|
|
|
|
|
if test -z "${INSTALLER_PATH:-}"; then
|
|
|
|
|
systemctl restart orbit.service 2>&1
|
|
|
|
|
else
|
|
|
|
|
echo "Detected in-band upgrade (orbit upgrading orbit). Delaying service"
|
|
|
|
|
echo "restart to prevent orbit from being stopped mid-script."
|
|
|
|
|
if command -v systemd-run >/dev/null 2>&1; then
|
|
|
|
|
systemd-run --on-active=60 --working-directory=/ systemctl restart --no-block orbit.service
|
|
|
|
|
else
|
|
|
|
|
echo "...nevermind, systemd-run not available, exiting postinst"
|
|
|
|
|
echo "without restarting."
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
`
|
|
|
|
|
|
2021-11-15 13:40:58 +00:00
|
|
|
func buildNFPM(opt Options, pkger nfpm.Packager) (string, error) {
|
2021-03-03 01:19:24 +00:00
|
|
|
// Initialize directories
|
2021-10-27 23:17:41 +00:00
|
|
|
tmpDir, err := initializeTempDir()
|
2021-03-03 01:19:24 +00:00
|
|
|
if err != nil {
|
2021-11-15 13:40:58 +00:00
|
|
|
return "", err
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
|
|
2022-05-02 18:18:59 +00:00
|
|
|
rootDir := filepath.Join(tmpDir, "root")
|
|
|
|
|
if err := secure.MkdirAll(rootDir, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("create root dir: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
2022-05-02 18:18:59 +00:00
|
|
|
orbitRoot := filepath.Join(rootDir, "opt", "orbit")
|
2021-08-11 14:02:22 +00:00
|
|
|
if err := secure.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("create orbit dir: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-07-17 20:07:59 +00:00
|
|
|
if opt.Architecture != ArchAmd64 && opt.Architecture != ArchArm64 {
|
|
|
|
|
return "", fmt.Errorf("Invalid architecture: %s", opt.Architecture)
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-03 01:19:24 +00:00
|
|
|
// Initialize autoupdate metadata
|
|
|
|
|
updateOpt := update.DefaultOptions
|
2022-03-15 19:04:12 +00:00
|
|
|
|
2021-03-03 01:19:24 +00:00
|
|
|
updateOpt.RootDirectory = orbitRoot
|
2024-07-17 20:07:59 +00:00
|
|
|
if opt.Architecture == ArchArm64 {
|
|
|
|
|
updateOpt.Targets = update.LinuxArm64Targets
|
|
|
|
|
} else {
|
|
|
|
|
updateOpt.Targets = update.LinuxTargets
|
|
|
|
|
}
|
2023-04-27 11:44:39 +00:00
|
|
|
updateOpt.ServerCertificatePath = opt.UpdateTLSServerCertificate
|
|
|
|
|
|
|
|
|
|
if opt.UpdateTLSClientCertificate != "" {
|
|
|
|
|
updateClientCrt, err := tls.LoadX509KeyPair(opt.UpdateTLSClientCertificate, opt.UpdateTLSClientKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("error loading update client certificate and key: %w", err)
|
|
|
|
|
}
|
|
|
|
|
updateOpt.ClientCertificate = &updateClientCrt
|
|
|
|
|
}
|
2022-03-15 19:04:12 +00:00
|
|
|
|
2022-05-04 14:14:12 +00:00
|
|
|
if opt.Desktop {
|
2024-07-17 20:07:59 +00:00
|
|
|
if opt.Architecture == ArchArm64 {
|
2024-10-28 23:40:19 +00:00
|
|
|
updateOpt.Targets[constant.DesktopTUFTargetName] = update.DesktopLinuxArm64Target
|
2024-07-17 20:07:59 +00:00
|
|
|
} else {
|
2024-10-28 23:40:19 +00:00
|
|
|
updateOpt.Targets[constant.DesktopTUFTargetName] = update.DesktopLinuxTarget
|
2024-07-17 20:07:59 +00:00
|
|
|
}
|
2022-05-04 14:14:12 +00:00
|
|
|
// Override default channel with the provided value.
|
2024-10-28 23:40:19 +00:00
|
|
|
updateOpt.Targets.SetTargetChannel(constant.DesktopTUFTargetName, opt.DesktopChannel)
|
2022-05-04 14:14:12 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-15 19:04:12 +00:00
|
|
|
// Override default channels with the provided values.
|
2024-10-28 23:40:19 +00:00
|
|
|
updateOpt.Targets.SetTargetChannel(constant.OrbitTUFTargetName, opt.OrbitChannel)
|
|
|
|
|
updateOpt.Targets.SetTargetChannel(constant.OsqueryTUFTargetName, opt.OsquerydChannel)
|
2022-03-15 19:04:12 +00:00
|
|
|
|
2021-03-10 22:42:02 +00:00
|
|
|
updateOpt.ServerURL = opt.UpdateURL
|
2021-03-23 00:38:32 +00:00
|
|
|
if opt.UpdateRoots != "" {
|
|
|
|
|
updateOpt.RootKeys = opt.UpdateRoots
|
|
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
|
2022-01-31 13:41:11 +00:00
|
|
|
updatesData, err := InitializeUpdates(updateOpt)
|
|
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("initialize updates: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
2022-01-31 13:41:11 +00:00
|
|
|
log.Debug().Stringer("data", updatesData).Msg("updates initialized")
|
|
|
|
|
if opt.Version == "" {
|
|
|
|
|
// We set the package version to orbit's latest version.
|
|
|
|
|
opt.Version = updatesData.OrbitVersion
|
|
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
|
2022-05-03 19:46:02 +00:00
|
|
|
varLibSymlink := false
|
|
|
|
|
if orbitSemVer, err := semver.NewVersion(updatesData.OrbitVersion); err == nil {
|
|
|
|
|
if orbitSemVer.LessThan(semver.MustParse("0.0.11")) {
|
|
|
|
|
varLibSymlink = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If err != nil we assume non-legacy Orbit.
|
|
|
|
|
|
2021-03-03 01:19:24 +00:00
|
|
|
// Write files
|
|
|
|
|
|
2024-06-10 19:49:45 +00:00
|
|
|
_, isRPM := pkger.(*rpm.RPM)
|
|
|
|
|
|
2022-05-02 18:18:59 +00:00
|
|
|
if err := writeSystemdUnit(opt, rootDir); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write systemd unit: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2022-05-02 18:18:59 +00:00
|
|
|
if err := writeEnvFile(opt, rootDir); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write env file: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2021-11-18 23:06:33 +00:00
|
|
|
if err := writeOsqueryFlagfile(opt, orbitRoot); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write flagfile: %w", err)
|
2021-11-18 23:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-11-19 01:17:05 +00:00
|
|
|
if err := writeOsqueryCertPEM(opt, orbitRoot); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write certs.pem: %w", err)
|
2021-11-19 01:17:05 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-03 01:19:24 +00:00
|
|
|
postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
|
|
|
|
|
if err := writePostInstall(opt, postInstallPath); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write postinstall script: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
2022-04-19 12:32:47 +00:00
|
|
|
preRemovePath := filepath.Join(tmpDir, "preremove.sh")
|
2025-09-18 21:55:31 +00:00
|
|
|
if err := writePreRemove(pkger, preRemovePath); err != nil {
|
2022-04-19 12:32:47 +00:00
|
|
|
return "", fmt.Errorf("write preremove script: %w", err)
|
|
|
|
|
}
|
|
|
|
|
postRemovePath := filepath.Join(tmpDir, "postremove.sh")
|
2024-06-10 19:49:45 +00:00
|
|
|
if err := writePostRemove(postRemovePath); err != nil {
|
2022-04-19 12:32:47 +00:00
|
|
|
return "", fmt.Errorf("write postremove script: %w", err)
|
|
|
|
|
}
|
2024-06-10 19:49:45 +00:00
|
|
|
var postTransPath string
|
|
|
|
|
if isRPM {
|
|
|
|
|
postTransPath = filepath.Join(tmpDir, "posttrans.sh")
|
|
|
|
|
if err := writeRPMPostTrans(opt, postTransPath); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write RPM posttrans script: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
|
2021-04-24 01:03:48 +00:00
|
|
|
if opt.FleetCertificate != "" {
|
2023-04-27 11:44:39 +00:00
|
|
|
if err := writeFleetServerCertificate(opt, orbitRoot); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write fleet server certificate: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opt.FleetTLSClientCertificate != "" {
|
|
|
|
|
if err := writeFleetClientCertificate(opt, orbitRoot); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write fleet client certificate: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opt.UpdateTLSServerCertificate != "" {
|
|
|
|
|
if err := writeUpdateServerCertificate(opt, orbitRoot); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write update server certificate: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opt.UpdateTLSClientCertificate != "" {
|
|
|
|
|
if err := writeUpdateClientCertificate(opt, orbitRoot); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write update client certificate: %w", err)
|
2021-04-24 01:03:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-03 01:19:24 +00:00
|
|
|
// Pick up all file contents
|
|
|
|
|
|
|
|
|
|
contents := files.Contents{
|
|
|
|
|
&files.Content{
|
2022-05-02 18:18:59 +00:00
|
|
|
Source: filepath.Join(rootDir, "**"),
|
2021-03-03 01:19:24 +00:00
|
|
|
Destination: "/",
|
|
|
|
|
},
|
2022-05-02 18:18:59 +00:00
|
|
|
// Symlink current into /opt/orbit/bin/orbit/orbit
|
2021-03-03 01:19:24 +00:00
|
|
|
&files.Content{
|
2024-10-28 23:40:19 +00:00
|
|
|
Source: "/opt/orbit/bin/orbit/" + updateOpt.Targets[constant.OrbitTUFTargetName].Platform + "/" + opt.OrbitChannel + "/orbit",
|
2022-05-02 18:18:59 +00:00
|
|
|
Destination: "/opt/orbit/bin/orbit/orbit",
|
2021-03-03 01:19:24 +00:00
|
|
|
Type: "symlink",
|
|
|
|
|
FileInfo: &files.ContentFileInfo{
|
|
|
|
|
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// Symlink current into /usr/local/bin
|
|
|
|
|
&files.Content{
|
2022-05-02 18:18:59 +00:00
|
|
|
Source: "/opt/orbit/bin/orbit/orbit",
|
2021-03-03 01:19:24 +00:00
|
|
|
Destination: "/usr/local/bin/orbit",
|
|
|
|
|
Type: "symlink",
|
|
|
|
|
FileInfo: &files.ContentFileInfo{
|
|
|
|
|
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-19 12:32:01 +00:00
|
|
|
// Add empty folders to be created.
|
|
|
|
|
for _, emptyFolder := range []string{"/var/log/osquery", "/var/log/orbit"} {
|
|
|
|
|
contents = append(contents, (&files.Content{
|
|
|
|
|
Destination: emptyFolder,
|
|
|
|
|
Type: "dir",
|
|
|
|
|
}).WithFileInfoDefaults())
|
|
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
|
2022-05-03 19:46:02 +00:00
|
|
|
if varLibSymlink {
|
|
|
|
|
contents = append(contents,
|
|
|
|
|
// Symlink needed to support old versions of orbit.
|
|
|
|
|
&files.Content{
|
|
|
|
|
Source: "/opt/orbit",
|
|
|
|
|
Destination: "/var/lib/orbit",
|
|
|
|
|
Type: "symlink",
|
|
|
|
|
FileInfo: &files.ContentFileInfo{
|
|
|
|
|
Mode: os.ModeSymlink,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-02 18:18:59 +00:00
|
|
|
contents, err = files.ExpandContentGlobs(contents, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("glob contents: %w", err)
|
|
|
|
|
}
|
|
|
|
|
for _, c := range contents {
|
|
|
|
|
log.Debug().Interface("file", c).Msg("added file")
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-10 19:49:45 +00:00
|
|
|
rpmInfo := nfpm.RPM{}
|
|
|
|
|
if _, ok := pkger.(*rpm.RPM); ok {
|
|
|
|
|
rpmInfo.Scripts.PostTrans = postTransPath
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-18 21:55:31 +00:00
|
|
|
archLinuxInfo := nfpm.ArchLinux{}
|
|
|
|
|
if _, ok := pkger.(arch.ArchLinux); ok {
|
|
|
|
|
archLinuxInfo.Packager = "Fleet"
|
|
|
|
|
preUpgradePath := filepath.Join(tmpDir, "preupgrade.sh")
|
|
|
|
|
if err := writeArchLinuxPreUpgrade(preUpgradePath); err != nil {
|
|
|
|
|
return "", fmt.Errorf("write preupgrade script: %w", err)
|
|
|
|
|
}
|
|
|
|
|
archLinuxInfo.Scripts.PreUpgrade = preUpgradePath
|
|
|
|
|
archLinuxInfo.Scripts.PostUpgrade = postInstallPath
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-19 12:32:01 +00:00
|
|
|
// Build package
|
2021-03-03 01:19:24 +00:00
|
|
|
info := &nfpm.Info{
|
2021-11-19 01:43:52 +00:00
|
|
|
Name: "fleet-osquery",
|
2021-03-03 01:19:24 +00:00
|
|
|
Version: opt.Version,
|
2021-11-19 01:43:52 +00:00
|
|
|
Description: "Fleet osquery -- runtime and autoupdater",
|
2024-07-17 20:07:59 +00:00
|
|
|
Arch: opt.Architecture,
|
2022-11-21 18:55:12 +00:00
|
|
|
Maintainer: "Fleet Device Management",
|
|
|
|
|
Vendor: "Fleet Device Management",
|
|
|
|
|
License: "https://github.com/fleetdm/fleet/blob/main/LICENSE",
|
2021-11-19 01:43:52 +00:00
|
|
|
Homepage: "https://fleetdm.com",
|
2021-03-03 01:19:24 +00:00
|
|
|
Overridables: nfpm.Overridables{
|
|
|
|
|
Contents: contents,
|
|
|
|
|
Scripts: nfpm.Scripts{
|
|
|
|
|
PostInstall: postInstallPath,
|
2022-04-19 12:32:47 +00:00
|
|
|
PreRemove: preRemovePath,
|
|
|
|
|
PostRemove: postRemovePath,
|
2021-03-03 01:19:24 +00:00
|
|
|
},
|
2025-09-18 21:55:31 +00:00
|
|
|
RPM: rpmInfo,
|
|
|
|
|
ArchLinux: archLinuxInfo,
|
2021-03-03 01:19:24 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
filename := pkger.ConventionalFileName(info)
|
Adding optional parameter outfile to fleetctl package (#29579)
Fixes #29581
# Checklist for submitter
If some of the following don't apply, delete the relevant line.
<!-- Note that API documentation changes are now addressed by the
product design team. -->
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
- [ ] If database migrations are included, checked table schema to
confirm autoupdate
- For new Fleet configuration settings
- [ ] Verified that the setting can be managed via GitOps, or confirmed
that the setting is explicitly being excluded from GitOps. If managing
via Gitops:
- [ ] Verified that the setting is exported via `fleetctl
generate-gitops`
- [ ] Added the setting to [the GitOps
documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485)
- [ ] Verified that the setting is cleared on the server if it is not
supplied in a YAML file (or that it is documented as being optional)
- [ ] Verified that any relevant UI is disabled when GitOps mode is
enabled
- For database migrations:
- [ ] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [ ] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [ ] Added/updated automated tests
- [ ] Manual QA for all new/changed functionality
- For Orbit and Fleet Desktop changes:
- [ ] Make sure fleetd is compatible with the latest released version of
Fleet (see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md)).
- [ ] Orbit runs on macOS, Linux and Windows. Check if the orbit
feature/bugfix should only apply to one platform (`runtime.GOOS`).
- [ ] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
- [ ] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).
- [ ] For unreleased bug fixes in a release candidate, confirmed that
the fix is not expected to adversely impact load test results or alerted
the release DRI if additional load testing is needed.
2025-06-12 15:25:40 +00:00
|
|
|
if opt.CustomOutfile != "" {
|
|
|
|
|
filename = opt.CustomOutfile
|
|
|
|
|
}
|
2022-07-11 12:49:13 +00:00
|
|
|
if opt.NativeTooling {
|
|
|
|
|
filename = filepath.Join("build", filename)
|
|
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
|
2023-11-22 19:04:38 +00:00
|
|
|
if err := os.Remove(filename); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
return "", fmt.Errorf("removing existing file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-18 14:32:55 +00:00
|
|
|
if opt.NativeTooling {
|
|
|
|
|
if err := secure.MkdirAll(filepath.Dir(filename), 0o700); err != nil {
|
[fleetctl] fix deb package build (#23796)
I'm trying to setup a GitLab CI job to build the deb packages, but it
fails with:
```
Error: open output file: open build/fleet-osquery_1.35.0_amd64.deb: no such file or directory
```
It happens because I can't mount the `build` volume as I would do on my
local pc. To reproduce:
```
docker run -it fleetdm/fleetctl package --type=deb --fleet-url=my.url --enroll-secret=mysecret
```
`pkg` and `msi` works fine. I can workaround it by creating the `build`
dir first, but I think that's also fleetctl job to create it.
# Checklist for submitter
- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files)
for more information.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.
- [ ] Added/updated tests
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes
- [ ] If database migrations are included, checked table schema to
confirm autoupdate
- For database migrations:
- [ ] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [ ] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [ ] Manual QA for all new/changed functionality
- For Orbit and Fleet Desktop changes:
- [ ] Orbit runs on macOS, Linux and Windows. Check if the orbit
feature/bugfix should only apply to one platform (`runtime.GOOS`).
- [ ] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.
- [ ] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).
2024-11-18 13:43:41 +00:00
|
|
|
return "", fmt.Errorf("cannot create build dir: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 14:02:22 +00:00
|
|
|
out, err := secure.OpenFile(filename, os.O_CREATE|os.O_RDWR, constant.DefaultFileMode)
|
2021-03-03 01:19:24 +00:00
|
|
|
if err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("open output file: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
defer out.Close()
|
|
|
|
|
|
|
|
|
|
if err := pkger.Package(info, out); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("write package: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
2021-08-17 12:41:56 +00:00
|
|
|
if err := out.Sync(); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return "", fmt.Errorf("sync output file: %w", err)
|
2021-08-17 12:41:56 +00:00
|
|
|
}
|
2021-03-03 01:19:24 +00:00
|
|
|
log.Info().Str("path", filename).Msg("wrote package")
|
|
|
|
|
|
2021-11-15 13:40:58 +00:00
|
|
|
return filename, nil
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeSystemdUnit(opt Options, rootPath string) error {
|
|
|
|
|
systemdRoot := filepath.Join(rootPath, "usr", "lib", "systemd", "system")
|
2021-08-11 14:02:22 +00:00
|
|
|
if err := secure.MkdirAll(systemdRoot, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("create systemd dir: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
2023-10-27 18:28:54 +00:00
|
|
|
if err := os.WriteFile(
|
2021-03-03 01:19:24 +00:00
|
|
|
filepath.Join(systemdRoot, "orbit.service"),
|
|
|
|
|
[]byte(`
|
|
|
|
|
[Unit]
|
|
|
|
|
Description=Orbit osquery
|
|
|
|
|
After=network.service syslog.service
|
|
|
|
|
StartLimitIntervalSec=0
|
|
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
TimeoutStartSec=0
|
|
|
|
|
EnvironmentFile=/etc/default/orbit
|
2022-05-02 18:18:59 +00:00
|
|
|
ExecStart=/opt/orbit/bin/orbit/orbit
|
2021-03-03 01:19:24 +00:00
|
|
|
Restart=always
|
|
|
|
|
RestartSec=1
|
|
|
|
|
KillMode=control-group
|
|
|
|
|
KillSignal=SIGTERM
|
|
|
|
|
CPUQuota=20%
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
|
`),
|
2022-05-23 22:18:19 +00:00
|
|
|
constant.DefaultSystemdUnitMode,
|
2021-03-03 01:19:24 +00:00
|
|
|
); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("write file: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var envTemplate = template.Must(template.New("env").Parse(`
|
2021-03-10 22:42:02 +00:00
|
|
|
ORBIT_UPDATE_URL={{ .UpdateURL }}
|
2021-03-10 00:27:35 +00:00
|
|
|
ORBIT_ORBIT_CHANNEL={{ .OrbitChannel }}
|
|
|
|
|
ORBIT_OSQUERYD_CHANNEL={{ .OsquerydChannel }}
|
2022-04-11 20:42:36 +00:00
|
|
|
ORBIT_UPDATE_INTERVAL={{ .OrbitUpdateInterval }}
|
2022-05-04 14:14:12 +00:00
|
|
|
{{ if .Desktop }}
|
|
|
|
|
ORBIT_FLEET_DESKTOP=true
|
|
|
|
|
ORBIT_DESKTOP_CHANNEL={{ .DesktopChannel }}
|
2023-04-27 11:44:39 +00:00
|
|
|
{{ if .FleetDesktopAlternativeBrowserHost }}
|
|
|
|
|
ORBIT_FLEET_DESKTOP_ALTERNATIVE_BROWSER_HOST={{ .FleetDesktopAlternativeBrowserHost }}
|
|
|
|
|
{{ end }}
|
2022-05-04 14:14:12 +00:00
|
|
|
{{ end }}
|
2021-03-10 00:27:35 +00:00
|
|
|
{{ if .Insecure }}ORBIT_INSECURE=true{{ end }}
|
2022-02-18 18:42:39 +00:00
|
|
|
{{ if .DisableUpdates }}ORBIT_DISABLE_UPDATES=true{{ end }}
|
2021-03-03 01:19:24 +00:00
|
|
|
{{ if .FleetURL }}ORBIT_FLEET_URL={{.FleetURL}}{{ end }}
|
2022-05-02 18:18:59 +00:00
|
|
|
{{ if .FleetCertificate }}ORBIT_FLEET_CERTIFICATE=/opt/orbit/fleet.pem{{ end }}
|
2023-04-27 11:44:39 +00:00
|
|
|
{{ if .UpdateTLSServerCertificate }}ORBIT_UPDATE_TLS_CERTIFICATE=/opt/orbit/update.pem{{ end }}
|
2021-03-03 01:19:24 +00:00
|
|
|
{{ if .EnrollSecret }}ORBIT_ENROLL_SECRET={{.EnrollSecret}}{{ end }}
|
2021-03-09 23:22:17 +00:00
|
|
|
{{ if .Debug }}ORBIT_DEBUG=true{{ end }}
|
2023-08-30 13:18:34 +00:00
|
|
|
{{ if .EnableScripts }}ORBIT_ENABLE_SCRIPTS=true{{ end }}
|
2023-12-15 18:26:32 +00:00
|
|
|
{{ if and (ne .HostIdentifier "") (ne .HostIdentifier "uuid") }}ORBIT_HOST_IDENTIFIER={{.HostIdentifier}}{{ end }}
|
2024-02-05 12:41:06 +00:00
|
|
|
{{ if .OsqueryDB }}ORBIT_OSQUERY_DB={{.OsqueryDB}}{{ end }}
|
2024-06-18 15:10:19 +00:00
|
|
|
{{ if .EndUserEmail }}ORBIT_END_USER_EMAIL={{.EndUserEmail}}{{ end }}
|
2025-07-23 17:30:44 +00:00
|
|
|
{{ if .FleetManagedHostIdentityCertificate }}ORBIT_FLEET_MANAGED_HOST_IDENTITY_CERTIFICATE=true{{ end }}
|
2025-09-05 14:07:03 +00:00
|
|
|
{{ if .DisableSetupExperience }}ORBIT_DISABLE_SETUP_EXPERIENCE=true{{ end }}
|
2021-03-03 01:19:24 +00:00
|
|
|
`))
|
|
|
|
|
|
|
|
|
|
func writeEnvFile(opt Options, rootPath string) error {
|
|
|
|
|
envRoot := filepath.Join(rootPath, "etc", "default")
|
2021-08-11 14:02:22 +00:00
|
|
|
if err := secure.MkdirAll(envRoot, constant.DefaultDirMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("create env dir: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var contents bytes.Buffer
|
|
|
|
|
if err := envTemplate.Execute(&contents, opt); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("execute template: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-27 18:28:54 +00:00
|
|
|
if err := os.WriteFile(
|
2021-03-03 01:19:24 +00:00
|
|
|
filepath.Join(envRoot, "orbit"),
|
|
|
|
|
contents.Bytes(),
|
|
|
|
|
constant.DefaultFileMode,
|
|
|
|
|
); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("write file: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-23 22:18:19 +00:00
|
|
|
var postInstallTemplate = template.Must(template.New("postinstall").Parse(`#!/bin/sh
|
2021-03-03 01:19:24 +00:00
|
|
|
|
|
|
|
|
# Exit on error
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
# If we have a systemd, daemon-reload away now
|
2022-01-31 23:56:05 +00:00
|
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
|
|
|
systemctl daemon-reload >/dev/null 2>&1
|
2021-03-03 01:19:24 +00:00
|
|
|
{{ if .StartService -}}
|
[orbit/packaging] delay restart for in-band pkg upgrade on Linux (#31286)
Admins _should_ be upgrading orbit/osqueryd/fleet-desktop using TUF, but
there's no official path for pushing updates to the orbit environment
file (`/etc/default/orbit`).
Knowing that this file is installed by fleet-osquery, I naively pushed a
new fleet-osquery package to a user's machine, thinking that would be
fine installing over the existing package. Instead it actually broke
orbit entirely on the host, rendering it unreachable until the user
manually reinstalled the package. This is because the pre-removal script
unconditionally stops orbit even if it's being upgraded, and when orbit
is stopped it terminates any processes underneath it, including the
ongoing package installation.
To workaround this, we replace the simple
`systemctl restart orbit.service` with a check for the `INSTALLER_PATH`
environment variable that orbit sets during software installations. If
the variable is present, `systemd-run` is used to schedule the service
restart 60 seconds in the future, which is assumed to be more than
enough time for the package manager to finish and exit. Unfortunately,
this bugfix cannot be made retroactive, because the broken version of
the prerm script is called before the new package starts to be
unpacked/installed.
Although there are other ways of doing anything an administrator might
be trying to accomplish by pushing a new fleet-osquery package, bricking
an endpoint simply by pushing this package through the Software page
seems like a pretty massive footgun that is easy to protect against.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for safe in-band upgrades of DEB and RPM packages
generated by the `fleetctl package` command via the Software page, after
an initial manual update.
* **Documentation**
* Added a note clarifying the new upgrade process and the need for a
one-time manual update before using in-band upgrades.
* **Bug Fixes**
* Improved upgrade scripts to prevent the Orbit service from stopping
unexpectedly during package upgrades, ensuring smoother and safer
updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Dan Fuhry <dan@fuhry.com>
2025-07-28 13:55:44 +00:00
|
|
|
` + postInstallSafeRestart + `
|
2022-01-31 23:56:05 +00:00
|
|
|
systemctl enable orbit.service 2>&1
|
2021-03-03 01:19:24 +00:00
|
|
|
{{- end}}
|
|
|
|
|
fi
|
|
|
|
|
`))
|
|
|
|
|
|
|
|
|
|
func writePostInstall(opt Options, path string) error {
|
|
|
|
|
var contents bytes.Buffer
|
|
|
|
|
if err := postInstallTemplate.Execute(&contents, opt); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("execute template: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-27 18:28:54 +00:00
|
|
|
if err := os.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
2021-11-22 14:13:26 +00:00
|
|
|
return fmt.Errorf("write file: %w", err)
|
2021-03-03 01:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2022-04-19 12:32:47 +00:00
|
|
|
|
2025-09-18 21:55:31 +00:00
|
|
|
func writePreRemove(pkger nfpm.Packager, path string) error {
|
2022-05-04 14:14:12 +00:00
|
|
|
// We add `|| true` in case the service is not running
|
|
|
|
|
// or has been manually disabled already. Otherwise,
|
|
|
|
|
// uninstallation fails.
|
2022-06-21 19:26:14 +00:00
|
|
|
//
|
[orbit/packaging] delay restart for in-band pkg upgrade on Linux (#31286)
Admins _should_ be upgrading orbit/osqueryd/fleet-desktop using TUF, but
there's no official path for pushing updates to the orbit environment
file (`/etc/default/orbit`).
Knowing that this file is installed by fleet-osquery, I naively pushed a
new fleet-osquery package to a user's machine, thinking that would be
fine installing over the existing package. Instead it actually broke
orbit entirely on the host, rendering it unreachable until the user
manually reinstalled the package. This is because the pre-removal script
unconditionally stops orbit even if it's being upgraded, and when orbit
is stopped it terminates any processes underneath it, including the
ongoing package installation.
To workaround this, we replace the simple
`systemctl restart orbit.service` with a check for the `INSTALLER_PATH`
environment variable that orbit sets during software installations. If
the variable is present, `systemd-run` is used to schedule the service
restart 60 seconds in the future, which is assumed to be more than
enough time for the package manager to finish and exit. Unfortunately,
this bugfix cannot be made retroactive, because the broken version of
the prerm script is called before the new package starts to be
unpacked/installed.
Although there are other ways of doing anything an administrator might
be trying to accomplish by pushing a new fleet-osquery package, bricking
an endpoint simply by pushing this package through the Software page
seems like a pretty massive footgun that is easy to protect against.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for safe in-band upgrades of DEB and RPM packages
generated by the `fleetctl package` command via the Software page, after
an initial manual update.
* **Documentation**
* Added a note clarifying the new upgrade process and the need for a
one-time manual update before using in-band upgrades.
* **Bug Fixes**
* Improved upgrade scripts to prevent the Orbit service from stopping
unexpectedly during package upgrades, ensuring smoother and safer
updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Dan Fuhry <dan@fuhry.com>
2025-07-28 13:55:44 +00:00
|
|
|
// Upgrades require special considerations.
|
|
|
|
|
//
|
|
|
|
|
// On Debian systems, the old package is removed BEFORE the
|
|
|
|
|
// new package is installed. The old package's prerm script is
|
|
|
|
|
// called with the first argument set to "upgrade" if the
|
|
|
|
|
// package is being upgraded. In this case, we do not stop or
|
|
|
|
|
// disable the service; it will be restarted by the post-install
|
|
|
|
|
// script. If called with "remove" or "deconfigure", we should
|
|
|
|
|
// stop and disable any running orbit service.
|
|
|
|
|
// https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html#details-of-unpack-phase-of-installation-or-upgrade
|
|
|
|
|
//
|
|
|
|
|
// On RPM systems, the old package is removed AFTER the new
|
|
|
|
|
// package is installed. The preun script is called with the
|
|
|
|
|
// argument "0" upon uninstall and "1" upon upgrade.
|
|
|
|
|
// https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_syntax
|
|
|
|
|
// https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#ordering
|
|
|
|
|
//
|
2022-06-21 19:26:14 +00:00
|
|
|
// "pkill fleet-desktop" is required because the application
|
|
|
|
|
// runs as user (separate from sudo command that launched it),
|
|
|
|
|
// so on some systems it's not killed properly.
|
2025-09-18 21:55:31 +00:00
|
|
|
preRemoveScript := `#!/bin/sh
|
2022-04-19 12:32:47 +00:00
|
|
|
|
[orbit/packaging] delay restart for in-band pkg upgrade on Linux (#31286)
Admins _should_ be upgrading orbit/osqueryd/fleet-desktop using TUF, but
there's no official path for pushing updates to the orbit environment
file (`/etc/default/orbit`).
Knowing that this file is installed by fleet-osquery, I naively pushed a
new fleet-osquery package to a user's machine, thinking that would be
fine installing over the existing package. Instead it actually broke
orbit entirely on the host, rendering it unreachable until the user
manually reinstalled the package. This is because the pre-removal script
unconditionally stops orbit even if it's being upgraded, and when orbit
is stopped it terminates any processes underneath it, including the
ongoing package installation.
To workaround this, we replace the simple
`systemctl restart orbit.service` with a check for the `INSTALLER_PATH`
environment variable that orbit sets during software installations. If
the variable is present, `systemd-run` is used to schedule the service
restart 60 seconds in the future, which is assumed to be more than
enough time for the package manager to finish and exit. Unfortunately,
this bugfix cannot be made retroactive, because the broken version of
the prerm script is called before the new package starts to be
unpacked/installed.
Although there are other ways of doing anything an administrator might
be trying to accomplish by pushing a new fleet-osquery package, bricking
an endpoint simply by pushing this package through the Software page
seems like a pretty massive footgun that is easy to protect against.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for safe in-band upgrades of DEB and RPM packages
generated by the `fleetctl package` command via the Software page, after
an initial manual update.
* **Documentation**
* Added a note clarifying the new upgrade process and the need for a
one-time manual update before using in-band upgrades.
* **Bug Fixes**
* Improved upgrade scripts to prevent the Orbit service from stopping
unexpectedly during package upgrades, ensuring smoother and safer
updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Dan Fuhry <dan@fuhry.com>
2025-07-28 13:55:44 +00:00
|
|
|
case "${1:-}" in
|
|
|
|
|
1|remove|deconfigure)
|
|
|
|
|
systemctl disable --now orbit.service || true
|
|
|
|
|
pkill fleet-desktop || true
|
|
|
|
|
;;
|
|
|
|
|
0|upgrade)
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
2025-09-18 21:55:31 +00:00
|
|
|
`
|
|
|
|
|
// pre_remove script on Arch Linux receives the old version as argument.
|
|
|
|
|
_, isArchLinux := pkger.(arch.ArchLinux)
|
|
|
|
|
if isArchLinux {
|
|
|
|
|
preRemoveScript = `#!/bin/sh
|
|
|
|
|
|
|
|
|
|
systemctl disable --now orbit.service || true
|
|
|
|
|
pkill fleet-desktop || true
|
|
|
|
|
|
|
|
|
|
`
|
|
|
|
|
}
|
|
|
|
|
if err := os.WriteFile(path, []byte(preRemoveScript), constant.DefaultFileMode); err != nil {
|
2022-04-19 12:32:47 +00:00
|
|
|
return fmt.Errorf("write file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-10 19:49:45 +00:00
|
|
|
func writePostRemove(path string) error {
|
2023-10-27 18:28:54 +00:00
|
|
|
if err := os.WriteFile(path, []byte(`#!/bin/sh
|
2022-04-19 12:32:47 +00:00
|
|
|
|
2023-10-27 16:29:09 +00:00
|
|
|
# For RPM during uninstall, $1 is 0
|
|
|
|
|
# For Debian during remove, $1 is "remove"
|
|
|
|
|
if [ "$1" = 0 ] || [ "$1" = "remove" ]; then
|
|
|
|
|
rm -rf /var/lib/orbit /var/log/orbit /usr/local/bin/orbit /etc/default/orbit /usr/lib/systemd/system/orbit.service /opt/orbit
|
|
|
|
|
fi
|
2022-04-19 12:32:47 +00:00
|
|
|
`), constant.DefaultFileMode); err != nil {
|
|
|
|
|
return fmt.Errorf("write file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-06-10 19:49:45 +00:00
|
|
|
|
|
|
|
|
// postTransTemplate contains the template for RPM posttrans scriptlet (used when upgrading).
|
|
|
|
|
// See https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/.
|
|
|
|
|
//
|
|
|
|
|
// We cannot rely on "$1" because it's always "0" for RPM < 4.12
|
|
|
|
|
// (see https://github.com/rpm-software-management/rpm/commit/ab069ec876639d46d12dd76dad54fd8fb762e43d)
|
|
|
|
|
// thus we check if orbit service is enabled, and if not we enable it (because posttrans
|
|
|
|
|
// will run both on "install" and "upgrade").
|
|
|
|
|
var postTransTemplate = template.Must(template.New("posttrans").Parse(`#!/bin/sh
|
|
|
|
|
|
|
|
|
|
# Exit on error
|
|
|
|
|
set -e
|
|
|
|
|
|
|
|
|
|
if ! systemctl is-enabled orbit >/dev/null 2>&1; then
|
|
|
|
|
# If we have a systemd, daemon-reload away now
|
|
|
|
|
if command -v systemctl >/dev/null 2>&1; then
|
|
|
|
|
systemctl daemon-reload >/dev/null 2>&1
|
|
|
|
|
{{ if .StartService -}}
|
[orbit/packaging] delay restart for in-band pkg upgrade on Linux (#31286)
Admins _should_ be upgrading orbit/osqueryd/fleet-desktop using TUF, but
there's no official path for pushing updates to the orbit environment
file (`/etc/default/orbit`).
Knowing that this file is installed by fleet-osquery, I naively pushed a
new fleet-osquery package to a user's machine, thinking that would be
fine installing over the existing package. Instead it actually broke
orbit entirely on the host, rendering it unreachable until the user
manually reinstalled the package. This is because the pre-removal script
unconditionally stops orbit even if it's being upgraded, and when orbit
is stopped it terminates any processes underneath it, including the
ongoing package installation.
To workaround this, we replace the simple
`systemctl restart orbit.service` with a check for the `INSTALLER_PATH`
environment variable that orbit sets during software installations. If
the variable is present, `systemd-run` is used to schedule the service
restart 60 seconds in the future, which is assumed to be more than
enough time for the package manager to finish and exit. Unfortunately,
this bugfix cannot be made retroactive, because the broken version of
the prerm script is called before the new package starts to be
unpacked/installed.
Although there are other ways of doing anything an administrator might
be trying to accomplish by pushing a new fleet-osquery package, bricking
an endpoint simply by pushing this package through the Software page
seems like a pretty massive footgun that is easy to protect against.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for safe in-band upgrades of DEB and RPM packages
generated by the `fleetctl package` command via the Software page, after
an initial manual update.
* **Documentation**
* Added a note clarifying the new upgrade process and the need for a
one-time manual update before using in-band upgrades.
* **Bug Fixes**
* Improved upgrade scripts to prevent the Orbit service from stopping
unexpectedly during package upgrades, ensuring smoother and safer
updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Dan Fuhry <dan@fuhry.com>
2025-07-28 13:55:44 +00:00
|
|
|
` + postInstallSafeRestart + `
|
2024-06-10 19:49:45 +00:00
|
|
|
systemctl enable orbit.service 2>&1
|
|
|
|
|
{{- end}}
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
`))
|
|
|
|
|
|
|
|
|
|
// writeRPMPostTrans sets the posttrans scriptlets necessary to support RPM upgrades.
|
|
|
|
|
func writeRPMPostTrans(opt Options, path string) error {
|
|
|
|
|
var contents bytes.Buffer
|
|
|
|
|
if err := postTransTemplate.Execute(&contents, opt); err != nil {
|
|
|
|
|
return fmt.Errorf("execute template: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if err := os.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
|
|
|
|
return fmt.Errorf("write file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-09-18 21:55:31 +00:00
|
|
|
|
|
|
|
|
func writeArchLinuxPreUpgrade(path string) error {
|
|
|
|
|
// Currently the pre-upgrade script for ArchLinux just cleanly
|
|
|
|
|
// brings the service down.
|
|
|
|
|
//
|
|
|
|
|
// We add `|| true` in case the service is not running
|
|
|
|
|
// or has been manually disabled already. Otherwise,
|
|
|
|
|
// script might return non-zero exit code.
|
|
|
|
|
const preUpgradeScript = `#!/bin/sh
|
|
|
|
|
|
|
|
|
|
systemctl disable --now orbit.service || true
|
|
|
|
|
pkill fleet-desktop || true
|
|
|
|
|
|
|
|
|
|
`
|
|
|
|
|
if err := os.WriteFile(path, []byte(preUpgradeScript), constant.DefaultFileMode); err != nil {
|
|
|
|
|
return fmt.Errorf("write file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|