mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
use Escrow Buddy to rotate FileVault keys on macOS (#20842)
back-end and agent part of #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] 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. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] Added/updated tests - [x] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [x] Orbit runs on macOS, Linux and Windows. Check if the orbit feature/bugfix should only apply to one platform (`runtime.GOOS`). - [x] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [x] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)).
This commit is contained in:
parent
fbf1f55399
commit
7a080a9b36
26 changed files with 537 additions and 199 deletions
8
Makefile
8
Makefile
|
|
@ -424,6 +424,14 @@ endif
|
|||
tar czf $(out-path)/swiftDialog.app.tar.gz -C $(TMP_DIR)/swiftDialog_pkg_payload_expanded/Library/Application\ Support/Dialog/ Dialog.app
|
||||
rm -rf $(TMP_DIR)
|
||||
|
||||
# Generate escrowBuddy.pkg bundle from the Escrow Buddy repo.
|
||||
#
|
||||
# Usage:
|
||||
# make escrow-buddy-pkg version=1.0.0 out-path=.
|
||||
escrow-buddy-pkg:
|
||||
curl -L https://github.com/macadmins/escrow-buddy/releases/download/v$(version)/Escrow.Buddy-$(version).pkg --output $(out-path)/escrowBuddy.pkg
|
||||
|
||||
|
||||
# Build and generate desktop.app.tar.gz bundle.
|
||||
#
|
||||
# Usage:
|
||||
|
|
|
|||
1
changes/13157-fv-escrow
Normal file
1
changes/13157-fv-escrow
Normal file
|
|
@ -0,0 +1 @@
|
|||
* `fleetd` now uses Escrow Buddy to rotate FileVault keys. Internal API endpoints documented in the API for contributors have been modified and/or removed.
|
||||
|
|
@ -270,6 +270,8 @@ func updatesAddFunc(c *cli.Context) error {
|
|||
dstPath += ".exe"
|
||||
case strings.HasSuffix(target, ".app.tar.gz"):
|
||||
dstPath += ".app.tar.gz"
|
||||
case strings.HasSuffix(target, ".pkg"):
|
||||
dstPath += ".pkg"
|
||||
// osquery extensions require the .ext suffix
|
||||
case strings.HasSuffix(target, ".ext"):
|
||||
dstPath += ".ext"
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ func (svc *Service) ListDevicePolicies(ctx context.Context, host *fleet.Host) ([
|
|||
return svc.ds.ListPoliciesForHost(ctx, host)
|
||||
}
|
||||
|
||||
func (svc *Service) RequestEncryptionKeyRotation(ctx context.Context, hostID uint) error {
|
||||
return svc.ds.SetDiskEncryptionResetStatus(ctx, hostID, true)
|
||||
}
|
||||
|
||||
const refetchMDMUnenrollCriticalQueryDuration = 3 * time.Minute
|
||||
|
||||
// TriggerMigrateMDMDevice triggers the webhook associated with the MDM
|
||||
|
|
|
|||
1
orbit/changes/13157-fv-escrow
Normal file
1
orbit/changes/13157-fv-escrow
Normal file
|
|
@ -0,0 +1 @@
|
|||
* Use Escrow Buddy to rotate FileVault keys on macOS
|
||||
|
|
@ -870,7 +870,11 @@ func main() {
|
|||
orbitClient.RegisterConfigReceiver(update.ApplyNudgeConfigReceiverMiddleware(update.NudgeConfigFetcherOptions{
|
||||
UpdateRunner: updateRunner, RootDir: c.String("root-dir"), Interval: nudgeLaunchInterval,
|
||||
}))
|
||||
orbitClient.RegisterConfigReceiver(update.ApplyDiskEncryptionRunnerMiddleware())
|
||||
if orbitClient.GetServerCapabilities().Has(fleet.CapabilityEscrowBuddy) {
|
||||
orbitClient.RegisterConfigReceiver(update.NewEscrowBuddyRunner(updateRunner, 5*time.Minute))
|
||||
} else {
|
||||
orbitClient.RegisterConfigReceiver(update.ApplyDiskEncryptionRunnerMiddleware())
|
||||
}
|
||||
orbitClient.RegisterConfigReceiver(update.ApplySwiftDialogDownloaderMiddleware(updateRunner))
|
||||
case "windows":
|
||||
orbitClient.RegisterConfigReceiver(update.ApplyWindowsMDMEnrollmentFetcherMiddleware(windowsMDMEnrollmentCommandFrequency, orbitHostInfo.HardwareUUID, orbitClient))
|
||||
|
|
|
|||
120
orbit/pkg/update/escrow_buddy.go
Normal file
120
orbit/pkg/update/escrow_buddy.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
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.Debug().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)
|
||||
}
|
||||
86
orbit/pkg/update/escrow_buddy_test.go
Normal file
86
orbit/pkg/update/escrow_buddy_test.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestEscrowBuddy(t *testing.T) {
|
||||
testingSuite := new(escrowBuddyTestSuite)
|
||||
testingSuite.s = &testingSuite.Suite
|
||||
suite.Run(t, testingSuite)
|
||||
}
|
||||
|
||||
type escrowBuddyTestSuite struct {
|
||||
suite.Suite
|
||||
withTUF
|
||||
}
|
||||
|
||||
func (s *escrowBuddyTestSuite) TestUpdatesDisabled() {
|
||||
t := s.T()
|
||||
cfg := &fleet.OrbitConfig{}
|
||||
cfg.Notifications.RotateDiskEncryptionKey = true
|
||||
r := NewEscrowBuddyRunner(nil, time.Second)
|
||||
err := r.Run(cfg)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func (s *escrowBuddyTestSuite) TestEscrowBuddyRotatesKey() {
|
||||
t := s.T()
|
||||
updater := &Updater{
|
||||
client: s.client,
|
||||
opt: Options{Targets: make(map[string]TargetInfo), RootDirectory: t.TempDir()},
|
||||
}
|
||||
runner := &Runner{updater: updater, localHashes: make(map[string][]byte)}
|
||||
escrowBuddyPath := "escrowBuddy/macos/stable/escrowBuddy.pkg"
|
||||
|
||||
cfg := &fleet.OrbitConfig{}
|
||||
r := &EscrowBuddyRunner{updateRunner: runner, interval: time.Millisecond}
|
||||
// mock the command to run the defaults cli
|
||||
cmdCalls := []map[string]any{}
|
||||
r.runCmdFunc = func(cmd string, args ...string) error {
|
||||
cmdCalls = append(cmdCalls, map[string]any{"cmd": cmd, "args": args})
|
||||
return nil
|
||||
}
|
||||
|
||||
// no new target added if the notification is not set
|
||||
err := r.Run(cfg)
|
||||
require.NoError(t, err)
|
||||
targets := runner.updater.opt.Targets
|
||||
require.Len(t, targets, 0)
|
||||
require.Empty(t, cmdCalls)
|
||||
|
||||
// there's an error when the remote repo doesn't have the target yet
|
||||
cfg.Notifications.RotateDiskEncryptionKey = true
|
||||
err = r.Run(cfg)
|
||||
require.ErrorContains(t, err, "tuf: file not found")
|
||||
require.Empty(t, cmdCalls)
|
||||
|
||||
// add escrow buddy to the remote
|
||||
s.addRemoteTarget(escrowBuddyPath)
|
||||
|
||||
err = r.Run(cfg)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cmdCalls, 1)
|
||||
require.Equal(t, cmdCalls[0]["cmd"], "sh")
|
||||
require.Equal(t, cmdCalls[0]["args"], []string{"-c", "defaults write /Library/Preferences/com.netflix.Escrow-Buddy.plist GenerateNewKey -bool true"})
|
||||
|
||||
targets = runner.updater.opt.Targets
|
||||
require.Len(t, targets, 1)
|
||||
ti, ok := targets["escrowBuddy"]
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, EscrowBuddyMacOSTarget, ti)
|
||||
|
||||
time.Sleep(3 * time.Millisecond)
|
||||
cfg.Notifications.RotateDiskEncryptionKey = false
|
||||
err = r.Run(cfg)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cmdCalls, 2)
|
||||
require.Equal(t, cmdCalls[1]["cmd"], "sh")
|
||||
require.Equal(t, cmdCalls[1]["args"], []string{"-c", "defaults write /Library/Preferences/com.netflix.Escrow-Buddy.plist GenerateNewKey -bool false"})
|
||||
|
||||
}
|
||||
|
|
@ -122,4 +122,10 @@ var (
|
|||
TargetFile: "swiftDialog.app.tar.gz",
|
||||
ExtractedExecSubPath: []string{"Dialog.app", "Contents", "MacOS", "Dialog"},
|
||||
}
|
||||
|
||||
EscrowBuddyMacOSTarget = TargetInfo{
|
||||
Platform: "macos",
|
||||
Channel: "stable",
|
||||
TargetFile: "escrowBuddy.pkg",
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ func (ts *withTUF) SetupSuite() {
|
|||
ts.mockFiles = map[string][]byte{
|
||||
"nudge/macos/stable/nudge.app.tar.gz": ts.memTarGz("/Nudge.app/Contents/MacOS/Nudge", "nudge"),
|
||||
"osqueryd/macos/stable/osqueryd.app.tar.gz": ts.memTarGz("osqueryd", "osqueryd"),
|
||||
"escrowBuddy/macos/stable/escrowBuddy.pkg": {},
|
||||
}
|
||||
ts.store = tuf.MemoryStore(nil, ts.mockFiles)
|
||||
|
||||
|
|
|
|||
|
|
@ -384,6 +384,12 @@ func (u *Updater) get(target string) (*LocalTarget, error) {
|
|||
return nil, fmt.Errorf("failed to remove old extracted dir: %q: %w", localTarget.DirPath, err)
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(localTarget.Path, ".pkg") {
|
||||
cmd := exec.Command("installer", "-pkg", localTarget.Path, "-target", "/")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return nil, fmt.Errorf("running pkgutil to install %s: %s: %w", localTarget.Path, string(out), err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Debug().Str("path", localTarget.Path).Str("target", target).Msg("found expected target locally")
|
||||
}
|
||||
|
|
@ -558,6 +564,14 @@ func (u *Updater) checkExec(target, tmpPath string, customCheckExec func(execPat
|
|||
tmpPath = filepath.Join(append([]string{filepath.Dir(tmpPath)}, localTarget.Info.ExtractedExecSubPath...)...)
|
||||
}
|
||||
|
||||
if strings.HasSuffix(tmpPath, ".pkg") && runtime.GOOS == "darwin" {
|
||||
cmd := exec.Command("pkgutil", "--payload-files", tmpPath)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("running pkgutil to verify %s: %s: %w", tmpPath, string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if customCheckExec != nil {
|
||||
if err := customCheckExec(tmpPath); err != nil {
|
||||
return fmt.Errorf("custom exec new version failed: %w", err)
|
||||
|
|
|
|||
|
|
@ -2271,7 +2271,6 @@ func (ds *Datastore) LoadHostByOrbitNodeKey(ctx context.Context, nodeKey string)
|
|||
h.policy_updated_at,
|
||||
h.public_ip,
|
||||
h.orbit_node_key,
|
||||
COALESCE(hdek.reset_requested, false) AS disk_encryption_reset_requested,
|
||||
IF(hdep.host_id AND ISNULL(hdep.deleted_at), true, false) AS dep_assigned_to_fleet,
|
||||
hd.encrypted as disk_encryption_enabled,
|
||||
COALESCE(hdek.decryptable, false) as encryption_key_available,
|
||||
|
|
@ -4955,20 +4954,6 @@ func (ds *Datastore) ListUpcomingHostMaintenanceWindows(ctx context.Context, hid
|
|||
return mws, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) SetDiskEncryptionResetStatus(ctx context.Context, hostID uint, status bool) error {
|
||||
const stmt = `
|
||||
INSERT INTO host_disk_encryption_keys (host_id, reset_requested, base64_encrypted)
|
||||
VALUES (?, ?, '')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
reset_requested = VALUES(reset_requested)`
|
||||
|
||||
_, err := ds.writer(ctx).ExecContext(ctx, stmt, hostID, status)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "upsert disk encryption reset status")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// countHostNotResponding counts the hosts that haven't been submitting results for sent queries.
|
||||
//
|
||||
// Notes:
|
||||
|
|
|
|||
|
|
@ -7616,7 +7616,6 @@ func testHostsLoadHostByOrbitNodeKey(t *testing.T, ds *Datastore) {
|
|||
|
||||
// the returned host by LoadHostByOrbitNodeKey will have the orbit key stored
|
||||
h.OrbitNodeKey = &orbitKey
|
||||
h.DiskEncryptionResetRequested = ptr.Bool(false)
|
||||
returned, err := ds.LoadHostByOrbitNodeKey(ctx, orbitKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -7696,8 +7695,8 @@ func testHostsLoadHostByOrbitNodeKey(t *testing.T, ds *Datastore) {
|
|||
require.NoError(t, err)
|
||||
loadFleet, err = ds.LoadHostByOrbitNodeKey(ctx, *hFleet.OrbitNodeKey)
|
||||
require.NoError(t, err)
|
||||
require.True(t, loadFleet.MDM.EncryptionKeyAvailable)
|
||||
require.NoError(t, err)
|
||||
require.True(t, loadFleet.MDM.EncryptionKeyAvailable)
|
||||
require.NotNil(t, loadFleet.DiskEncryptionEnabled)
|
||||
require.True(t, *loadFleet.DiskEncryptionEnabled)
|
||||
|
||||
|
|
@ -8361,8 +8360,8 @@ func testHostsEncryptionKeyRawDecryption(t *testing.T, ds *Datastore) {
|
|||
// no disk encryption key information
|
||||
got, err := ds.Host(ctx, host.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.NotNil(t, got.MDM.TestGetRawDecryptable())
|
||||
require.False(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.Equal(t, -1, *got.MDM.TestGetRawDecryptable())
|
||||
|
||||
// create the encryption key row, but unknown decryptable
|
||||
|
|
@ -8380,8 +8379,8 @@ func testHostsEncryptionKeyRawDecryption(t *testing.T, ds *Datastore) {
|
|||
|
||||
got, err = ds.Host(ctx, host.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.NotNil(t, got.MDM.TestGetRawDecryptable())
|
||||
require.False(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.Equal(t, 0, *got.MDM.TestGetRawDecryptable())
|
||||
|
||||
// mark the key as decryptable
|
||||
|
|
@ -8390,8 +8389,8 @@ func testHostsEncryptionKeyRawDecryption(t *testing.T, ds *Datastore) {
|
|||
|
||||
got, err = ds.Host(ctx, host.ID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.NotNil(t, got.MDM.TestGetRawDecryptable())
|
||||
require.True(t, got.MDM.EncryptionKeyAvailable)
|
||||
require.Equal(t, 1, *got.MDM.TestGetRawDecryptable())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ const (
|
|||
// CapabilityEndUserEmail denotes the ability of the server to support
|
||||
// receiving the end-user email from orbit.
|
||||
CapabilityEndUserEmail Capability = "end_user_email"
|
||||
// CapabilityEscrowBuddy allows to use Escrow Buddy to rotate FileVault keys
|
||||
CapabilityEscrowBuddy Capability = "escrow_buddy"
|
||||
)
|
||||
|
||||
func GetServerOrbitCapabilities() CapabilityMap {
|
||||
|
|
@ -85,6 +87,7 @@ func GetServerOrbitCapabilities() CapabilityMap {
|
|||
CapabilityOrbitEndpoints: {},
|
||||
CapabilityTokenRotation: {},
|
||||
CapabilityEndUserEmail: {},
|
||||
CapabilityEscrowBuddy: {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -883,8 +883,6 @@ type Datastore interface {
|
|||
// GetHostDiskEncryptionKey returns the encryption key information for a given host
|
||||
GetHostDiskEncryptionKey(ctx context.Context, hostID uint) (*HostDiskEncryptionKey, error)
|
||||
|
||||
SetDiskEncryptionResetStatus(ctx context.Context, hostID uint, status bool) error
|
||||
|
||||
// GetHostCertAssociationsToExpire retrieves host certificate
|
||||
// associations that are close to expire and don't have a renewal in
|
||||
// progress based on the provided arguments.
|
||||
|
|
|
|||
|
|
@ -321,10 +321,6 @@ type Host struct {
|
|||
// omitted if we don't have encryption information yet.
|
||||
DiskEncryptionEnabled *bool `json:"disk_encryption_enabled,omitempty" db:"disk_encryption_enabled" csv:"-"`
|
||||
|
||||
// DiskEncryptionResetRequested is only fetched when loading a host by
|
||||
// orbit_node_key, and so it's not used in the UI.
|
||||
DiskEncryptionResetRequested *bool `json:"disk_encryption_reset_requested,omitempty" db:"disk_encryption_reset_requested" csv:"-"`
|
||||
|
||||
HostIssues `json:"issues,omitempty" csv:"-"`
|
||||
|
||||
// DeviceMapping is in fact included in the CSV export, but it is not directly
|
||||
|
|
@ -574,11 +570,7 @@ func (d *MDMHostData) PopulateOSSettingsAndMacOSSettings(profiles []HostMDMApple
|
|||
// but either we didn't get an encryption key or we're not able to
|
||||
// decrypt the key we've got
|
||||
settings.DiskEncryption = DiskEncryptionActionRequired.addrOf()
|
||||
if *d.rawDecryptable == 0 {
|
||||
settings.ActionRequired = ActionRequiredRotateKey.addrOf()
|
||||
} else {
|
||||
settings.ActionRequired = ActionRequiredLogOut.addrOf()
|
||||
}
|
||||
settings.ActionRequired = ActionRequiredRotateKey.addrOf()
|
||||
} else {
|
||||
// if [a FileVault profile is pending to be installed or] the
|
||||
// matching row in host_disk_encryption_keys has a field decryptable
|
||||
|
|
@ -1230,19 +1222,3 @@ func IsEligibleForDEPMigration(host *Host, mdmInfo *HostMDM, isConnectedToFleetM
|
|||
// the checkout message from the host.
|
||||
(!isConnectedToFleetMDM || mdmInfo.Name != WellKnownMDMFleet)
|
||||
}
|
||||
|
||||
// IsEligibleForBitLockerEncryption checks if the host needs to enforce disk
|
||||
// encryption using Fleet MDM features.
|
||||
func IsEligibleForBitLockerEncryption(h *Host, mdmInfo *HostMDM, isConnectedToFleetMDM bool) bool {
|
||||
isServer := mdmInfo != nil && mdmInfo.IsServer
|
||||
isWindows := h.FleetPlatform() == "windows"
|
||||
needsEncryption := h.DiskEncryptionEnabled != nil && !*h.DiskEncryptionEnabled
|
||||
encryptedWithoutKey := h.DiskEncryptionEnabled != nil && *h.DiskEncryptionEnabled && !h.MDM.EncryptionKeyAvailable
|
||||
|
||||
return isWindows &&
|
||||
h.IsOsqueryEnrolled() &&
|
||||
isConnectedToFleetMDM &&
|
||||
!isServer &&
|
||||
mdmInfo != nil &&
|
||||
(needsEncryption || encryptedWithoutKey)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,50 +214,6 @@ func TestMDMEnrollmentStatus(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIsEligibleForBitLockerEncryption(t *testing.T) {
|
||||
require.False(t, IsEligibleForBitLockerEncryption(&Host{}, &HostMDM{}, false))
|
||||
|
||||
hostThatNeedsEnforcement := &Host{
|
||||
Platform: "windows",
|
||||
OsqueryHostID: ptr.String("test"),
|
||||
MDM: MDMHostData{
|
||||
EncryptionKeyAvailable: false,
|
||||
},
|
||||
DiskEncryptionEnabled: ptr.Bool(false),
|
||||
}
|
||||
hostThatNeedsEnforcementMdmInfo := &HostMDM{
|
||||
Name: WellKnownMDMFleet,
|
||||
Enrolled: true,
|
||||
IsServer: false,
|
||||
InstalledFromDep: true,
|
||||
}
|
||||
require.True(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
|
||||
// macOS hosts are not elegible
|
||||
hostThatNeedsEnforcement.Platform = "darwin"
|
||||
require.False(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
hostThatNeedsEnforcement.Platform = "windows"
|
||||
require.True(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
|
||||
// hosts with disk encryption already enabled are elegible only if we
|
||||
// can't decrypt the key
|
||||
hostThatNeedsEnforcement.DiskEncryptionEnabled = ptr.Bool(true)
|
||||
require.True(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
hostThatNeedsEnforcement.MDM.EncryptionKeyAvailable = true
|
||||
require.False(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
|
||||
hostThatNeedsEnforcement.DiskEncryptionEnabled = ptr.Bool(false)
|
||||
hostThatNeedsEnforcement.MDM.EncryptionKeyAvailable = false
|
||||
require.True(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
|
||||
// hosts without MDMinfo are not elegible
|
||||
require.False(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, nil, true))
|
||||
require.True(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, true))
|
||||
|
||||
// hosts that are not enrolled in MDM are not elegible
|
||||
require.False(t, IsEligibleForBitLockerEncryption(hostThatNeedsEnforcement, hostThatNeedsEnforcementMdmInfo, false))
|
||||
}
|
||||
|
||||
func TestIsEligibleForDEPMigration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -928,8 +928,6 @@ type Service interface {
|
|||
// for all hosts that are already marked as failing.
|
||||
ResetAutomation(ctx context.Context, teamIDs, policyIDs []uint) error
|
||||
|
||||
RequestEncryptionKeyRotation(ctx context.Context, hostID uint) error
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Windows MDM
|
||||
|
||||
|
|
|
|||
|
|
@ -618,8 +618,6 @@ type SetHostsDiskEncryptionKeyStatusFunc func(ctx context.Context, hostIDs []uin
|
|||
|
||||
type GetHostDiskEncryptionKeyFunc func(ctx context.Context, hostID uint) (*fleet.HostDiskEncryptionKey, error)
|
||||
|
||||
type SetDiskEncryptionResetStatusFunc func(ctx context.Context, hostID uint, status bool) error
|
||||
|
||||
type GetHostCertAssociationsToExpireFunc func(ctx context.Context, expiryDays int, limit int) ([]fleet.SCEPIdentityAssociation, error)
|
||||
|
||||
type SetCommandForPendingSCEPRenewalFunc func(ctx context.Context, assocs []fleet.SCEPIdentityAssociation, cmdUUID string) error
|
||||
|
|
@ -1910,9 +1908,6 @@ type DataStore struct {
|
|||
GetHostDiskEncryptionKeyFunc GetHostDiskEncryptionKeyFunc
|
||||
GetHostDiskEncryptionKeyFuncInvoked bool
|
||||
|
||||
SetDiskEncryptionResetStatusFunc SetDiskEncryptionResetStatusFunc
|
||||
SetDiskEncryptionResetStatusFuncInvoked bool
|
||||
|
||||
GetHostCertAssociationsToExpireFunc GetHostCertAssociationsToExpireFunc
|
||||
GetHostCertAssociationsToExpireFuncInvoked bool
|
||||
|
||||
|
|
@ -4597,13 +4592,6 @@ func (s *DataStore) GetHostDiskEncryptionKey(ctx context.Context, hostID uint) (
|
|||
return s.GetHostDiskEncryptionKeyFunc(ctx, hostID)
|
||||
}
|
||||
|
||||
func (s *DataStore) SetDiskEncryptionResetStatus(ctx context.Context, hostID uint, status bool) error {
|
||||
s.mu.Lock()
|
||||
s.SetDiskEncryptionResetStatusFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.SetDiskEncryptionResetStatusFunc(ctx, hostID, status)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetHostCertAssociationsToExpire(ctx context.Context, expiryDays int, limit int) ([]fleet.SCEPIdentityAssociation, error) {
|
||||
s.mu.Lock()
|
||||
s.GetHostCertAssociationsToExpireFuncInvoked = true
|
||||
|
|
|
|||
|
|
@ -564,41 +564,6 @@ func (svc *Service) GetDeviceMDMAppleEnrollmentProfile(ctx context.Context) ([]b
|
|||
return signed, nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Request a disk encryption reset
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type rotateEncryptionKeyRequest struct {
|
||||
Token string `url:"token"`
|
||||
}
|
||||
|
||||
func (r *rotateEncryptionKeyRequest) deviceAuthToken() string {
|
||||
return r.Token
|
||||
}
|
||||
|
||||
type rotateEncryptionKeyResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r rotateEncryptionKeyResponse) error() error { return r.Err }
|
||||
|
||||
func rotateEncryptionKeyEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
||||
host, ok := hostctx.FromContext(ctx)
|
||||
if !ok {
|
||||
err := ctxerr.Wrap(ctx, fleet.NewAuthRequiredError("internal error: missing host from request context"))
|
||||
return rotateEncryptionKeyResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
if err := svc.RequestEncryptionKeyRotation(ctx, host.ID); err != nil {
|
||||
return rotateEncryptionKeyResponse{Err: err}, nil
|
||||
}
|
||||
return rotateEncryptionKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (svc *Service) RequestEncryptionKeyRotation(ctx context.Context, hostID uint) error {
|
||||
return fleet.ErrMissingLicense
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Signal start of mdm migration on a device
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -799,10 +799,6 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
|||
errorLimiter.Limit("get_device_mdm", desktopQuota),
|
||||
).GET("/api/_version_/fleet/device/{token}/mdm/apple/manual_enrollment_profile", getDeviceMDMManualEnrollProfileEndpoint, getDeviceMDMManualEnrollProfileRequest{})
|
||||
|
||||
demdm.WithCustomMiddleware(
|
||||
errorLimiter.Limit("post_device_rotate_encryption_key", desktopQuota),
|
||||
).POST("/api/_version_/fleet/device/{token}/rotate_encryption_key", rotateEncryptionKeyEndpoint, rotateEncryptionKeyRequest{})
|
||||
|
||||
demdm.WithCustomMiddleware(
|
||||
errorLimiter.Limit("post_device_migrate_mdm", desktopQuota),
|
||||
).POST("/api/_version_/fleet/device/{token}/migrate_mdm", migrateMDMDeviceEndpoint, deviceMigrateMDMRequest{})
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ func TestHostDetailsMDMAppleDiskEncryption(t *testing.T) {
|
|||
OperationType: fleet.MDMOperationTypeInstall,
|
||||
},
|
||||
fleet.DiskEncryptionActionRequired,
|
||||
fleet.ActionRequiredLogOut,
|
||||
fleet.ActionRequiredRotateKey,
|
||||
&fleet.MDMDeliveryPending,
|
||||
},
|
||||
{
|
||||
|
|
@ -1857,3 +1857,196 @@ func TestBulkOperationFilterValidation(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDiskEncryptionNotifications(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
ctx := context.Background()
|
||||
svc := &Service{ds: ds}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
host *fleet.Host
|
||||
appConfig *fleet.AppConfig
|
||||
diskEncryptionConfigured bool
|
||||
isConnectedToFleetMDM bool
|
||||
mdmInfo *fleet.HostMDM
|
||||
getHostDiskEncryptionKey func(context.Context, uint) (*fleet.HostDiskEncryptionKey, error)
|
||||
expectedNotifications *fleet.OrbitConfigNotifications
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "no MDM configured",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin"},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: false},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: nil,
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "not connected to Fleet MDM",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin"},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: false,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: nil,
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "host not enrolled in osquery",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin", OsqueryHostID: nil},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: nil,
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "disk encryption not configured",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin"},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: false,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: nil,
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "darwin with decryptable key",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin"},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return &fleet.HostDiskEncryptionKey{Decryptable: ptr.Bool(true)}, nil
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
RotateDiskEncryptionKey: false,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "windows server with no encryption needed",
|
||||
host: &fleet.Host{ID: 1, Platform: "windows", DiskEncryptionEnabled: ptr.Bool(true)},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: &fleet.HostMDM{IsServer: true},
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return nil, newNotFoundError()
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
EnforceBitLockerEncryption: false,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "windows with encryption enabled but key missing",
|
||||
host: &fleet.Host{ID: 1, Platform: "windows", DiskEncryptionEnabled: ptr.Bool(true)},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: &fleet.HostMDM{IsServer: false},
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return nil, newNotFoundError()
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
EnforceBitLockerEncryption: true,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "darwin with missing encryption key",
|
||||
host: &fleet.Host{ID: 1, Platform: "darwin"},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: nil,
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return nil, newNotFoundError()
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
RotateDiskEncryptionKey: false,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "windows with encryption key and not decryptable",
|
||||
host: &fleet.Host{ID: 1, Platform: "windows", DiskEncryptionEnabled: ptr.Bool(true)},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: &fleet.HostMDM{IsServer: false},
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return &fleet.HostDiskEncryptionKey{Decryptable: ptr.Bool(false)}, nil
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
EnforceBitLockerEncryption: true,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "windows with enforce BitLocker",
|
||||
host: &fleet.Host{ID: 1, Platform: "windows", DiskEncryptionEnabled: ptr.Bool(false)},
|
||||
appConfig: &fleet.AppConfig{
|
||||
MDM: fleet.MDM{EnabledAndConfigured: true},
|
||||
},
|
||||
diskEncryptionConfigured: true,
|
||||
isConnectedToFleetMDM: true,
|
||||
mdmInfo: &fleet.HostMDM{IsServer: false},
|
||||
getHostDiskEncryptionKey: func(ctx context.Context, id uint) (*fleet.HostDiskEncryptionKey, error) {
|
||||
return nil, newNotFoundError()
|
||||
},
|
||||
expectedNotifications: &fleet.OrbitConfigNotifications{
|
||||
EnforceBitLockerEncryption: true,
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.getHostDiskEncryptionKey != nil {
|
||||
ds.GetHostDiskEncryptionKeyFunc = tt.getHostDiskEncryptionKey
|
||||
}
|
||||
ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) {
|
||||
return tt.appConfig, nil
|
||||
}
|
||||
|
||||
notifs := &fleet.OrbitConfigNotifications{}
|
||||
err := svc.setDiskEncryptionNotifications(ctx, notifs, tt.host, tt.appConfig, tt.diskEncryptionConfigured, tt.isConnectedToFleetMDM, tt.mdmInfo)
|
||||
if tt.expectedError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tt.expectedNotifications.RotateDiskEncryptionKey, notifs.RotateDiskEncryptionKey)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1729,7 +1729,7 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
|
|||
require.NotNil(t, getHostResp.Host.MDM.MacOSSettings.DiskEncryption)
|
||||
require.Equal(t, fleet.DiskEncryptionActionRequired, *getHostResp.Host.MDM.MacOSSettings.DiskEncryption)
|
||||
require.NotNil(t, getHostResp.Host.MDM.MacOSSettings.ActionRequired)
|
||||
require.Equal(t, fleet.ActionRequiredLogOut, *getHostResp.Host.MDM.MacOSSettings.ActionRequired)
|
||||
require.Equal(t, fleet.ActionRequiredRotateKey, *getHostResp.Host.MDM.MacOSSettings.ActionRequired)
|
||||
require.NotNil(t, getHostResp.Host.MDM.OSSettings)
|
||||
require.NotNil(t, getHostResp.Host.MDM.OSSettings.DiskEncryption.Status)
|
||||
require.Equal(t, fleet.DiskEncryptionActionRequired, *getHostResp.Host.MDM.OSSettings.DiskEncryption.Status)
|
||||
|
|
@ -2521,36 +2521,6 @@ func (s *integrationMDMTestSuite) TestEnrollOrbitAfterDEPSync() {
|
|||
require.Equal(t, h.ID, got.ID)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestDiskEncryptionRotation() {
|
||||
t := s.T()
|
||||
h := createOrbitEnrolledHost(t, "darwin", "h", s.ds)
|
||||
|
||||
// false by default
|
||||
resp := orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
require.False(t, resp.Notifications.RotateDiskEncryptionKey)
|
||||
|
||||
// create an auth token for h
|
||||
token := "much_valid"
|
||||
mysql.ExecAdhocSQL(t, s.ds, func(db sqlx.ExtContext) error {
|
||||
_, err := db.ExecContext(context.Background(), `INSERT INTO host_device_auth (host_id, token) VALUES (?, ?)`, h.ID, token)
|
||||
return err
|
||||
})
|
||||
|
||||
tokRes := s.DoRawNoAuth("POST", "/api/latest/fleet/device/"+token+"/rotate_encryption_key", nil, http.StatusOK)
|
||||
tokRes.Body.Close()
|
||||
|
||||
// true after the POST request
|
||||
resp = orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
require.True(t, resp.Notifications.RotateDiskEncryptionKey)
|
||||
|
||||
// false on following requests
|
||||
resp = orbitGetConfigResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/config", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *h.OrbitNodeKey)), http.StatusOK, &resp)
|
||||
require.False(t, resp.Notifications.RotateDiskEncryptionKey)
|
||||
}
|
||||
|
||||
func (s *integrationMDMTestSuite) TestFleetdConfiguration() {
|
||||
t := s.T()
|
||||
s.assertConfigProfilesByIdentifier(nil, mobileconfig.FleetdConfigPayloadIdentifier, false)
|
||||
|
|
|
|||
|
|
@ -207,15 +207,6 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro
|
|||
notifs.NeedsMDMMigration = true
|
||||
}
|
||||
|
||||
if host.DiskEncryptionResetRequested != nil && *host.DiskEncryptionResetRequested {
|
||||
notifs.RotateDiskEncryptionKey = true
|
||||
|
||||
// Since this is an user initiated action, we disable
|
||||
// the flag when we deliver the notification to Orbit
|
||||
if err := svc.ds.SetDiskEncryptionResetStatus(ctx, host.ID, false); err != nil {
|
||||
return fleet.OrbitConfig{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set the host's orbit notifications for Windows MDM
|
||||
|
|
@ -309,9 +300,17 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro
|
|||
}
|
||||
}
|
||||
|
||||
if mdmConfig.EnableDiskEncryption &&
|
||||
fleet.IsEligibleForBitLockerEncryption(host, mdmInfo, isConnectedToFleetMDM) {
|
||||
notifs.EnforceBitLockerEncryption = true
|
||||
err = svc.setDiskEncryptionNotifications(
|
||||
ctx,
|
||||
¬ifs,
|
||||
host,
|
||||
appConfig,
|
||||
mdmConfig.EnableDiskEncryption,
|
||||
isConnectedToFleetMDM,
|
||||
mdmInfo,
|
||||
)
|
||||
if err != nil {
|
||||
return fleet.OrbitConfig{}, ctxerr.Wrap(ctx, err, "setting team disk encryption notifications")
|
||||
}
|
||||
|
||||
var updateChannels *fleet.OrbitUpdateChannels
|
||||
|
|
@ -371,10 +370,17 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro
|
|||
}
|
||||
}
|
||||
|
||||
if appConfig.MDM.WindowsEnabledAndConfigured &&
|
||||
appConfig.MDM.EnableDiskEncryption.Value &&
|
||||
fleet.IsEligibleForBitLockerEncryption(host, mdmInfo, isConnectedToFleetMDM) {
|
||||
notifs.EnforceBitLockerEncryption = true
|
||||
err = svc.setDiskEncryptionNotifications(
|
||||
ctx,
|
||||
¬ifs,
|
||||
host,
|
||||
appConfig,
|
||||
appConfig.MDM.EnableDiskEncryption.Value,
|
||||
isConnectedToFleetMDM,
|
||||
mdmInfo,
|
||||
)
|
||||
if err != nil {
|
||||
return fleet.OrbitConfig{}, ctxerr.Wrap(ctx, err, "setting no-team disk encryption notifications")
|
||||
}
|
||||
|
||||
var updateChannels *fleet.OrbitUpdateChannels
|
||||
|
|
@ -396,6 +402,46 @@ func (svc *Service) GetOrbitConfig(ctx context.Context) (fleet.OrbitConfig, erro
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (svc *Service) setDiskEncryptionNotifications(
|
||||
ctx context.Context,
|
||||
notifs *fleet.OrbitConfigNotifications,
|
||||
host *fleet.Host,
|
||||
appConfig *fleet.AppConfig,
|
||||
diskEncryptionConfigured bool,
|
||||
isConnectedToFleetMDM bool,
|
||||
mdmInfo *fleet.HostMDM,
|
||||
) error {
|
||||
anyMDMConfigured := appConfig.MDM.EnabledAndConfigured || appConfig.MDM.WindowsEnabledAndConfigured
|
||||
if !anyMDMConfigured ||
|
||||
!isConnectedToFleetMDM ||
|
||||
!host.IsOsqueryEnrolled() ||
|
||||
!diskEncryptionConfigured {
|
||||
return nil
|
||||
}
|
||||
|
||||
encryptionKey, err := svc.ds.GetHostDiskEncryptionKey(ctx, host.ID)
|
||||
if err != nil {
|
||||
if !fleet.IsNotFound(err) {
|
||||
return ctxerr.Wrap(ctx, err, "fetching host disk encryption key")
|
||||
}
|
||||
}
|
||||
|
||||
switch host.FleetPlatform() {
|
||||
case "darwin":
|
||||
notifs.RotateDiskEncryptionKey = encryptionKey.Decryptable != nil && !*encryptionKey.Decryptable
|
||||
case "windows":
|
||||
isServer := mdmInfo != nil && mdmInfo.IsServer
|
||||
needsEncryption := host.DiskEncryptionEnabled != nil && !*host.DiskEncryptionEnabled
|
||||
keyWasDecrypted := encryptionKey != nil && encryptionKey.Decryptable != nil && *encryptionKey.Decryptable
|
||||
encryptedWithoutKey := host.DiskEncryptionEnabled != nil && *host.DiskEncryptionEnabled && !keyWasDecrypted
|
||||
notifs.EnforceBitLockerEncryption = !isServer &&
|
||||
mdmInfo != nil &&
|
||||
(needsEncryption || encryptedWithoutKey)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterExtensionsForHost filters a extensions configuration depending on the host platform and label membership.
|
||||
//
|
||||
// If all extensions are filtered, then it returns (nil, nil) (Orbit expects empty extensions if there
|
||||
|
|
|
|||
|
|
@ -225,6 +225,19 @@ make nudge-app-tar-gz version=1.1.10.81462 out-path=.
|
|||
fleetctl updates add --target /path/to/macos/nudge.app.tar.gz --platform macos --name nudge --version 1.1.10.81462 -t edge
|
||||
```
|
||||
|
||||
#### Releasing `Escrow Buddy` to `stable`
|
||||
|
||||
> `releaser.sh` doesn't support `Escrow Buddy` yet.
|
||||
> macOS only component
|
||||
|
||||
The `Escrow Buddy` pkg installer can be generated by running:
|
||||
```sh
|
||||
make escrow-buddy-pkg version=1.0.0 out-path=.
|
||||
```
|
||||
```sh
|
||||
fleetctl updates add --target /path/to/escrowBuddy.pkg --platform macos --name escrowBuddy --version 1.0.0 -t stable
|
||||
```
|
||||
|
||||
#### Updating timestamp
|
||||
|
||||
```sh
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ SYSTEMS=${SYSTEMS:-macos linux linux-arm64 windows}
|
|||
echo "Generating packages for $SYSTEMS"
|
||||
|
||||
NUDGE_VERSION=stable
|
||||
SWIFT_DIALOG_MACOS_APP_VERSION=2.2.1
|
||||
SWIFT_DIALOG_MACOS_APP_BUILD_VERSION=4591
|
||||
ESCROW_BUDDY_PKG_VERSION=1.0.0
|
||||
|
||||
if [[ -z "$OSQUERY_VERSION" ]]; then
|
||||
OSQUERY_VERSION=5.12.2
|
||||
|
|
@ -168,6 +167,20 @@ for system in $SYSTEMS; do
|
|||
rm swiftDialog.app.tar.gz
|
||||
fi
|
||||
|
||||
# Add Escrow Buddy on macos (if enabled).
|
||||
if [[ $system == "macos" && -n "$ESCROW_BUDDY" ]]; then
|
||||
make escrow-buddy-pkg version=$ESCROW_BUDDY_PKG_VERSION out-path=.
|
||||
|
||||
./build/fleetctl updates add \
|
||||
--path $TUF_PATH \
|
||||
--target escrowBuddy.pkg \
|
||||
--platform macos \
|
||||
--name escrowBuddy \
|
||||
--version 42.0.0 -t 42.0 -t 42 -t stable
|
||||
rm escrowBuddy.pkg
|
||||
fi
|
||||
|
||||
|
||||
# Add Fleet Desktop application on windows (if enabled).
|
||||
if [[ $system == "windows" && -n "$FLEET_DESKTOP" ]]; then
|
||||
FLEET_DESKTOP_VERSION=42.0.0 \
|
||||
|
|
|
|||
Loading…
Reference in a new issue