mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
ensure profiles and commands are delivered when MDM is turned on (#12580)
Related to #12482 and #12453, this cleans up Fleet tables that track profile and bootstrap package status on re-enrollment.
This commit is contained in:
parent
4b139245cb
commit
5ddd940cb8
1
changes/mdm-turn-on
Normal file
1
changes/mdm-turn-on
Normal file
@ -0,0 +1 @@
|
||||
* Make sure that all configuration profiles and commands are sent to devices if MDM is turned on, even if the device never turned off MDM.
|
@ -418,6 +418,7 @@ INNER JOIN
|
||||
ON
|
||||
nvq.id = h.uuid
|
||||
WHERE
|
||||
nvq.active = 1 AND
|
||||
%s
|
||||
`, ds.whereFilterHostsByTeams(tmFilter, "h"))
|
||||
stmt, params := appendListOptionsWithCursorToSQL(stmt, nil, &listOpts.ListOptions)
|
||||
@ -2173,7 +2174,7 @@ func (ds *Datastore) InsertMDMAppleBootstrapPackage(ctx context.Context, bp *fle
|
||||
if isDuplicate(err) {
|
||||
return ctxerr.Wrap(ctx, alreadyExists("BootstrapPackage", fmt.Sprintf("for team %d", bp.TeamID)))
|
||||
}
|
||||
return ctxerr.Wrap(ctx, err, "create bootstrap pacckage")
|
||||
return ctxerr.Wrap(ctx, err, "create bootstrap package")
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -2626,14 +2627,31 @@ func (ds *Datastore) GetMDMAppleDefaultSetupAssistant(ctx context.Context, teamI
|
||||
return asst.ProfileUUID, asst.UploadedAt, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) ResetMDMAppleNanoEnrollment(ctx context.Context, hostUUID string) error {
|
||||
// it's okay if we didn't update any rows, `nano_enrollments` entries
|
||||
// are created on `TokenUpdate`, and this function is called on
|
||||
// `Authenticate` to make sure we start on a clean state if a host is
|
||||
// re-enrolling.
|
||||
_, err := ds.writer(ctx).ExecContext(ctx, `UPDATE nano_enrollments SET token_update_tally = 0 WHERE id = ?`, hostUUID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "updating nano_enrollments")
|
||||
}
|
||||
return nil
|
||||
func (ds *Datastore) ResetMDMAppleEnrollment(ctx context.Context, hostUUID string) error {
|
||||
return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
|
||||
// it's okay if we didn't update any rows, `nano_enrollments` entries
|
||||
// are created on `TokenUpdate`, and this function is called on
|
||||
// `Authenticate` to make sure we start on a clean state if a host is
|
||||
// re-enrolling.
|
||||
_, err := tx.ExecContext(ctx, `UPDATE nano_enrollments SET token_update_tally = 0 WHERE id = ?`, hostUUID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "resetting nano_enrollments")
|
||||
}
|
||||
|
||||
// Deleting profiles from this table will cause all profiles to
|
||||
// be re-delivered on the next cron run.
|
||||
if err := ds.deleteMDMAppleProfilesForHost(ctx, tx, hostUUID); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "resetting profiles status")
|
||||
}
|
||||
|
||||
// Deleting the matching entry on this table will cause
|
||||
// the aggregate report to show this host as 'pending' to
|
||||
// install the bootstrap package.
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM host_mdm_apple_bootstrap_packages WHERE host_uuid = ?`, hostUUID)
|
||||
if err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "resetting host_mdm_apple_bootstrap_packages")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func TestMDMApple(t *testing.T) {
|
||||
{"TestSetVerifiedMacOSProfiles", testSetVerifiedMacOSProfiles},
|
||||
{"TestMDMAppleConfigProfileHash", testMDMAppleConfigProfileHash},
|
||||
{"TestMatchMDMAppleConfigProfiles", testMatchMDMAppleConfigProfiles},
|
||||
{"TestResetMDMAppleNanoEnrollment", testResetMDMAppleNanoEnrollment},
|
||||
{"TestResetMDMAppleEnrollment", testResetMDMAppleEnrollment},
|
||||
{"TestMDMAppleDeleteHostDEPAssignments", testMDMAppleDeleteHostDEPAssignments},
|
||||
}
|
||||
|
||||
@ -3438,6 +3438,17 @@ func testListMDMAppleCommands(t *testing.T, ds *Datastore) {
|
||||
TeamID: &tm1.ID,
|
||||
},
|
||||
})
|
||||
|
||||
// randomly set two commadns as inactive
|
||||
ExecAdhocSQL(t, ds, func(tx sqlx.ExtContext) error {
|
||||
_, err := tx.ExecContext(ctx, `UPDATE nano_enrollment_queue SET active = 0 LIMIT 2`)
|
||||
return err
|
||||
})
|
||||
// only two results are listed
|
||||
res, err = ds.ListMDMAppleCommands(ctx, fleet.TeamFilter{User: test.UserAdmin}, &fleet.MDMAppleCommandListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res, 2)
|
||||
|
||||
}
|
||||
|
||||
func testMDMAppleEULA(t *testing.T, ds *Datastore) {
|
||||
@ -4430,7 +4441,7 @@ func testMatchMDMAppleConfigProfiles(t *testing.T, ds *Datastore) {
|
||||
}
|
||||
}
|
||||
|
||||
func testResetMDMAppleNanoEnrollment(t *testing.T, ds *Datastore) {
|
||||
func testResetMDMAppleEnrollment(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
host, err := ds.NewHost(ctx, &fleet.Host{
|
||||
Hostname: "test-host1-name",
|
||||
@ -4444,22 +4455,72 @@ func testResetMDMAppleNanoEnrollment(t *testing.T, ds *Datastore) {
|
||||
|
||||
// try with a host that doesn't have a matching entry
|
||||
// in nano_enrollments
|
||||
err = ds.ResetMDMAppleNanoEnrollment(ctx, host.UUID)
|
||||
err = ds.ResetMDMAppleEnrollment(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add a matching entry
|
||||
// add a matching entry in the nano table
|
||||
nanoEnroll(t, ds, host, false)
|
||||
|
||||
enrollment, err := ds.GetNanoMDMEnrollment(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, enrollment.TokenUpdateTally, 1)
|
||||
|
||||
err = ds.ResetMDMAppleNanoEnrollment(ctx, host.UUID)
|
||||
// add configuration profiles
|
||||
cp, err := ds.NewMDMAppleConfigProfile(ctx, *generateCP("name0", "identifier0", 0))
|
||||
require.NoError(t, err)
|
||||
upsertHostCPs([]*fleet.Host{host}, []*fleet.MDMAppleConfigProfile{cp}, fleet.MDMAppleOperationTypeInstall, &fleet.MDMAppleDeliveryVerified, ctx, ds, t)
|
||||
|
||||
gotProfs, err := ds.GetHostMDMProfiles(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, gotProfs, 1)
|
||||
|
||||
// add a record of the bootstrap package being installed
|
||||
_, err = ds.writer(ctx).Exec(`
|
||||
INSERT INTO nano_commands (command_uuid, request_type, command)
|
||||
VALUES ('command-uuid', 'foo', '<?xml')
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
_, err = ds.writer(ctx).Exec(`
|
||||
INSERT INTO nano_command_results (id, command_uuid, status, result)
|
||||
VALUES (?, 'command-uuid', 'Acknowledged', '<?xml')
|
||||
`, host.UUID)
|
||||
require.NoError(t, err)
|
||||
err = ds.InsertMDMAppleBootstrapPackage(ctx, &fleet.MDMAppleBootstrapPackage{
|
||||
TeamID: uint(0),
|
||||
Name: t.Name(),
|
||||
Sha256: sha256.New().Sum(nil),
|
||||
Bytes: []byte("content"),
|
||||
Token: uuid.New().String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = ds.RecordHostBootstrapPackage(ctx, "command-uuid", host.UUID)
|
||||
require.NoError(t, err)
|
||||
err = ds.SetOrUpdateMDMData(context.Background(), host.ID, false, true, "foo.mdm.example.com", true, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
sum, err := ds.GetMDMAppleBootstrapPackageSummary(ctx, uint(0))
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, sum.Failed)
|
||||
require.Zero(t, sum.Pending)
|
||||
require.EqualValues(t, 1, sum.Installed)
|
||||
|
||||
// reset the enrollment
|
||||
err = ds.ResetMDMAppleEnrollment(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
enrollment, err = ds.GetNanoMDMEnrollment(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, enrollment.TokenUpdateTally)
|
||||
|
||||
gotProfs, err = ds.GetHostMDMProfiles(ctx, host.UUID)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, gotProfs)
|
||||
|
||||
sum, err = ds.GetMDMAppleBootstrapPackageSummary(ctx, uint(0))
|
||||
require.NoError(t, err)
|
||||
require.Zero(t, sum.Failed)
|
||||
require.Zero(t, sum.Installed)
|
||||
require.EqualValues(t, 1, sum.Pending)
|
||||
}
|
||||
|
||||
func testMDMAppleDeleteHostDEPAssignments(t *testing.T, ds *Datastore) {
|
||||
|
@ -345,7 +345,8 @@ var hostRefs = []string{
|
||||
// the host.uuid is not always named the same, so the map key is the table name
|
||||
// and the map value is the column name to match to the host.uuid.
|
||||
var additionalHostRefsByUUID = map[string]string{
|
||||
"host_mdm_apple_profiles": "host_uuid",
|
||||
"host_mdm_apple_profiles": "host_uuid",
|
||||
"host_mdm_apple_bootstrap_packages": "host_uuid",
|
||||
}
|
||||
|
||||
func (ds *Datastore) DeleteHost(ctx context.Context, hid uint) error {
|
||||
|
@ -2,6 +2,7 @@ package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -5773,6 +5774,22 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
|
||||
_, err = ds.writer(context.Background()).Exec(`INSERT INTO host_dep_assignments (host_id) VALUES (?)`, host.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ds.writer(context.Background()).Exec(`
|
||||
INSERT INTO nano_commands (command_uuid, request_type, command)
|
||||
VALUES ('command-uuid', 'foo', '<?xml')
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
err = ds.InsertMDMAppleBootstrapPackage(context.Background(), &fleet.MDMAppleBootstrapPackage{
|
||||
TeamID: uint(0),
|
||||
Name: t.Name(),
|
||||
Sha256: sha256.New().Sum(nil),
|
||||
Bytes: []byte("content"),
|
||||
Token: uuid.New().String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = ds.RecordHostBootstrapPackage(context.Background(), "command-uuid", host.UUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check there's an entry for the host in all the associated tables.
|
||||
for _, hostRef := range hostRefs {
|
||||
var ok bool
|
||||
|
@ -852,10 +852,9 @@ type Datastore interface {
|
||||
// not already enrolled in Fleet.
|
||||
IngestMDMAppleDeviceFromCheckin(ctx context.Context, mdmHost MDMAppleHostDetails) error
|
||||
|
||||
// ResetMDMAppleNanoEnrollment resets the
|
||||
// `nano_enrollments.token_update_tally` if a matching row for the host
|
||||
// exists.
|
||||
ResetMDMAppleNanoEnrollment(ctx context.Context, hostUUID string) error
|
||||
// ResetMDMAppleEnrollment resets all tables with enrollment-related
|
||||
// information if a matching row for the host exists.
|
||||
ResetMDMAppleEnrollment(ctx context.Context, hostUUID string) error
|
||||
|
||||
// ListMDMAppleDEPSerialsInTeam returns a list of serial numbers of hosts
|
||||
// that are enrolled or pending enrollment in Fleet's MDM via DEP for the
|
||||
|
@ -574,7 +574,7 @@ type IngestMDMAppleDevicesFromDEPSyncFunc func(ctx context.Context, devices []go
|
||||
|
||||
type IngestMDMAppleDeviceFromCheckinFunc func(ctx context.Context, mdmHost fleet.MDMAppleHostDetails) error
|
||||
|
||||
type ResetMDMAppleNanoEnrollmentFunc func(ctx context.Context, hostUUID string) error
|
||||
type ResetMDMAppleEnrollmentFunc func(ctx context.Context, hostUUID string) error
|
||||
|
||||
type ListMDMAppleDEPSerialsInTeamFunc func(ctx context.Context, teamID *uint) ([]string, error)
|
||||
|
||||
@ -1489,8 +1489,8 @@ type DataStore struct {
|
||||
IngestMDMAppleDeviceFromCheckinFunc IngestMDMAppleDeviceFromCheckinFunc
|
||||
IngestMDMAppleDeviceFromCheckinFuncInvoked bool
|
||||
|
||||
ResetMDMAppleNanoEnrollmentFunc ResetMDMAppleNanoEnrollmentFunc
|
||||
ResetMDMAppleNanoEnrollmentFuncInvoked bool
|
||||
ResetMDMAppleEnrollmentFunc ResetMDMAppleEnrollmentFunc
|
||||
ResetMDMAppleEnrollmentFuncInvoked bool
|
||||
|
||||
ListMDMAppleDEPSerialsInTeamFunc ListMDMAppleDEPSerialsInTeamFunc
|
||||
ListMDMAppleDEPSerialsInTeamFuncInvoked bool
|
||||
@ -3558,11 +3558,11 @@ func (s *DataStore) IngestMDMAppleDeviceFromCheckin(ctx context.Context, mdmHost
|
||||
return s.IngestMDMAppleDeviceFromCheckinFunc(ctx, mdmHost)
|
||||
}
|
||||
|
||||
func (s *DataStore) ResetMDMAppleNanoEnrollment(ctx context.Context, hostUUID string) error {
|
||||
func (s *DataStore) ResetMDMAppleEnrollment(ctx context.Context, hostUUID string) error {
|
||||
s.mu.Lock()
|
||||
s.ResetMDMAppleNanoEnrollmentFuncInvoked = true
|
||||
s.ResetMDMAppleEnrollmentFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.ResetMDMAppleNanoEnrollmentFunc(ctx, hostUUID)
|
||||
return s.ResetMDMAppleEnrollmentFunc(ctx, hostUUID)
|
||||
}
|
||||
|
||||
func (s *DataStore) ListMDMAppleDEPSerialsInTeam(ctx context.Context, teamID *uint) ([]string, error) {
|
||||
|
@ -2200,7 +2200,7 @@ func (svc *MDMAppleCheckinAndCommandService) Authenticate(r *mdm.Request, m *mdm
|
||||
if err := svc.ds.IngestMDMAppleDeviceFromCheckin(r.Context, host); err != nil {
|
||||
return ctxerr.Wrap(r.Context, err, "ingesting device in Authenticate message")
|
||||
}
|
||||
if err := svc.ds.ResetMDMAppleNanoEnrollment(r.Context, host.UDID); err != nil {
|
||||
if err := svc.ds.ResetMDMAppleEnrollment(r.Context, host.UDID); err != nil {
|
||||
return ctxerr.Wrap(r.Context, err, "resetting nano enrollment info in Authenticate message")
|
||||
}
|
||||
info, err := svc.ds.GetHostMDMCheckinInfo(r.Context, m.Enrollment.UDID)
|
||||
|
@ -975,7 +975,7 @@ func TestMDMAuthenticate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ds.ResetMDMAppleNanoEnrollmentFunc = func(ctx context.Context, hostUUID string) error {
|
||||
ds.ResetMDMAppleEnrollmentFunc = func(ctx context.Context, hostUUID string) error {
|
||||
require.Equal(t, uuid, hostUUID)
|
||||
return nil
|
||||
}
|
||||
@ -994,7 +994,7 @@ func TestMDMAuthenticate(t *testing.T) {
|
||||
require.True(t, ds.IngestMDMAppleDeviceFromCheckinFuncInvoked)
|
||||
require.True(t, ds.GetHostMDMCheckinInfoFuncInvoked)
|
||||
require.True(t, ds.NewActivityFuncInvoked)
|
||||
require.True(t, ds.ResetMDMAppleNanoEnrollmentFuncInvoked)
|
||||
require.True(t, ds.ResetMDMAppleEnrollmentFuncInvoked)
|
||||
}
|
||||
|
||||
func TestMDMTokenUpdate(t *testing.T) {
|
||||
|
@ -790,11 +790,21 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
|
||||
<-ch
|
||||
}
|
||||
|
||||
// add global profiles
|
||||
globalProfile := mobileconfigForTest("N1", "I1")
|
||||
s.Do("POST", "/api/v1/fleet/mdm/apple/profiles/batch", batchSetMDMAppleProfilesRequest{Profiles: [][]byte{globalProfile}}, http.StatusNoContent)
|
||||
|
||||
checkPostEnrollmentCommands := func(mdmDevice *mdmtest.TestMDMClient, shouldReceive bool) {
|
||||
// run the worker to process the DEP enroll request
|
||||
s.runWorker()
|
||||
// run the worker to assign configuration profiles
|
||||
ch := make(chan bool)
|
||||
s.onProfileScheduleDone = func() { close(ch) }
|
||||
_, err := s.profileSchedule.Trigger()
|
||||
require.NoError(t, err)
|
||||
<-ch
|
||||
|
||||
var fleetdCmd *micromdm.CommandPayload
|
||||
var fleetdCmd, installProfileCmd *micromdm.CommandPayload
|
||||
cmd, err := mdmDevice.Idle()
|
||||
require.NoError(t, err)
|
||||
for cmd != nil {
|
||||
@ -802,16 +812,24 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
|
||||
cmd.Command.InstallEnterpriseApplication.ManifestURL != nil &&
|
||||
strings.Contains(*cmd.Command.InstallEnterpriseApplication.ManifestURL, apple_mdm.FleetdPublicManifestURL) {
|
||||
fleetdCmd = cmd
|
||||
} else if cmd.Command.RequestType == "InstallProfile" {
|
||||
installProfileCmd = cmd
|
||||
}
|
||||
cmd, err = mdmDevice.Acknowledge(cmd.CommandUUID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if shouldReceive {
|
||||
require.NotNil(t, fleetdCmd)
|
||||
require.NotNil(t, fleetdCmd.Command)
|
||||
// received request to install fleetd
|
||||
require.NotNil(t, fleetdCmd, "host didn't get a command to install fleetd")
|
||||
require.NotNil(t, fleetdCmd.Command, "host didn't get a command to install fleetd")
|
||||
|
||||
// received request to install the global configuration profile
|
||||
require.NotNil(t, installProfileCmd, "host didn't get a command to install profiles")
|
||||
require.NotNil(t, installProfileCmd.Command, "host didn't get a command to install profiles")
|
||||
} else {
|
||||
require.Nil(t, fleetdCmd)
|
||||
require.Nil(t, fleetdCmd, "host got a command to install fleetd")
|
||||
require.Nil(t, installProfileCmd, "host got a command to install profiles")
|
||||
}
|
||||
}
|
||||
|
||||
@ -887,7 +905,7 @@ func (s *integrationMDMTestSuite) TestDEPProfileAssignment() {
|
||||
err := mdmDevice.Enroll()
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure the host gets a request to install fleetd
|
||||
// make sure the host gets post enrollment requests
|
||||
checkPostEnrollmentCommands(mdmDevice, true)
|
||||
|
||||
// only one shows up as pending
|
||||
|
Loading…
Reference in New Issue
Block a user