mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
automatically install fleetd for hosts that turn MDM manually (#15883)
for #15057
This commit is contained in:
parent
c846b2b966
commit
f3d400d48e
8 changed files with 136 additions and 11 deletions
1
changes/manual-enrollment
Normal file
1
changes/manual-enrollment
Normal file
|
|
@ -0,0 +1 @@
|
|||
* fleetd is now automatically installed on Apple hosts turning on MDM features manually if it wasn't installed before.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue