mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 16:39:01 +00:00
feedback left by @mna and @gillespi314 in https://github.com/fleetdm/fleet/pull/20842 also fixes a bug found by @PezHub https://github.com/fleetdm/fleet/issues/13157#issuecomment-2261615471 related to https://github.com/fleetdm/fleet/issues/13157 # 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. --> - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
120 lines
4.4 KiB
Go
120 lines
4.4 KiB
Go
package update
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// EscrowBuddyRunner sets up [Escrow Buddy][1] to rotate FileVault keys on
|
|
// macOS without user interaction. This runner:
|
|
//
|
|
// - Ensures Escrow Buddy is added as a target for the update runner, so the
|
|
// authorization plugin is downloaded and installed.
|
|
// - Shells out to call `defaults` to configure Escrow Buddy according to
|
|
// server instructions provided via notifications.
|
|
//
|
|
// [1]: https://github.com/macadmins/escrow-buddy
|
|
type EscrowBuddyRunner struct {
|
|
// updateRunner is the wrapped Runner where Escrow Buddy will be set as
|
|
// a target.
|
|
updateRunner *Runner
|
|
// runCmdFunc can be set in tests to mock the command executed to
|
|
// configure Escrow Buddy
|
|
runCmdFunc func(cmd string, args ...string) error
|
|
// runMu guards runs to prevent multiple Run calls happening at the
|
|
// same time.
|
|
runMu sync.Mutex
|
|
// lastRun is used to guarantee that the run interval is enforced
|
|
lastRun time.Time
|
|
// interval defines how often Run is allowed to perform work
|
|
interval time.Duration
|
|
}
|
|
|
|
// NewEscrowBuddyRunner returns a new instance configured with the provided values
|
|
func NewEscrowBuddyRunner(runner *Runner, interval time.Duration) fleet.OrbitConfigReceiver {
|
|
return &EscrowBuddyRunner{updateRunner: runner, interval: interval}
|
|
}
|
|
|
|
func (e *EscrowBuddyRunner) Run(cfg *fleet.OrbitConfig) error {
|
|
log.Debug().Msgf("EscrowBuddyRunner: notification: %t", cfg.Notifications.RotateDiskEncryptionKey)
|
|
|
|
if e.updateRunner == nil {
|
|
log.Info().Msg("EscrowBuddyRunner: received nil UpdateRunner, this probably indicates that updates are turned off. Skipping any actions related to Disk encryption")
|
|
return nil
|
|
}
|
|
|
|
if !e.runMu.TryLock() {
|
|
log.Debug().Msg("EscrowBuddyRunner: a previous instance is currently running, returning early")
|
|
return nil
|
|
}
|
|
|
|
defer e.runMu.Unlock()
|
|
if time.Since(e.lastRun) < e.interval {
|
|
log.Debug().Msgf("EscrowBuddyRunner: last run (%v) is less than the configured interval (%v), returning early", e.lastRun, e.interval)
|
|
return nil
|
|
}
|
|
|
|
updaterHasTarget := e.updateRunner.HasRunnerOptTarget("escrowBuddy")
|
|
// if the notification is false, it could mean that we shouldn't do
|
|
// anything at all (eg: MDM is not configured) or that this host
|
|
// doesn't need to rotate the key.
|
|
//
|
|
// if Escrow Buddy is a TUF target, it means that we tried to rotate
|
|
// the key before, and we must disable it to keep the local state as
|
|
// instructed by the server.
|
|
if !cfg.Notifications.RotateDiskEncryptionKey {
|
|
if updaterHasTarget {
|
|
log.Debug().Msg("EscrowBuddyRunner: disabling disk encryption rotation")
|
|
e.lastRun = time.Now()
|
|
return e.setGenerateNewKeyTo(false)
|
|
}
|
|
|
|
log.Debug().Msg("EscrowBuddyRunner: skipping any actions related to disk encryption")
|
|
return nil
|
|
}
|
|
|
|
runnerHasLocalHash := e.updateRunner.HasLocalHash("escrowBuddy")
|
|
if !updaterHasTarget || !runnerHasLocalHash {
|
|
log.Info().Msg("refreshing the update runner config with Escrow Buddy targets and hashes")
|
|
log.Debug().Msgf("updater has target: %t, runner has local hash: %t", updaterHasTarget, runnerHasLocalHash)
|
|
if err := e.setTargetsAndHashes(); err != nil {
|
|
return fmt.Errorf("setting Escrow Buddy targets and hashes: %w", err)
|
|
}
|
|
}
|
|
|
|
log.Debug().Msg("EscrowBuddyRunner: enabling disk encryption rotation")
|
|
if err := e.setGenerateNewKeyTo(true); err != nil {
|
|
return fmt.Errorf("enabling disk encryption rotation: %w", err)
|
|
}
|
|
|
|
e.lastRun = time.Now()
|
|
return nil
|
|
}
|
|
|
|
func (e *EscrowBuddyRunner) setTargetsAndHashes() error {
|
|
e.updateRunner.AddRunnerOptTarget("escrowBuddy")
|
|
e.updateRunner.updater.SetTargetInfo("escrowBuddy", EscrowBuddyMacOSTarget)
|
|
// we don't want to keep escrowBuddy as a target if we failed to update the
|
|
// cached hashes in the runner.
|
|
if err := e.updateRunner.StoreLocalHash("escrowBuddy"); err != nil {
|
|
log.Debug().Msgf("removing escrowBuddy from target options, error updating local hashes: %s", err)
|
|
e.updateRunner.RemoveRunnerOptTarget("escrowBuddy")
|
|
e.updateRunner.updater.RemoveTargetInfo("escrowBuddy")
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *EscrowBuddyRunner) setGenerateNewKeyTo(enabled bool) error {
|
|
log.Debug().Msgf("running defaults write to configure Escrow Buddy with value %t", enabled)
|
|
cmd := fmt.Sprintf("defaults write /Library/Preferences/com.netflix.Escrow-Buddy.plist GenerateNewKey -bool %t", enabled)
|
|
fn := e.runCmdFunc
|
|
if fn == nil {
|
|
fn = runCmdCollectErr
|
|
}
|
|
return fn("sh", "-c", cmd)
|
|
}
|