automatically install fleetd for hosts that turn MDM manually (#15883)

for #15057
This commit is contained in:
Roberto Dip 2024-01-03 15:16:59 -03:00 committed by GitHub
parent c846b2b966
commit f3d400d48e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 136 additions and 11 deletions

View file

@ -0,0 +1 @@
* fleetd is now automatically installed on Apple hosts turning on MDM features manually if it wasn't installed before.

View file

@ -3626,7 +3626,8 @@ func (ds *Datastore) GetHostMDMCheckinInfo(ctx context.Context, hostUUID string)
COALESCE(hm.installed_from_dep, false) as installed_from_dep,
hd.display_name,
COALESCE(h.team_id, 0) as team_id,
hda.host_id IS NOT NULL AND hda.deleted_at IS NULL as dep_assigned_to_fleet
hda.host_id IS NOT NULL AND hda.deleted_at IS NULL as dep_assigned_to_fleet,
h.node_key IS NOT NULL as osquery_enrolled
FROM
hosts h
LEFT JOIN

View file

@ -6997,18 +6997,30 @@ func testHostsGetHostMDMCheckinInfo(t *testing.T, ds *Datastore) {
require.Equal(t, true, info.InstalledFromDEP)
require.EqualValues(t, tm.ID, info.TeamID)
require.False(t, info.DEPAssignedToFleet)
require.True(t, info.OsqueryEnrolled)
err = ds.UpsertMDMAppleHostDEPAssignments(ctx, []fleet.Host{*host})
require.NoError(t, err)
info, err = ds.GetHostMDMCheckinInfo(ctx, host.UUID)
require.NoError(t, err)
require.True(t, info.DEPAssignedToFleet)
require.True(t, info.OsqueryEnrolled)
err = ds.DeleteHostDEPAssignments(ctx, []string{host.HardwareSerial})
require.NoError(t, err)
info, err = ds.GetHostMDMCheckinInfo(ctx, host.UUID)
require.NoError(t, err)
require.False(t, info.DEPAssignedToFleet)
require.True(t, info.OsqueryEnrolled)
// host with an empty node key
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `UPDATE hosts SET node_key = NULL WHERE uuid = ?`, host.UUID)
return err
})
info, err = ds.GetHostMDMCheckinInfo(ctx, host.UUID)
require.NoError(t, err)
require.False(t, info.OsqueryEnrolled)
}
func testHostsLoadHostByOrbitNodeKey(t *testing.T, ds *Datastore) {

View file

@ -1152,6 +1152,7 @@ type HostMDMCheckinInfo struct {
DisplayName string `json:"display_name" db:"display_name"`
TeamID uint `json:"team_id" db:"team_id"`
DEPAssignedToFleet bool `json:"dep_assigned_to_fleet" db:"dep_assigned_to_fleet"`
OsqueryEnrolled bool `json:"osquery_enrolled" db:"osquery_enrolled"`
}
type HostDiskEncryptionKey struct {

View file

@ -2233,15 +2233,15 @@ func (svc *MDMAppleCheckinAndCommandService) TokenUpdate(r *mdm.Request, m *mdm.
return err
}
var tmID *uint
if info.TeamID != 0 {
tmID = &info.TeamID
}
// TODO: improve this to not enqueue the job if a host that is
// assigned in ABM is manually enrolling for some reason.
if info.DEPAssignedToFleet || info.InstalledFromDEP {
svc.logger.Log("info", "queueing post-enroll task for newly enrolled DEP device", "host_uuid", r.ID)
var tmID *uint
if info.TeamID != 0 {
tmID = &info.TeamID
}
if err := worker.QueueAppleMDMJob(
r.Context,
svc.ds,
@ -2254,6 +2254,21 @@ func (svc *MDMAppleCheckinAndCommandService) TokenUpdate(r *mdm.Request, m *mdm.
return ctxerr.Wrap(r.Context, err, "queue DEP post-enroll task")
}
}
// manual MDM enrollments that are not fleet-enrolled yet
if !info.InstalledFromDEP && !info.OsqueryEnrolled {
if err := worker.QueueAppleMDMJob(
r.Context,
svc.ds,
svc.logger,
worker.AppleMDMPostManualEnrollmentTask,
r.ID,
tmID,
r.Params[mobileconfig.FleetEnrollReferenceKey],
); err != nil {
return ctxerr.Wrap(r.Context, err, "queue manual post-enroll task")
}
}
}
return nil
}

View file

@ -10323,3 +10323,48 @@ func (s *integrationMDMTestSuite) TestWindowsFreshEnrollEmptyQuery() {
require.Contains(t, dqResp.Queries, "fleet_detail_query_mdm_config_profiles_windows")
require.NotEmpty(t, dqResp.Queries, "fleet_detail_query_mdm_config_profiles_windows")
}
func (s *integrationMDMTestSuite) TestManualEnrollmentCommands() {
t := s.T()
checkInstallFleetdCommandSent := func(mdmDevice *mdmtest.TestAppleMDMClient, wantCommand bool) {
foundInstallFleetdCommand := false
cmd, err := mdmDevice.Idle()
require.NoError(t, err)
for cmd != nil {
if manifest := cmd.Command.InstallEnterpriseApplication.ManifestURL; manifest != nil {
foundInstallFleetdCommand = true
require.Equal(t, "InstallEnterpriseApplication", cmd.Command.RequestType)
require.Contains(t, *cmd.Command.InstallEnterpriseApplication.ManifestURL, apple_mdm.FleetdPublicManifestURL)
}
cmd, err = mdmDevice.Acknowledge(cmd.CommandUUID)
require.NoError(t, err)
}
require.Equal(t, wantCommand, foundInstallFleetdCommand)
}
// create a device that's not enrolled into Fleet, it should get a command to
// install fleetd
mdmDevice := mdmtest.NewTestMDMClientAppleDirect(mdmtest.AppleEnrollInfo{
SCEPChallenge: s.fleetCfg.MDM.AppleSCEPChallenge,
SCEPURL: s.server.URL + apple_mdm.SCEPPath,
MDMURL: s.server.URL + apple_mdm.MDMPath,
})
err := mdmDevice.Enroll()
require.NoError(t, err)
s.runWorker()
checkInstallFleetdCommandSent(mdmDevice, true)
// create a device that's enrolled into Fleet before turning on MDM features,
// it shouldn't get the command to install fleetd
desktopToken := uuid.New().String()
host := createOrbitEnrolledHost(t, "darwin", "h1", s.ds)
err = s.ds.SetOrUpdateDeviceAuthToken(context.Background(), host.ID, desktopToken)
require.NoError(t, err)
mdmDevice = mdmtest.NewTestMDMClientAppleDesktopManual(s.server.URL, desktopToken)
mdmDevice.UUID = host.UUID
err = mdmDevice.Enroll()
require.NoError(t, err)
s.runWorker()
checkInstallFleetdCommandSent(mdmDevice, false)
}

View file

@ -23,7 +23,8 @@ type AppleMDMTask string
// List of supported tasks.
const (
AppleMDMPostDEPEnrollmentTask AppleMDMTask = "post_dep_enrollment"
AppleMDMPostDEPEnrollmentTask AppleMDMTask = "post_dep_enrollment"
AppleMDMPostManualEnrollmentTask AppleMDMTask = "post_manual_enrollment"
)
// AppleMDM is the job processor for the apple_mdm job.
@ -61,14 +62,30 @@ func (a *AppleMDM) Run(ctx context.Context, argsJSON json.RawMessage) error {
switch args.Task {
case AppleMDMPostDEPEnrollmentTask:
return a.runPostDEPEnrollment(ctx, args)
err := a.runPostDEPEnrollment(ctx, args)
return ctxerr.Wrap(ctx, err, "running post Apple DEP enrollment task")
case AppleMDMPostManualEnrollmentTask:
err := a.runPostManualEnrollment(ctx, args)
return ctxerr.Wrap(ctx, err, "running post Apple manual enrollment task")
default:
return ctxerr.Errorf(ctx, "unknown task: %v", args.Task)
}
}
func (a *AppleMDM) runPostManualEnrollment(ctx context.Context, args appleMDMArgs) error {
if err := a.installFleetd(ctx, args.HostUUID); err != nil {
return ctxerr.Wrap(ctx, err, "installing post-enrollment packages")
}
return nil
}
func (a *AppleMDM) runPostDEPEnrollment(ctx context.Context, args appleMDMArgs) error {
if err := a.installEnrollmentPackages(ctx, args.HostUUID, args.TeamID); err != nil {
if err := a.installFleetd(ctx, args.HostUUID); err != nil {
return ctxerr.Wrap(ctx, err, "installing post-enrollment packages")
}
if err := a.installBootstrapPackage(ctx, args.HostUUID, args.TeamID); err != nil {
return ctxerr.Wrap(ctx, err, "installing post-enrollment packages")
}
@ -109,13 +126,16 @@ func (a *AppleMDM) runPostDEPEnrollment(ctx context.Context, args appleMDMArgs)
return nil
}
func (a *AppleMDM) installEnrollmentPackages(ctx context.Context, hostUUID string, teamID *uint) error {
func (a *AppleMDM) installFleetd(ctx context.Context, hostUUID string) error {
cmdUUID := uuid.New().String()
if err := a.Commander.InstallEnterpriseApplication(ctx, []string{hostUUID}, cmdUUID, apple_mdm.FleetdPublicManifestURL); err != nil {
return err
}
a.Log.Log("info", "sent command to install fleetd", "host_uuid", hostUUID)
return nil
}
func (a *AppleMDM) installBootstrapPackage(ctx context.Context, hostUUID string, teamID *uint) error {
// GetMDMAppleBootstrapPackageMeta expects team id 0 for no team
var tmID uint
if teamID != nil {
@ -143,7 +163,7 @@ func (a *AppleMDM) installEnrollmentPackages(ctx context.Context, hostUUID strin
}
manifest := appmanifest.NewFromSha(meta.Sha256, url)
cmdUUID = uuid.New().String()
cmdUUID := uuid.New().String()
err = a.Commander.InstallEnterpriseApplicationWithEmbeddedManifest(ctx, []string{hostUUID}, cmdUUID, manifest)
if err != nil {
return err

View file

@ -388,4 +388,34 @@ func TestAppleMDM(t *testing.T) {
require.Empty(t, jobs)
require.ElementsMatch(t, []string{"InstallEnterpriseApplication", "AccountConfiguration"}, getEnqueuedCommandTypes(t))
})
t.Run("installs fleetd for manual enrollments", func(t *testing.T) {
defer mysql.TruncateTables(t, ds)
h := createEnrolledHost(t, 1, nil, true)
mdmWorker := &AppleMDM{
Datastore: ds,
Log: nopLog,
Commander: apple_mdm.NewMDMAppleCommander(mdmStorage, mockPusher{}),
}
w := NewWorker(ds, nopLog)
w.Register(mdmWorker)
err := QueueAppleMDMJob(ctx, ds, nopLog, AppleMDMPostManualEnrollmentTask, h.UUID, nil, "")
require.NoError(t, err)
// run the worker, should succeed
err = w.ProcessJobs(ctx)
require.NoError(t, err)
// ensure the job's not_before allows it to be returned if it were to run
// again
time.Sleep(time.Second)
jobs, err := ds.GetQueuedJobs(ctx, 1)
require.NoError(t, err)
require.Empty(t, jobs)
require.ElementsMatch(t, []string{"InstallEnterpriseApplication"}, getEnqueuedCommandTypes(t))
})
}