-

+
-
Consolidate your security stack
-
Consolidate your point vulnerability solution with your cybersecurity asset management and log capture tools.
+
Untangle your security stack
+
Use open data and APIs to connect your point vulnerability solution with your cybersecurity asset management and log capture tools.
Prevent duplicated, inaccurate CMDBs to reduce tool sprawl and wasted budget
Normalize asset management data and software inventories from multiple tools and operating systems
From 1a679d0882dece90d20b1828cfb810402c59a6cb Mon Sep 17 00:00:00 2001
From: Victor Lyuboslavsky
Date: Thu, 29 Feb 2024 10:39:27 -0600
Subject: [PATCH 3/5] Moving `context.WithoutCancel` outside defer. (#17260)
#17197
Fixing orphaned live queries when context is canceled
Co-authored-by: Lucas Rodriguez
---
server/service/campaigns.go | 10 ++++-----
server/service/live_queries.go | 39 ++++++++++++++++++++++------------
2 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/server/service/campaigns.go b/server/service/campaigns.go
index efa82fbf89..cba57bd04e 100644
--- a/server/service/campaigns.go
+++ b/server/service/campaigns.go
@@ -156,17 +156,17 @@ func (svc *Service) NewDistributedQueryCampaign(ctx context.Context, queryString
}
}
- err = svc.liveQueryStore.RunQuery(strconv.Itoa(int(campaign.ID)), queryString, hostIDs)
- if err != nil {
- return nil, ctxerr.Wrap(ctx, err, "run query")
- }
-
// Metrics are used for total hosts targeted for the activity feed.
campaign.Metrics, err = svc.ds.CountHostsInTargets(ctx, filter, targets, time.Now())
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "counting hosts")
}
+ err = svc.liveQueryStore.RunQuery(strconv.Itoa(int(campaign.ID)), queryString, hostIDs)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "run query")
+ }
+
return campaign, nil
}
diff --git a/server/service/live_queries.go b/server/service/live_queries.go
index 7bbe6a79b1..11c46457b5 100644
--- a/server/service/live_queries.go
+++ b/server/service/live_queries.go
@@ -263,27 +263,30 @@ func (svc *Service) RunLiveQueryDeadline(
queryIDPtr = nil
queryString = query
}
+
campaign, err := svc.NewDistributedQueryCampaign(ctx, queryString, queryIDPtr, fleet.HostTargets{HostIDs: hostIDs})
if err != nil {
+ level.Error(svc.logger).Log(
+ "msg", "new distributed query campaign",
+ "queryString", queryString,
+ "queryID", queryID,
+ "err", err,
+ )
resultsCh <- fleet.QueryCampaignResult{QueryID: queryID, Error: ptr.String(err.Error()), Err: err}
return
}
queryID = campaign.QueryID
- readChan, cancelFunc, err := svc.GetCampaignReader(ctx, campaign)
- if err != nil {
- resultsCh <- fleet.QueryCampaignResult{QueryID: queryID, Error: ptr.String(err.Error()), Err: err}
- return
- }
- defer cancelFunc()
-
+ // We do not want to use the outer `ctx` directly because we want to cleanup the campaign
+ // even if the outer `ctx` is canceled (e.g. a client terminating the connection).
+ // Also, we make sure stats and activity DB operations don't get killed after we return results.
+ ctxWithoutCancel := context.WithoutCancel(ctx)
defer func() {
- // We do not want to use the outer `ctx` directly because we want to cleanup the campaign
- // even if the outer `ctx` is canceled (e.g. a client terminating the connection).
- ctx := context.WithoutCancel(ctx)
- err := svc.CompleteCampaign(ctx, campaign)
+ err := svc.CompleteCampaign(ctxWithoutCancel, campaign)
if err != nil {
- level.Error(svc.logger).Log("msg", "completing campaign (sync)", "query.id", campaign.QueryID, "err", err)
+ level.Error(svc.logger).Log(
+ "msg", "completing campaign (sync)", "query.id", campaign.QueryID, "campaign.id", campaign.ID, "err", err,
+ )
resultsCh <- fleet.QueryCampaignResult{
QueryID: queryID,
Error: ptr.String(err.Error()),
@@ -292,6 +295,16 @@ func (svc *Service) RunLiveQueryDeadline(
}
}()
+ readChan, cancelFunc, err := svc.GetCampaignReader(ctx, campaign)
+ if err != nil {
+ level.Error(svc.logger).Log(
+ "msg", "get campaign reader", "query.id", campaign.QueryID, "campaign.id", campaign.ID, "err", err,
+ )
+ resultsCh <- fleet.QueryCampaignResult{QueryID: queryID, Error: ptr.String(err.Error()), Err: err}
+ return
+ }
+ defer cancelFunc()
+
var results []fleet.QueryResult
timeout := time.After(deadline)
@@ -305,8 +318,6 @@ func (svc *Service) RunLiveQueryDeadline(
level.Error(svc.logger).Log("msg", "error checking saved query", "query.id", campaign.QueryID, "err", err)
perfStatsTracker.saveStats = false
}
- // to make sure stats and activity DB operations don't get killed after we return results.
- ctxWithoutCancel := context.WithoutCancel(ctx)
totalHosts := campaign.Metrics.TotalHosts
// We update aggregated stats and activity at the end asynchronously.
defer func() {
From 3f1ade51a0e33d4d32e404df6300afea5603b035 Mon Sep 17 00:00:00 2001
From: Luke Heath
Date: Thu, 29 Feb 2024 11:02:17 -0600
Subject: [PATCH 4/5] Add myself to product groups handbook maintainers
(#17239)
---
CODEOWNERS | 19 ++++++++++---------
.../get-is-pr-preapproved.js | 2 +-
website/config/custom.js | 3 ++-
3 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/CODEOWNERS b/CODEOWNERS
index 52f1680b77..eba3b36a49 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -84,15 +84,16 @@ go.mod @fleetdm/go
#
# (see website/config/custom.js for DRIs of other paths not listed here)
##############################################################################################
-/handbook/company @mikermcneil
-/handbook/README.md @mikermcneil
-/handbook/business-operations @sampfluger88
-/handbook/digital-experience @sampfluger88
-/handbook/customer-success @sampfluger88
-/handbook/demand @sampfluger88
-/handbook/engineering @sampfluger88
-/handbook/sales @sampfluger88
-/handbook/product-design @sampfluger88
+/handbook/company @mikermcneil
+/handbook/README.md @mikermcneil
+/handbook/business-operations @sampfluger88
+/handbook/digital-experience @sampfluger88
+/handbook/customer-success @sampfluger88
+/handbook/demand @sampfluger88
+/handbook/engineering @sampfluger88 @lukeheath
+/handbook/sales @sampfluger88
+/handbook/product-design @sampfluger88
+/handbook/company/product-groups @sampfluger88 @lukeheath
##############################################################################################
# 🦿 GitHub issue templates
diff --git a/website/api/helpers/github-automations/get-is-pr-preapproved.js b/website/api/helpers/github-automations/get-is-pr-preapproved.js
index 8cb8cd281a..1c90da1f16 100644
--- a/website/api/helpers/github-automations/get-is-pr-preapproved.js
+++ b/website/api/helpers/github-automations/get-is-pr-preapproved.js
@@ -39,7 +39,7 @@ module.exports = {
MAINTAINERS_BY_PATH = sails.config.custom.confidentialGithubRepoMaintainersByPath;
}
- if (repo === 'fleet-mdm-gitops') {
+ if (repo === 'fleet-gitops') {
MAINTAINERS_BY_PATH = sails.config.custom.fleetMdmGitopsGithubRepoMaintainersByPath;
}
diff --git a/website/config/custom.js b/website/config/custom.js
index 2937ef2590..83ae5b768f 100644
--- a/website/config/custom.js
+++ b/website/config/custom.js
@@ -221,9 +221,10 @@ module.exports.custom = {
// Handbook
'handbook/README.md': 'mikermcneil', // See https://github.com/fleetdm/fleet/pull/13195
'handbook/company': 'mikermcneil',
+ 'handbook/company/product-groups': ['lukeheath', 'sampfluger88','mikermcneil'],
'handbook/digital-experience': ['sampfluger88','mikermcneil'],
'handbook/business-operations': ['sampfluger88','mikermcneil'],
- 'handbook/engineering': ['sampfluger88','mikermcneil'],
+ 'handbook/engineering': ['sampfluger88','mikermcneil', 'lukeheath'],
'handbook/product-design': ['sampfluger88','mikermcneil'],
'handbook/sales': ['sampfluger88','mikermcneil'],
'handbook/demand': ['sampfluger88','mikermcneil'],
From 0c08ec30f884f691105568498b45eb2a61c25978 Mon Sep 17 00:00:00 2001
From: Roberto Dip
Date: Thu, 29 Feb 2024 14:19:17 -0300
Subject: [PATCH 5/5] display disk encryption as 'verifying' while we check the
key (#17259)
for #16593
---
changes/16593-disk-encryption-verifying | 1 +
server/datastore/mysql/apple_mdm.go | 350 +++++++++++------------
server/datastore/mysql/apple_mdm_test.go | 22 +-
server/datastore/mysql/hosts.go | 38 ++-
server/datastore/mysql/labels.go | 6 +-
server/fleet/hosts.go | 13 +-
server/service/hosts_test.go | 4 +-
server/service/integration_mdm_test.go | 6 +-
8 files changed, 230 insertions(+), 210 deletions(-)
create mode 100644 changes/16593-disk-encryption-verifying
diff --git a/changes/16593-disk-encryption-verifying b/changes/16593-disk-encryption-verifying
new file mode 100644
index 0000000000..44e531026d
--- /dev/null
+++ b/changes/16593-disk-encryption-verifying
@@ -0,0 +1 @@
+* Display disk encryption status in macOS as "verifying" while Fleet verifies if the escrowed key can be decrypted.
diff --git a/server/datastore/mysql/apple_mdm.go b/server/datastore/mysql/apple_mdm.go
index 8f81f61e03..bc56a327be 100644
--- a/server/datastore/mysql/apple_mdm.go
+++ b/server/datastore/mysql/apple_mdm.go
@@ -1960,186 +1960,179 @@ func (ds *Datastore) UpdateOrDeleteHostMDMAppleProfile(ctx context.Context, prof
return err
}
-func subqueryHostsMacOSSettingsStatusFailed() (string, []interface{}) {
- sql := `
- SELECT
- 1 FROM host_mdm_apple_profiles hmap
- WHERE
- h.uuid = hmap.host_uuid
- AND hmap.status = ?`
- args := []interface{}{fleet.MDMDeliveryFailed}
+const (
+ appleMDMFailedProfilesStmt = `
+ h.uuid = hmap.host_uuid AND
+ hmap.status = :failed`
- return sql, args
-}
+ appleMDMPendingProfilesStmt = `
+ h.uuid = hmap.host_uuid AND
+ (
+ hmap.status IS NULL OR
+ hmap.status = :pending OR
+ -- special case for filevault, it's pending if the profile is
+ -- pending OR the profile is verified or verifying but we still
+ -- don't have an encryption key.
+ (
+ hmap.profile_identifier = :filevault AND
+ hmap.status IN (:verifying, :verified) AND
+ hmap.operation_type = :install AND
+ NOT EXISTS (
+ SELECT 1
+ FROM host_disk_encryption_keys hdek
+ WHERE h.id = hdek.host_id AND
+ (hdek.decryptable = 1 OR hdek.decryptable IS NULL)
+ )
+ )
+ )`
-func subqueryHostsMacOSSettingsStatusPending() (string, []interface{}) {
- sql := `
- SELECT
- 1 FROM host_mdm_apple_profiles hmap
- WHERE
- h.uuid = hmap.host_uuid
- AND (hmap.status IS NULL
- OR hmap.status = ?
- OR(hmap.profile_identifier = ?
- AND hmap.status IN (?, ?)
- AND hmap.operation_type = ?
- AND NOT EXISTS (
- SELECT
- 1 FROM host_disk_encryption_keys hdek
- WHERE
- h.id = hdek.host_id
- AND hdek.decryptable = 1)))
- AND NOT EXISTS (
- SELECT
- 1 FROM host_mdm_apple_profiles hmap2
- WHERE
- h.uuid = hmap2.host_uuid
- AND hmap2.status = ?)`
- args := []interface{}{
- fleet.MDMDeliveryPending,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
- fleet.MDMOperationTypeInstall,
- fleet.MDMDeliveryFailed,
+ appleMDMVerifyingProfilesStmt = `
+ h.uuid = hmap.host_uuid AND
+ hmap.operation_type = :install AND
+ (
+ -- all profiles except filevault that are 'verifying'
+ (
+ hmap.profile_identifier != :filevault AND
+ hmap.status = :verifying
+ )
+ OR
+ -- special cases for filevault
+ (
+ hmap.profile_identifier = :filevault AND
+ (
+ -- filevault profile is verified, but we didn't verify the encryption key
+ (
+ hmap.status = :verified AND
+ EXISTS (
+ SELECT 1
+ FROM host_disk_encryption_keys AS hdek
+ WHERE h.id = hdek.host_id AND
+ hdek.decryptable IS NULL
+ )
+ )
+ OR
+ -- filevault profile is verifying, and we already have an encryption key, in any state
+ (
+ hmap.status = :verifying AND
+ EXISTS (
+ SELECT 1
+ FROM host_disk_encryption_keys AS hdek
+ WHERE h.id = hdek.host_id AND
+ hdek.decryptable = 1 OR hdek.decryptable IS NULL
+ )
+ )
+ )
+ )
+ )`
+
+ appleVerifiedProfilesStmt = `
+ h.uuid = hmap.host_uuid AND
+ hmap.operation_type = :install AND
+ hmap.status = :verified AND
+ (
+ hmap.profile_identifier != :filevault OR
+ EXISTS (
+ SELECT 1
+ FROM host_disk_encryption_keys hdek
+ WHERE h.id = hdek.host_id AND
+ hdek.decryptable = 1
+ )
+ )`
+)
+
+// subqueryAppleProfileStatus builds the right subquery that can be used to
+// filter hosts based on their profile status.
+//
+// The subquery mechanism works by finding profiles for hosts that:
+// - match with the provided status
+// - match any status that supercedes the provided status (eg: failed supercedes verifying)
+//
+// Hosts will be considered to be in the given status only if the profiles
+// match the given status and zero profiles match any superceding status.
+func subqueryAppleProfileStatus(status fleet.MDMDeliveryStatus) (string, []any, error) {
+ var condition string
+ var excludeConditions string
+ switch status {
+ case fleet.MDMDeliveryFailed:
+ condition = appleMDMFailedProfilesStmt
+ excludeConditions = "FALSE"
+ case fleet.MDMDeliveryPending:
+ condition = appleMDMPendingProfilesStmt
+ excludeConditions = appleMDMFailedProfilesStmt
+ case fleet.MDMDeliveryVerifying:
+ condition = appleMDMVerifyingProfilesStmt
+ excludeConditions = fmt.Sprintf("(%s) OR (%s)", appleMDMPendingProfilesStmt, appleMDMFailedProfilesStmt)
+ case fleet.MDMDeliveryVerified:
+ condition = appleVerifiedProfilesStmt
+ excludeConditions = fmt.Sprintf("(%s) OR (%s) OR (%s)", appleMDMPendingProfilesStmt, appleMDMFailedProfilesStmt, appleMDMVerifyingProfilesStmt)
+ default:
+ return "", nil, fmt.Errorf("invalid status: %s", status)
}
- return sql, args
-}
-func subqueryHostsMacOSSetttingsStatusVerifying() (string, []interface{}) {
- sql := `
- SELECT
- 1 FROM host_mdm_apple_profiles hmap
- WHERE
- h.uuid = hmap.host_uuid
- AND hmap.operation_type = ?
- AND hmap.status = ?
- AND(hmap.profile_identifier != ?
- OR EXISTS (
- SELECT
- 1 FROM host_disk_encryption_keys hdek
- WHERE
- h.id = hdek.host_id
- AND hdek.decryptable = 1))
- AND NOT EXISTS (
- SELECT
- 1 FROM host_mdm_apple_profiles hmap2
- WHERE (h.uuid = hmap2.host_uuid
- AND hmap2.operation_type = ?
- AND(hmap2.status IS NULL
- OR hmap2.status NOT IN(?, ?)
- OR(hmap2.profile_identifier = ?
- AND hmap2.status IN(?, ?)
- AND NOT EXISTS (
- SELECT
- 1 FROM host_disk_encryption_keys hdek
- WHERE
- h.id = hdek.host_id
- AND hdek.decryptable = 1))))
- OR(h.uuid = hmap2.host_uuid
- AND hmap2.operation_type = ?
- AND(hmap2.status IS NULL
- OR hmap2.status NOT IN(?, ?))))`
+ sql := fmt.Sprintf(`
+ SELECT 1
+ FROM host_mdm_apple_profiles hmap
+ WHERE %s AND
+ NOT EXISTS (
+ SELECT 1
+ FROM host_mdm_apple_profiles hmap
+ WHERE %s
+ )`, condition, excludeConditions)
- args := []interface{}{
- fleet.MDMOperationTypeInstall,
- fleet.MDMDeliveryVerifying,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMOperationTypeInstall,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
- fleet.MDMOperationTypeRemove,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
+ arg := map[string]any{
+ "install": fleet.MDMOperationTypeInstall,
+ "remove": fleet.MDMOperationTypeRemove,
+ "verifying": fleet.MDMDeliveryVerifying,
+ "failed": fleet.MDMDeliveryFailed,
+ "verified": fleet.MDMDeliveryVerified,
+ "pending": fleet.MDMDeliveryPending,
+ "filevault": mobileconfig.FleetFileVaultPayloadIdentifier,
}
- return sql, args
-}
-
-func subqueryHostsMacOSSetttingsStatusVerified() (string, []interface{}) {
- sql := `
- SELECT
- 1 FROM host_mdm_apple_profiles hmap
- WHERE
- h.uuid = hmap.host_uuid
- AND hmap.operation_type = ?
- AND hmap.status = ?
- AND(hmap.profile_identifier != ?
- OR EXISTS (
- SELECT
- 1 FROM host_disk_encryption_keys hdek
- WHERE
- h.id = hdek.host_id
- AND hdek.decryptable = 1))
- AND NOT EXISTS (
- SELECT
- 1 FROM host_mdm_apple_profiles hmap2
- WHERE (h.uuid = hmap2.host_uuid
- AND hmap2.operation_type = ?
- AND (hmap2.status IS NULL
- OR hmap2.status != ?
- OR(hmap2.profile_identifier = ?
- AND hmap2.status = ?
- AND NOT EXISTS (
- SELECT
- 1 FROM host_disk_encryption_keys hdek
- WHERE
- h.id = hdek.host_id
- AND hdek.decryptable = 1))))
- OR(h.uuid = hmap2.host_uuid
- AND hmap2.operation_type = ?
- AND (hmap2.status IS NULL
- OR hmap2.status NOT IN(?, ?))))`
- args := []interface{}{
- fleet.MDMOperationTypeInstall,
- fleet.MDMDeliveryVerified,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMOperationTypeInstall,
- fleet.MDMDeliveryVerified,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMDeliveryVerified,
- fleet.MDMOperationTypeRemove,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
+ query, args, err := sqlx.Named(sql, arg)
+ if err != nil {
+ return "", nil, fmt.Errorf("subqueryAppleProfileStatus %s: %w", status, err)
}
- return sql, args
+
+ return query, args, nil
}
func (ds *Datastore) GetMDMAppleProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
var args []interface{}
- subqueryFailed, subqueryFailedArgs := subqueryHostsMacOSSettingsStatusFailed()
+
+ subqueryFailed, subqueryFailedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "building failed subquery")
+ }
args = append(args, subqueryFailedArgs...)
- subqueryPending, subqueryPendingArgs := subqueryHostsMacOSSettingsStatusPending()
+
+ subqueryPending, subqueryPendingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "building pending subquery")
+ }
args = append(args, subqueryPendingArgs...)
- subqueryVerifying, subqueryVeryingingArgs := subqueryHostsMacOSSetttingsStatusVerifying()
- args = append(args, subqueryVeryingingArgs...)
- subqueryVerified, subqueryVerifiedArgs := subqueryHostsMacOSSetttingsStatusVerified()
+
+ subqueryVerifying, subqueryVerifyingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "building verifying subquery")
+ }
+ args = append(args, subqueryVerifyingArgs...)
+
+ subqueryVerified, subqueryVerifiedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "building verified subquery")
+ }
args = append(args, subqueryVerifiedArgs...)
sqlFmt := `
-SELECT
- COUNT(
- CASE WHEN EXISTS (%s)
- THEN 1
- END) AS failed,
- COUNT(
- CASE WHEN EXISTS (%s)
- THEN 1
- END) AS pending,
- COUNT(
- CASE WHEN EXISTS (%s)
- THEN 1
- END) AS verifying,
- COUNT(
- CASE WHEN EXISTS (%s)
- THEN 1
- END) AS verified
-FROM
- hosts h
-WHERE
- h.platform = 'darwin' AND %s`
+ SELECT
+ COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS failed,
+ COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS pending,
+ COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS verifying,
+ COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS verified
+ FROM
+ hosts h
+ WHERE
+ h.platform = 'darwin' AND %s`
teamFilter := "h.team_id IS NULL"
if teamID != nil && *teamID > 0 {
@@ -2148,9 +2141,8 @@ WHERE
}
stmt := fmt.Sprintf(sqlFmt, subqueryFailed, subqueryPending, subqueryVerifying, subqueryVerified, teamFilter)
-
var res fleet.MDMProfilesSummary
- err := sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, args...)
+ err = sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, args...)
if err != nil {
return nil, err
}
@@ -2204,14 +2196,18 @@ func subqueryFileVaultVerifying() (string, []interface{}) {
1 FROM host_mdm_apple_profiles hmap
WHERE
h.uuid = hmap.host_uuid
- AND hdek.decryptable = 1
AND hmap.profile_identifier = ?
- AND hmap.status = ?
- AND hmap.operation_type = ?`
+ AND hmap.operation_type = ?
+ AND (
+ (hmap.status = ? AND hdek.decryptable IS NULL)
+ OR
+ (hmap.status = ? AND hdek.decryptable = 1)
+ )`
args := []interface{}{
mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMDeliveryVerifying,
fleet.MDMOperationTypeInstall,
+ fleet.MDMDeliveryVerified,
+ fleet.MDMDeliveryVerifying,
}
return sql, args
}
@@ -2263,23 +2259,11 @@ func subqueryFileVaultEnforcing() (string, []interface{}) {
AND hmap.profile_identifier = ?
AND (hmap.status IS NULL OR hmap.status = ?)
AND hmap.operation_type = ?
- UNION SELECT
- 1 FROM host_mdm_apple_profiles hmap
- WHERE
- h.uuid = hmap.host_uuid
- AND hmap.profile_identifier = ?
- AND (hmap.status IS NOT NULL AND (hmap.status = ? OR hmap.status = ?))
- AND hmap.operation_type = ?
- AND hdek.decryptable IS NULL
- AND hdek.host_id IS NOT NULL`
+ `
args := []interface{}{
mobileconfig.FleetFileVaultPayloadIdentifier,
fleet.MDMDeliveryPending,
fleet.MDMOperationTypeInstall,
- mobileconfig.FleetFileVaultPayloadIdentifier,
- fleet.MDMDeliveryVerifying,
- fleet.MDMDeliveryVerified,
- fleet.MDMOperationTypeInstall,
}
return sql, args
}
diff --git a/server/datastore/mysql/apple_mdm_test.go b/server/datastore/mysql/apple_mdm_test.go
index ec1e0b134d..ed3da41248 100644
--- a/server/datastore/mysql/apple_mdm_test.go
+++ b/server/datastore/mysql/apple_mdm_test.go
@@ -1769,9 +1769,11 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore)
res, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
require.NoError(t, err)
require.NotNil(t, res)
- require.Equal(t, uint(len(hosts)), res.Pending) // still pending because disk encryption key decryptable is not set
+ // hosts still pending because disk encryption key decryptable is not set
+ require.Equal(t, uint(len(hosts)-1), res.Pending)
require.Equal(t, uint(0), res.Failed)
- require.Equal(t, uint(0), res.Verifying)
+ // one host is verifying because the disk is encrypted and we're verifying the key
+ require.Equal(t, uint(1), res.Verifying)
require.Equal(t, uint(0), res.Verified)
err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{hosts[0].ID}, false, time.Now().Add(1*time.Hour))
@@ -2434,7 +2436,13 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
// verifying status
verifyingHost := hosts[0]
- upsertHostCPs([]*fleet.Host{verifyingHost}, []*fleet.MDMAppleConfigProfile{noTeamFVProfile}, fleet.MDMOperationTypeInstall, &fleet.MDMDeliveryVerifying, ctx, ds, t)
+ upsertHostCPs(
+ []*fleet.Host{verifyingHost},
+ []*fleet.MDMAppleConfigProfile{noTeamFVProfile},
+ fleet.MDMOperationTypeInstall,
+ &fleet.MDMDeliveryVerifying,
+ ctx, ds, t,
+ )
oneMinuteAfterThreshold := time.Now().Add(+1 * time.Minute)
createDiskEncryptionRecord(ctx, ds, t, verifyingHost.ID, "key-1", true, oneMinuteAfterThreshold)
@@ -2643,7 +2651,13 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
require.Equal(t, uint(0), allProfilesSummary.Verified)
// verified status
- upsertHostCPs([]*fleet.Host{verifyingTeam1Host}, []*fleet.MDMAppleConfigProfile{team1FVProfile}, fleet.MDMOperationTypeInstall, &fleet.MDMDeliveryVerified, ctx, ds, t)
+ upsertHostCPs(
+ []*fleet.Host{verifyingTeam1Host},
+ []*fleet.MDMAppleConfigProfile{team1FVProfile},
+ fleet.MDMOperationTypeInstall,
+ &fleet.MDMDeliveryVerified,
+ ctx, ds, t,
+ )
fvProfileSummary, err = ds.GetMDMAppleFileVaultSummary(ctx, &tm.ID)
require.NoError(t, err)
require.NotNil(t, fvProfileSummary)
diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go
index bc56b2683d..05c2d2f319 100644
--- a/server/datastore/mysql/hosts.go
+++ b/server/datastore/mysql/hosts.go
@@ -1076,7 +1076,11 @@ func (ds *Datastore) applyHostFilters(
sqlStmt, params = filterHostsByTeam(sqlStmt, opt, params)
sqlStmt, params = filterHostsByPolicy(sqlStmt, opt, params)
sqlStmt, params = filterHostsByMDM(sqlStmt, opt, params)
- sqlStmt, params = filterHostsByMacOSSettingsStatus(sqlStmt, opt, params)
+ var err error
+ sqlStmt, params, err = filterHostsByMacOSSettingsStatus(sqlStmt, opt, params)
+ if err != nil {
+ return "", nil, ctxerr.Wrap(ctx, err, "building query to filter macOS settings status")
+ }
sqlStmt, params = filterHostsByMacOSDiskEncryptionStatus(sqlStmt, opt, params)
if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil {
if errors.Is(err, sql.ErrNoRows) {
@@ -1197,9 +1201,9 @@ func filterHostsByStatus(now time.Time, sql string, opt fleet.HostListOptions, p
return sql, params
}
-func filterHostsByMacOSSettingsStatus(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) {
+func filterHostsByMacOSSettingsStatus(sql string, opt fleet.HostListOptions, params []any) (string, []any, error) {
if !opt.MacOSSettingsFilter.IsValid() {
- return sql, params
+ return sql, params, nil
}
newSQL := ""
@@ -1210,22 +1214,26 @@ func filterHostsByMacOSSettingsStatus(sql string, opt fleet.HostListOptions, par
}
var subquery string
- var subqueryParams []interface{}
+ var subqueryParams []any
+ var err error
switch opt.MacOSSettingsFilter {
case fleet.OSSettingsFailed:
- subquery, subqueryParams = subqueryHostsMacOSSettingsStatusFailed()
+ subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
case fleet.OSSettingsPending:
- subquery, subqueryParams = subqueryHostsMacOSSettingsStatusPending()
+ subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
case fleet.OSSettingsVerifying:
- subquery, subqueryParams = subqueryHostsMacOSSetttingsStatusVerifying()
+ subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
case fleet.OSSettingsVerified:
- subquery, subqueryParams = subqueryHostsMacOSSetttingsStatusVerified()
+ subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
+ }
+ if err != nil {
+ return "", nil, fmt.Errorf("building subquery for %s filter: %w", opt.MacOSSettingsFilter, err)
}
if subquery != "" {
newSQL += fmt.Sprintf(` AND EXISTS (%s)`, subquery)
}
- return sql + newSQL, append(params, subqueryParams...)
+ return sql + newSQL, append(params, subqueryParams...), nil
}
func filterHostsByMacOSDiskEncryptionStatus(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) {
@@ -1278,15 +1286,19 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
// construct the WHERE for macOS
var subqueryMacOS string
var paramsMacOS []interface{}
+ var err error
switch opt.OSSettingsFilter {
case fleet.OSSettingsFailed:
- subqueryMacOS, paramsMacOS = subqueryHostsMacOSSettingsStatusFailed()
+ subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
case fleet.OSSettingsPending:
- subqueryMacOS, paramsMacOS = subqueryHostsMacOSSettingsStatusPending()
+ subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
case fleet.OSSettingsVerifying:
- subqueryMacOS, paramsMacOS = subqueryHostsMacOSSetttingsStatusVerifying()
+ subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
case fleet.OSSettingsVerified:
- subqueryMacOS, paramsMacOS = subqueryHostsMacOSSetttingsStatusVerified()
+ subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
+ }
+ if err != nil {
+ return "", nil, fmt.Errorf("building subquery for %s filter: %w", opt.OSSettingsFilter, err)
}
if subqueryMacOS != "" {
whereMacOS = "EXISTS (" + subqueryMacOS + ")"
diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go
index 7cc3fbb58c..c2075b3a5f 100644
--- a/server/datastore/mysql/labels.go
+++ b/server/datastore/mysql/labels.go
@@ -584,10 +584,14 @@ func (ds *Datastore) applyHostLabelFilters(ctx context.Context, filter fleet.Tea
params = append(params, *opt.LowDiskSpaceFilter)
}
+ var err error
query, params = filterHostsByStatus(ds.clock.Now(), query, opt, params)
query, params = filterHostsByTeam(query, opt, params)
query, params = filterHostsByMDM(query, opt, params)
- query, params = filterHostsByMacOSSettingsStatus(query, opt, params)
+ query, params, err = filterHostsByMacOSSettingsStatus(query, opt, params)
+ if err != nil {
+ return "", nil, ctxerr.Wrap(ctx, err, "building macOS settings status filter")
+ }
query, params = filterHostsByMacOSDiskEncryptionStatus(query, opt, params)
query, params = filterHostsByMDMBootstrapPackageStatus(query, opt, params)
if enableDiskEncryption, err := ds.getConfigEnableDiskEncryption(ctx, opt.TeamFilter); err != nil {
diff --git a/server/fleet/hosts.go b/server/fleet/hosts.go
index 8028ce5a35..e483cd555f 100644
--- a/server/fleet/hosts.go
+++ b/server/fleet/hosts.go
@@ -505,10 +505,10 @@ func (d *MDMHostData) PopulateOSSettingsAndMacOSSettings(profiles []HostMDMApple
if d.rawDecryptable != nil && *d.rawDecryptable == 1 {
// if a FileVault profile has been successfully installed on the host
// AND we have fetched and are able to decrypt the key
- switch {
- case *fvprof.Status == MDMDeliveryVerifying:
+ switch *fvprof.Status {
+ case MDMDeliveryVerifying:
settings.DiskEncryption = DiskEncryptionVerifying.addrOf()
- case *fvprof.Status == MDMDeliveryVerified:
+ case MDMDeliveryVerified:
settings.DiskEncryption = DiskEncryptionVerified.addrOf()
}
} else if d.rawDecryptable != nil {
@@ -525,7 +525,12 @@ func (d *MDMHostData) PopulateOSSettingsAndMacOSSettings(profiles []HostMDMApple
// if [a FileVault profile is pending to be installed or] the
// matching row in host_disk_encryption_keys has a field decryptable
// = NULL
- settings.DiskEncryption = DiskEncryptionEnforcing.addrOf()
+ switch *fvprof.Status {
+ case MDMDeliveryVerifying, MDMDeliveryVerified:
+ settings.DiskEncryption = DiskEncryptionVerifying.addrOf()
+ case MDMDeliveryPending:
+ settings.DiskEncryption = DiskEncryptionEnforcing.addrOf()
+ }
}
case fvprof.Status != nil && *fvprof.Status == MDMDeliveryFailed:
diff --git a/server/service/hosts_test.go b/server/service/hosts_test.go
index 6570d8e78d..32c25e1877 100644
--- a/server/service/hosts_test.go
+++ b/server/service/hosts_test.go
@@ -144,9 +144,9 @@ func TestHostDetailsMDMAppleDiskEncryption(t *testing.T) {
Status: &fleet.MDMDeliveryVerifying,
OperationType: fleet.MDMOperationTypeInstall,
},
- fleet.DiskEncryptionEnforcing,
+ fleet.DiskEncryptionVerifying,
"",
- &fleet.MDMDeliveryPending,
+ &fleet.MDMDeliveryVerifying,
},
{
"installed profile, not decryptable",
diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go
index 27f19ff118..1c08a55e88 100644
--- a/server/service/integration_mdm_test.go
+++ b/server/service/integration_mdm_test.go
@@ -2963,15 +2963,15 @@ func (s *integrationMDMTestSuite) TestMDMAppleHostDiskEncryption() {
require.NoError(t, err)
// get that host - it has an encryption key with unknown decryptability, so
- // it should report "enforcing" disk encryption.
+ // it should report "verifying" disk encryption.
getHostResp = getHostResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", host.ID), nil, http.StatusOK, &getHostResp)
require.NotNil(t, getHostResp.Host.MDM.MacOSSettings.DiskEncryption)
- require.Equal(t, fleet.DiskEncryptionEnforcing, *getHostResp.Host.MDM.MacOSSettings.DiskEncryption)
+ require.Equal(t, fleet.DiskEncryptionVerifying, *getHostResp.Host.MDM.MacOSSettings.DiskEncryption)
require.Nil(t, 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.DiskEncryptionEnforcing, *getHostResp.Host.MDM.OSSettings.DiskEncryption.Status)
+ require.Equal(t, fleet.DiskEncryptionVerifying, *getHostResp.Host.MDM.OSSettings.DiskEncryption.Status)
require.Equal(t, "", getHostResp.Host.MDM.OSSettings.DiskEncryption.Detail)
// request with no token