35513: Resend Windows SCEP profile if renewal date is hit (#37184)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #35513 

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [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/guides/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] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Magnus Jensen <magnus@fleetdm.com>
This commit is contained in:
Jordan Montgomery
2025-12-15 09:36:34 -05:00
committed by GitHub
co-authored by Magnus Jensen
parent 229481fc79
commit 9ec74e09e5
5 changed files with 308 additions and 297 deletions
-1
View File
@@ -987,7 +987,6 @@ func newCleanupsAndAggregationSchedule(
},
),
schedule.WithJob("renew_host_mdm_managed_certificates", func(ctx context.Context) error {
// TODO(MHJ): Move this datastore method to shared space, for when windows renewal is being worked on.
return ds.RenewMDMManagedCertificates(ctx)
}),
schedule.WithJob("query_results_cleanup", func(ctx context.Context) error {
-71
View File
@@ -830,77 +830,6 @@ func (ds *Datastore) GetAppleHostMDMCertificateProfile(ctx context.Context, host
return &profile, nil
}
// RenewMDMManagedCertificates marks managed certificate profiles for resend when renewal is required
func (ds *Datastore) RenewMDMManagedCertificates(ctx context.Context) error {
// This will trigger a resend next time profiles are checked
updateQuery := `UPDATE host_mdm_apple_profiles SET status = NULL WHERE status IS NOT NULL AND operation_type = ? AND (`
hostProfileClause := ``
values := []interface{}{fleet.MDMOperationTypeInstall}
totalHostCertsToRenew := 0
hostCertTypesToRenew := fleet.ListCATypesWithRenewalSupport()
for _, hostCertType := range hostCertTypesToRenew {
hostCertsToRenew := []struct {
HostUUID string `db:"host_uuid"`
ProfileUUID string `db:"profile_uuid"`
NotValidAfter time.Time `db:"not_valid_after"`
ValidityPeriod int `db:"validity_period"`
}{}
// Fetch all MDM Managed certificates of the given type that aren't already queued for
// resend(hmap.status=null) and which
// * Have a validity period > 30 days and are expiring in the next 30 days
// * Have a validity period <= 30 days and are within half the validity period of expiration
// nb: we SELECT not_valid_after and validity_period here so we can use them in the HAVING clause, but
// we don't actually need them for the update logic.
err := sqlx.SelectContext(ctx, ds.reader(ctx), &hostCertsToRenew, `
SELECT
hmmc.host_uuid,
hmmc.profile_uuid,
hmmc.not_valid_after,
DATEDIFF(hmmc.not_valid_after, hmmc.not_valid_before) AS validity_period
FROM
host_mdm_managed_certificates hmmc
INNER JOIN
host_mdm_apple_profiles hmap
ON hmmc.host_uuid = hmap.host_uuid AND hmmc.profile_uuid = hmap.profile_uuid
WHERE
hmmc.type = ? AND hmap.status IS NOT NULL AND hmap.operation_type = ?
HAVING
validity_period IS NOT NULL AND
((validity_period > 30 AND not_valid_after < DATE_ADD(NOW(), INTERVAL 30 DAY)) OR
(validity_period <= 30 AND not_valid_after < DATE_ADD(NOW(), INTERVAL validity_period/2 DAY)))
LIMIT 1000`, hostCertType, fleet.MDMOperationTypeInstall)
if err != nil {
return ctxerr.Wrap(ctx, err, "retrieving mdm managed certificates to renew")
}
if len(hostCertsToRenew) == 0 {
level.Debug(ds.logger).Log("msg", "No "+hostCertType+" certificates to renew")
continue
}
totalHostCertsToRenew += len(hostCertsToRenew)
for _, hostCertToRenew := range hostCertsToRenew {
hostProfileClause += `(host_uuid = ? AND profile_uuid = ?) OR `
values = append(values, hostCertToRenew.HostUUID, hostCertToRenew.ProfileUUID)
}
}
if totalHostCertsToRenew == 0 {
return nil
}
hostProfileClause = strings.TrimSuffix(hostProfileClause, " OR ")
level.Debug(ds.logger).Log("msg", "Renewing MDM managed digicert/SCEP certificates", "len(hostCertsToRenew)", totalHostCertsToRenew)
err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
_, err := tx.ExecContext(ctx, updateQuery+hostProfileClause+")", values...)
if err != nil {
return ctxerr.Wrap(ctx, err, "updating mdm managed certificates to renew")
}
return nil
})
return err
}
// ResendHostCertificateProfile marks the given profile UUID to be resent to the host with the given UUID. It
// also deactivates prior nano commands and resets the retry counter for the profile UUID and host UUID.
//
+86
View File
@@ -2731,6 +2731,92 @@ func (ds *Datastore) ListHostMDMManagedCertificates(ctx context.Context, hostUUI
return hostCertsToRenew, ctxerr.Wrap(ctx, err, "get mdm managed certificates for host")
}
// RenewMDMManagedCertificates marks managed certificate profiles for resend when renewal is required
func (ds *Datastore) RenewMDMManagedCertificates(ctx context.Context) error {
totalHostCertsToRenew := 0
hostCertTypesToRenew := fleet.ListCATypesWithRenewalSupport()
// Map is used to take advantage of Go map iteration order randomization so that
// if a customer is issuing certs across multiple platforms we will not bias renewals
// toward a specific platform
hostProfileTables := map[string]string{
"apple": "host_mdm_apple_profiles",
"windows": "host_mdm_windows_profiles",
}
for _, hostCertType := range hostCertTypesToRenew {
// Limit to 1000 renewals per CA type per run across all platforms
limit := 1000
for hostPlatform, table := range hostProfileTables {
if limit == 0 {
level.Debug(ds.logger).Log("msg", "Skipping check of %s certificates on %s hosts to renew - limit exceeded by prior platform", hostCertType, hostPlatform)
continue
}
// This will trigger a resend next time profiles are checked
updateQuery := `UPDATE ` + table + ` SET status = NULL WHERE status IS NOT NULL AND operation_type = ? AND (`
hostProfileClause := ``
values := []any{fleet.MDMOperationTypeInstall}
hostCertsToRenew := []struct {
HostUUID string `db:"host_uuid"`
ProfileUUID string `db:"profile_uuid"`
NotValidAfter time.Time `db:"not_valid_after"`
ValidityPeriod int `db:"validity_period"`
}{}
// Fetch all MDM Managed certificates of the given type that aren't already queued for
// resend(hmap.status=null) and which
// * Have a validity period > 30 days and are expiring in the next 30 days
// * Have a validity period <= 30 days and are within half the validity period of expiration
// nb: we SELECT not_valid_after and validity_period here so we can use them in the HAVING clause, but
// we don't actually need them for the update logic.
err := sqlx.SelectContext(ctx, ds.reader(ctx), &hostCertsToRenew, `
SELECT
hmmc.host_uuid,
hmmc.profile_uuid,
hmmc.not_valid_after,
DATEDIFF(hmmc.not_valid_after, hmmc.not_valid_before) AS validity_period
FROM
host_mdm_managed_certificates hmmc
INNER JOIN
`+table+` hp
ON hmmc.host_uuid = hp.host_uuid AND hmmc.profile_uuid = hp.profile_uuid
WHERE
hmmc.type = ? AND hp.status IS NOT NULL AND hp.operation_type = ?
HAVING
validity_period IS NOT NULL AND
((validity_period > 30 AND not_valid_after < DATE_ADD(NOW(), INTERVAL 30 DAY)) OR
(validity_period <= 30 AND not_valid_after < DATE_ADD(NOW(), INTERVAL validity_period/2 DAY)))
LIMIT ?`, hostCertType, fleet.MDMOperationTypeInstall, limit)
if err != nil {
return ctxerr.Wrap(ctx, err, "retrieving mdm managed certificates to renew")
}
if len(hostCertsToRenew) == 0 {
level.Debug(ds.logger).Log("msg", "No %s certificates on %s hosts to renew", hostCertType, hostPlatform)
continue
}
limit -= len(hostCertsToRenew)
totalHostCertsToRenew += len(hostCertsToRenew)
for _, hostCertToRenew := range hostCertsToRenew {
hostProfileClause += `(host_uuid = ? AND profile_uuid = ?) OR `
values = append(values, hostCertToRenew.HostUUID, hostCertToRenew.ProfileUUID)
}
hostProfileClause = strings.TrimSuffix(hostProfileClause, " OR ")
level.Info(ds.logger).Log("msg", "Renewing MDM managed certificates", "len", len(hostCertsToRenew), "type", hostCertType, "platform", hostPlatform)
err = ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
_, err := tx.ExecContext(ctx, updateQuery+hostProfileClause+")", values...)
if err != nil {
return ctxerr.Wrap(ctx, err, "updating mdm managed certificates to renew")
}
return nil
})
if err != nil {
return ctxerr.Wrap(ctx, err, "renewing mdm managed certificates")
}
}
}
return nil
}
// GetHostMDMIdentifiers searches for a host by identifier (hostname, uuid, or hardware_serial).
//
// NOTE: We're not using existing methods like ds.whereFilterHostsByIdentifier,
+220 -225
View File
@@ -3199,14 +3199,12 @@ func testWindowsMDMManagedSCEPCertificates(t *testing.T, ds *Datastore) {
assert.Equal(t, caName, profile.CAName)
// Renew should not do anything yet
/*
TODO: See comment below
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryPending, *profile.Status) */
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryPending, *profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
@@ -3215,249 +3213,246 @@ func testWindowsMDMManagedSCEPCertificates(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.NotNil(t, profile)
/*
TODO: Uncomment when adding renewal logic for Custom SCEP on Windows.
serial := "8ABADCAFEF684D6348F5EC95AEFF468F237A9D75"
serial := "8ABADCAFEF684D6348F5EC95AEFF468F237A9D75"
t.Run("Non renewal scenario 1 - validity window > 30 days but not yet time to renew", func(t *testing.T) {
// Set not_valid_before to 1 day in the past and not_valid_after to 31 days in the future so
// teh validity window is 32 days of which there are 31 left which should not trigger renewal
notValidAfter := time.Now().Add(31 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-1 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
t.Run("Non renewal scenario 1 - validity window > 30 days but not yet time to renew", func(t *testing.T) {
// Set not_valid_before to 1 day in the past and not_valid_after to 31 days in the future so
// the validity window is 32 days of which there are 31 left which should not trigger renewal
notValidAfter := time.Now().Add(31 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-1 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_windows_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
})
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
// Renew should not change the MDM delivery status
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Renew should not change the MDM delivery status
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
t.Run("Non renewal scenario 2 - validity window < 30 days but not yet time to renew", func(t *testing.T) {
// Set not_valid_before to 13 days in the past and not_valid_after to 15 days in the future so
// the validity window is 28 days of which there are 15 left which should not trigger renewal
notValidAfter := time.Now().Add(15 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-13 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_apple_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
t.Run("Non renewal scenario 2 - validity window < 30 days but not yet time to renew", func(t *testing.T) {
// Set not_valid_before to 13 days in the past and not_valid_after to 15 days in the future so
// the validity window is 28 days of which there are 15 left which should not trigger renewal
notValidAfter := time.Now().Add(15 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-13 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_windows_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
})
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
// Renew should not change the MDM delivery status
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Renew should not change the MDM delivery status
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
t.Run("Renew scenario 1 - validity window > 30 days", func(t *testing.T) {
// Set not_valid_before to 31 days in the past the validity window becomes 60 days, of which there are
// 29 left which should trigger the first renewal scenario(window > 30 days, renew when < 30
// days left)
notValidAfter := time.Now().Add(29 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-31 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_apple_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
t.Run("Renew scenario 1 - validity window > 30 days", func(t *testing.T) {
// Set not_valid_before to 31 days in the past the validity window becomes 60 days, of which there are
// 29 left which should trigger the first renewal scenario(window > 30 days, renew when < 30
// days left)
notValidAfter := time.Now().Add(29 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().Add(-31 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_windows_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
})
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
// Renew should set the MDM delivery status to "null" so the profile gets resent and the certificate renewed
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.Nil(t, profile.Status)
// Renew should set the MDM delivery status to "null" so the profile gets resent and the certificate renewed
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.Nil(t, profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
t.Run("Renew scenario 2 - validity window < 30 days", func(t *testing.T) {
// Set not_valid_before to 15 days in the past and not_valid_after to 14 days in the future so the
// validity window becomes 29 days, of which there are 14 left which should trigger the second
// renewal scenario(window < 30 days, renew when there is half that time left)
notValidBefore := time.Now().Add(-15 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidAfter := time.Now().Add(14 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
require.NoError(t, err)
t.Run("Renew scenario 2 - validity window < 30 days", func(t *testing.T) {
// Set not_valid_before to 15 days in the past and not_valid_after to 14 days in the future so the
// validity window becomes 29 days, of which there are 14 left which should trigger the second
// renewal scenario(window < 30 days, renew when there is half that time left)
notValidBefore := time.Now().Add(-15 * 24 * time.Hour).UTC().Round(time.Microsecond)
notValidAfter := time.Now().Add(14 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMManagedCertificate{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: caType,
CAName: caName,
Serial: &serial,
},
})
require.NoError(t, err)
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_apple_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
UPDATE host_mdm_windows_profiles SET status = ? WHERE host_uuid = ? AND profile_uuid = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
if err != nil {
return err
}
return nil
})
require.NoError(t, err)
if err != nil {
return err
}
return nil
})
require.NoError(t, err)
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
// Verify the policy is not currently marked for resend and that the upsert executed correctly
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Equal(t, challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, caType, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, caName, profile.CAName)
// Renew should set the MDM delivery status to "null" so the profile gets resent and the certificate renewed
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.Nil(t, profile.Status)
// Renew should set the MDM delivery status to "null" so the profile gets resent and the certificate renewed
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.Nil(t, profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
}) */
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetWindowsHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, caName)
require.NoError(t, err)
require.NotNil(t, profile)
})
})
}
}
+2
View File
@@ -494,6 +494,8 @@ func preprocessWindowsProfileContents(deps ProfilePreprocessDependencies, params
switch {
case fleetVar == string(fleet.FleetVarSCEPWindowsCertificateID):
result = profiles.ReplaceFleetVariableInXML(fleet.FleetVarSCEPWindowsCertificateIDRegexp, result, params.ProfileUUID)
case fleetVar == string(fleet.FleetVarSCEPRenewalID):
result = profiles.ReplaceFleetVariableInXML(fleet.FleetVarSCEPRenewalIDRegexp, result, "fleet-"+params.ProfileUUID)
case strings.HasPrefix(fleetVar, string(fleet.FleetVarCustomSCEPChallengePrefix)):
caName := strings.TrimPrefix(fleetVar, string(fleet.FleetVarCustomSCEPChallengePrefix))
err := profiles.IsCustomSCEPConfigured(deps.Context, deps.CustomSCEPCAs, caName, fleetVar, func(errMsg string) error {