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:
co-authored by
Magnus Jensen
parent
229481fc79
commit
9ec74e09e5
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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: ¬ValidBefore,
|
||||
NotValidAfter: ¬ValidAfter,
|
||||
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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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, ¬ValidBefore, profile.NotValidBefore)
|
||||
assert.Equal(t, ¬ValidAfter, 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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user