Renewal of DigiCert certificates on macOS (#28449)

Adds renewal of Digicert certificates:
https://github.com/fleetdm/fleet/issues/26553 . Does not attempt to
renew custom SCEP or NDES. Also we aren't actually calling the DigiCert
renewal endpoint at this time because we don't believe we need to and we
can't necessarily do that as we weren't previously storing the serial
number however this change adds storage of the serial number.


# Checklist for submitter

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

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [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/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 database migrations are included, checked table schema to
confirm autoupdate
- For database migrations:
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [x] Added/updated automated tests
- [x] A detailed QA plan exists on the associated ticket (if it isn't
there, work with the product group's QA engineer to add it)
This commit is contained in:
Jordan Montgomery
2025-04-24 08:35:15 -04:00
committed by GitHub
parent d5bf210354
commit 862739292e
12 changed files with 394 additions and 23 deletions
+1
View File
@@ -0,0 +1 @@
Fleet-managed DigiCert certificates will be renewed 30 days before expiry for those valid longer than 30 days or when half the validity period remains for certificates valid 30 days or less. This only applies to certificates that were initially requested after this feature was added. For hosts with DigiCert certificates originally requested prior to this renew feature, manually resending the profile will generate a new certificate which will be automatically renewed before its next expiry.
+3
View File
@@ -933,6 +933,9 @@ func newCleanupsAndAggregationSchedule(
return service.RenewSCEPCertificates(ctx, logger, ds, config, commander)
},
),
schedule.WithJob("renew_host_mdm_managed_certificates", func(ctx context.Context) error {
return ds.RenewMDMManagedCertificates(ctx)
}),
schedule.WithJob("query_results_cleanup", func(ctx context.Context) error {
config, err := ds.AppConfig(ctx)
if err != nil {
+13 -4
View File
@@ -6,6 +6,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"net/http"
"net/url"
@@ -66,7 +67,6 @@ func WithLogger(logger kitlog.Logger) Opt {
}
func (s *Service) VerifyProfileID(ctx context.Context, config fleet.DigiCertIntegration) error {
client := fleethttp.NewClient(fleethttp.WithTimeout(s.timeout))
config.URL = strings.TrimRight(config.URL, "/")
@@ -251,6 +251,13 @@ func (s *Service) GetCertificate(ctx context.Context, config fleet.DigiCertInteg
return nil, ctxerr.Errorf(ctx, "unexpected DigiCert delivery format: %s", certResp.DeliveryFormat)
}
// Serial number is an up to 20-byte(40 char) hex string
_, err = hex.DecodeString(certResp.SerialNumber)
if err != nil || certResp.SerialNumber == "" || len(certResp.SerialNumber) > 40 {
level.Error(s.logger).Log("msg", "DigiCert certificate returned with invalid serial number", "serial_number", certResp.SerialNumber, "decode_err", err)
return nil, ctxerr.Errorf(ctx, "invalid DigiCert serial number: %s", certResp.SerialNumber)
}
if len(certResp.Certificate) == 0 {
return nil, ctxerr.Errorf(ctx, "did not receive DigiCert certificate")
}
@@ -279,8 +286,10 @@ func (s *Service) GetCertificate(ctx context.Context, config fleet.DigiCertInteg
}
return &fleet.DigiCertCertificate{
PfxData: pkcs12Data,
Password: password,
NotValidAfter: cert.NotAfter,
PfxData: pkcs12Data,
Password: password,
NotValidBefore: cert.NotBefore,
NotValidAfter: cert.NotAfter,
SerialNumber: certResp.SerialNumber,
}, nil
}
+70 -5
View File
@@ -521,9 +521,11 @@ func (ds *Datastore) GetHostMDMCertificateProfile(ctx context.Context, hostUUID
hmap.profile_uuid,
hmap.status,
hmmc.challenge_retrieved_at,
hmmc.not_valid_before,
hmmc.not_valid_after,
hmmc.type,
hmmc.ca_name
hmmc.ca_name,
hmmc.serial
FROM
host_mdm_apple_profiles hmap
JOIN host_mdm_managed_certificates hmmc
@@ -551,6 +553,65 @@ func (ds *Datastore) CleanUpMDMManagedCertificates(ctx context.Context) error {
return nil
}
// RenewMDMManagedCertificates marks managed certificate profiles for resend when renewal is required
func (ds *Datastore) RenewMDMManagedCertificates(ctx context.Context) error {
// Fetch all MDM Managed digicert certificates 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.
hostCertsToRenew := []struct {
HostUUID string `db:"host_uuid"`
ProfileUUID string `db:"profile_uuid"`
NotValidAfter time.Time `db:"not_valid_after"`
ValidityPeriod int `db:"validity_period"`
}{}
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
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`, fleet.CAConfigDigiCert)
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 digicert certificates to renew")
return nil
}
// This will trigger a resend next time profiles are checked
updateQuery := `UPDATE host_mdm_apple_profiles SET status = NULL WHERE `
hostProfileClause := ``
values := []interface{}{}
for _, hostCertToRenew := range hostCertsToRenew {
hostProfileClause += `(host_uuid = ? AND profile_uuid = ?) OR `
values = append(values, hostCertToRenew.HostUUID, hostCertToRenew.ProfileUUID)
}
level.Debug(ds.logger).Log("msg", "Renewing MDM managed digicert certificates", "len(hostCertsToRenew)", len(hostCertsToRenew))
err = ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
_, err := tx.ExecContext(ctx, updateQuery+strings.TrimSuffix(hostProfileClause, " OR "), values...)
if err != nil {
return ctxerr.Wrap(ctx, err, "updating mdm managed certificates to renew")
}
return nil
})
return err
}
func (ds *Datastore) NewMDMAppleEnrollmentProfile(
ctx context.Context,
payload fleet.MDMAppleEnrollmentProfilePayload,
@@ -5883,16 +5944,20 @@ func (ds *Datastore) BulkUpsertMDMManagedCertificates(ctx context.Context, paylo
host_uuid,
profile_uuid,
challenge_retrieved_at,
not_valid_before,
not_valid_after,
type,
ca_name
ca_name,
serial
)
VALUES %s
ON DUPLICATE KEY UPDATE
challenge_retrieved_at = VALUES(challenge_retrieved_at),
not_valid_before = VALUES(not_valid_before),
not_valid_after = VALUES(not_valid_after),
type = VALUES(type),
ca_name = VALUES(ca_name)`,
ca_name = VALUES(ca_name),
serial = VALUES(serial)`,
strings.TrimSuffix(valuePart, ","),
)
@@ -5901,8 +5966,8 @@ func (ds *Datastore) BulkUpsertMDMManagedCertificates(ctx context.Context, paylo
}
generateValueArgs := func(p *fleet.MDMBulkUpsertManagedCertificatePayload) (string, []any) {
valuePart := "(?, ?, ?, ?, ?, ?),"
args := []any{p.HostUUID, p.ProfileUUID, p.ChallengeRetrievedAt, p.NotValidAfter, p.Type, p.CAName}
valuePart := "(?, ?, ?, ?, ?, ?, ?, ?),"
args := []any{p.HostUUID, p.ProfileUUID, p.ChallengeRetrievedAt, p.NotValidBefore, p.NotValidAfter, p.Type, p.CAName, p.Serial}
return valuePart, args
}
+243 -4
View File
@@ -90,7 +90,8 @@ func TestMDMApple(t *testing.T) {
{"TestMDMGetABMTokenOrgNamesAssociatedWithTeam", testMDMGetABMTokenOrgNamesAssociatedWithTeam},
{"HostMDMCommands", testHostMDMCommands},
{"IngestMDMAppleDeviceFromOTAEnrollment", testIngestMDMAppleDeviceFromOTAEnrollment},
{"MDMManagedCertificates", testMDMManagedCertificates},
{"MDMManagedSCEPCertificates", testMDMManagedSCEPCertificates},
{"MDMManagedDigicertCertificates", testMDMManagedDigicertCertificates},
{"AppleMDMSetBatchAsyncLastSeenAt", testAppleMDMSetBatchAsyncLastSeenAt},
{"TestMDMAppleProfileLabels", testMDMAppleProfileLabels},
{"AggregateMacOSSettingsAllPlatforms", testAggregateMacOSSettingsAllPlatforms},
@@ -608,7 +609,6 @@ func testHostDetailsMDMProfiles(t *testing.T, ds *Datastore) {
require.NoError(t, err)
require.Len(t, gotProfs, 1)
assert.Equal(t, &fleet.MDMDeliveryVerifying, gotProfs[0].Status)
}
func TestIngestMDMAppleDevicesFromDEPSync(t *testing.T) {
@@ -2676,7 +2676,8 @@ func testDeleteMDMAppleProfilesForHost(t *testing.T, ds *Datastore) {
}
func createDiskEncryptionRecord(ctx context.Context, ds *Datastore, t *testing.T, host *fleet.Host, key string, decryptable bool,
threshold time.Time) {
threshold time.Time,
) {
err := ds.SetOrUpdateHostDiskEncryptionKey(ctx, host, key, "", nil)
require.NoError(t, err)
err = ds.SetHostsDiskEncryptionKeyStatus(ctx, []uint{host.ID}, decryptable, threshold)
@@ -7190,7 +7191,7 @@ func TestGetMDMAppleOSUpdatesSettingsByHostSerial(t *testing.T) {
require.ErrorIs(t, err, sql.ErrNoRows)
}
func testMDMManagedCertificates(t *testing.T, ds *Datastore) {
func testMDMManagedSCEPCertificates(t *testing.T, ds *Datastore) {
ctx := context.Background()
initialCP := storeDummyConfigProfileForTest(t, ds)
host, err := ds.NewHost(ctx, &fleet.Host{
@@ -7231,12 +7232,14 @@ func testMDMManagedCertificates(t *testing.T, ds *Datastore) {
assert.Nil(t, profile)
challengeRetrievedAt := time.Now().Add(-time.Hour).UTC().Round(time.Microsecond)
notValidBefore := time.Now().UTC().Round(time.Microsecond)
notValidAfter := time.Now().Add(24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMBulkUpsertManagedCertificatePayload{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: &challengeRetrievedAt,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: fleet.CAConfigCustomSCEPProxy,
CAName: "test-ca",
@@ -7252,10 +7255,20 @@ func testMDMManagedCertificates(t *testing.T, ds *Datastore) {
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
require.NotNil(t, profile.ChallengeRetrievedAt)
assert.Equal(t, &challengeRetrievedAt, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, fleet.CAConfigCustomSCEPProxy, profile.Type)
assert.Nil(t, profile.Serial)
assert.Equal(t, "test-ca", profile.CAName)
// Renew should do nothing for SCEP
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryPending, *profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
@@ -7290,6 +7303,232 @@ func testMDMManagedCertificates(t *testing.T, ds *Datastore) {
require.ErrorIs(t, err, sql.ErrNoRows)
}
func testMDMManagedDigicertCertificates(t *testing.T, ds *Datastore) {
ctx := context.Background()
initialCP := storeDummyConfigProfileForTest(t, ds)
host, err := ds.NewHost(ctx, &fleet.Host{
DetailUpdatedAt: time.Now(),
LabelUpdatedAt: time.Now(),
PolicyUpdatedAt: time.Now(),
SeenTime: time.Now(),
OsqueryHostID: ptr.String("host0-osquery-id"),
NodeKey: ptr.String("host0-node-key"),
UUID: "host0-test-mdm-profiles",
Hostname: "hostname0",
})
require.NoError(t, err)
// Host and profile are not linked
profile, err := ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
assert.Nil(t, profile)
err = ds.BulkUpsertMDMAppleHostProfiles(ctx, []*fleet.MDMAppleBulkUpsertHostProfilePayload{
{
ProfileUUID: initialCP.ProfileUUID,
ProfileIdentifier: initialCP.Identifier,
ProfileName: initialCP.Name,
HostUUID: host.UUID,
Status: &fleet.MDMDeliveryVerified,
OperationType: fleet.MDMOperationTypeInstall,
CommandUUID: "command-uuid",
Checksum: []byte("checksum"),
},
},
)
require.NoError(t, err)
// Host and profile do not have certificate metadata
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
assert.Nil(t, profile)
notValidBefore := time.Now().UTC().Round(time.Microsecond)
notValidAfter := time.Now().Add(29 * 24 * time.Hour).UTC().Round(time.Microsecond)
serial := "3ABADCAFEF684D6348F5EC95AEFF468F237A9D7E"
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMBulkUpsertManagedCertificatePayload{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: nil,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: fleet.CAConfigDigiCert,
CAName: "test-ca",
Serial: &serial,
},
})
require.NoError(t, err)
// Check that the managed certificate was inserted correctly
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.NotNil(t, profile)
assert.Equal(t, host.UUID, profile.HostUUID)
assert.Equal(t, initialCP.ProfileUUID, profile.ProfileUUID)
assert.Nil(t, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, fleet.CAConfigDigiCert, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, "test-ca", profile.CAName)
// Renew should not do anything yet so the MDM delivery status should stay "verified"
err = ds.RenewMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.NotNil(t, profile.Status)
assert.Equal(t, fleet.MDMDeliveryVerified, *profile.Status)
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)
notValidBefore := time.Now().Add(-31 * 24 * time.Hour).UTC().Round(time.Microsecond)
err = ds.BulkUpsertMDMManagedCertificates(ctx, []*fleet.MDMBulkUpsertManagedCertificatePayload{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: nil,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: fleet.CAConfigDigiCert,
CAName: "test-ca",
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 = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
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.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
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.Nil(t, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, fleet.CAConfigDigiCert, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, "test-ca", 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.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.Nil(t, profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
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.MDMBulkUpsertManagedCertificatePayload{
{
HostUUID: host.UUID,
ProfileUUID: initialCP.ProfileUUID,
ChallengeRetrievedAt: nil,
NotValidBefore: &notValidBefore,
NotValidAfter: &notValidAfter,
Type: fleet.CAConfigDigiCert,
CAName: "test-ca",
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 = ?
`, fleet.MDMDeliveryVerified, host.UUID, initialCP.ProfileUUID)
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.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
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.Nil(t, profile.ChallengeRetrievedAt)
assert.Equal(t, &notValidBefore, profile.NotValidBefore)
assert.Equal(t, &notValidAfter, profile.NotValidAfter)
assert.Equal(t, fleet.CAConfigDigiCert, profile.Type)
require.NotNil(t, profile.Serial)
assert.Equal(t, serial, *profile.Serial)
assert.Equal(t, "test-ca", 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.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.Nil(t, profile.Status)
// Cleanup should do nothing
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
profile, err = ds.GetHostMDMCertificateProfile(ctx, host.UUID, initialCP.ProfileUUID, "test-ca")
require.NoError(t, err)
require.NotNil(t, profile)
})
badProfileUUID := uuid.NewString()
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
_, err := q.ExecContext(ctx, `
INSERT INTO host_mdm_managed_certificates (host_uuid, profile_uuid) VALUES (?, ?)
`, host.UUID, badProfileUUID)
if err != nil {
return err
}
return nil
})
var uid string
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
badProfileUUID)
})
require.Equal(t, badProfileUUID, uid)
// Cleanup should delete the above orphaned record
err = ds.CleanUpMDMManagedCertificates(ctx)
require.NoError(t, err)
err = ExecAdhocSQLWithError(ds, func(q sqlx.ExtContext) error {
return sqlx.GetContext(ctx, q, &uid, `SELECT profile_uuid FROM host_mdm_managed_certificates WHERE profile_uuid = ?`,
badProfileUUID)
})
require.ErrorIs(t, err, sql.ErrNoRows)
}
func testAppleMDMSetBatchAsyncLastSeenAt(t *testing.T, ds *Datastore) {
ctx := context.Background()
@@ -0,0 +1,29 @@
package tables
import (
"database/sql"
"fmt"
)
func init() {
MigrationClient.AddMigration(Up_20250421085116, Down_20250421085116)
}
func Up_20250421085116(tx *sql.Tx) error {
if columnsExists(tx, "host_mdm_managed_certificates", "serial", "not_valid_before") {
return nil
}
_, err := tx.Exec(`
ALTER TABLE host_mdm_managed_certificates
ADD COLUMN serial varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
ADD COLUMN not_valid_before datetime(6) NULL
`)
if err != nil {
return fmt.Errorf("failed to add serial columns to host_mdm_managed_certificates table: %s", err)
}
return nil
}
func Down_20250421085116(tx *sql.Tx) error {
return nil
}
File diff suppressed because one or more lines are too long
+4
View File
@@ -292,9 +292,11 @@ type HostMDMCertificateProfile struct {
ProfileUUID string `db:"profile_uuid"`
Status *MDMDeliveryStatus `db:"status"`
ChallengeRetrievedAt *time.Time `db:"challenge_retrieved_at"`
NotValidBefore *time.Time `db:"not_valid_before"`
NotValidAfter *time.Time `db:"not_valid_after"`
Type CAConfigAssetType `db:"type"`
CAName string `db:"ca_name"`
Serial *string `db:"serial"`
}
type HostMDMProfileDetail string
@@ -976,9 +978,11 @@ type MDMBulkUpsertManagedCertificatePayload struct {
ProfileUUID string
HostUUID string
ChallengeRetrievedAt *time.Time
NotValidBefore *time.Time
NotValidAfter *time.Time
Type CAConfigAssetType
CAName string
Serial *string
}
// MDMAppleEnrolledDeviceInfo represents the information of a device enrolled
+3
View File
@@ -1993,6 +1993,9 @@ type Datastore interface {
// CleanUpMDMManagedCertificates removes all managed certificates that are not associated with any host+profile.
CleanUpMDMManagedCertificates(ctx context.Context) error
// RenewMDMManagedCertificates marks managed certificate profiles for resend when renewal is required
RenewMDMManagedCertificates(ctx context.Context) error
// /////////////////////////////////////////////////////////////////////////////
// Secret variables
+5 -3
View File
@@ -6,9 +6,11 @@ import (
)
type DigiCertCertificate struct {
PfxData []byte
Password string
NotValidAfter time.Time
PfxData []byte
Password string
NotValidBefore time.Time
NotValidAfter time.Time
SerialNumber string
}
type DigiCertService interface {
+12
View File
@@ -1252,6 +1252,8 @@ type GetHostMDMCertificateProfileFunc func(ctx context.Context, hostUUID string,
type CleanUpMDMManagedCertificatesFunc func(ctx context.Context) error
type RenewMDMManagedCertificatesFunc func(ctx context.Context) error
type UpsertSecretVariablesFunc func(ctx context.Context, secretVariables []fleet.SecretVariable) error
type GetSecretVariablesFunc func(ctx context.Context, names []string) ([]fleet.SecretVariable, error)
@@ -3168,6 +3170,9 @@ type DataStore struct {
CleanUpMDMManagedCertificatesFunc CleanUpMDMManagedCertificatesFunc
CleanUpMDMManagedCertificatesFuncInvoked bool
RenewMDMManagedCertificatesFunc RenewMDMManagedCertificatesFunc
RenewMDMManagedCertificatesFuncInvoked bool
UpsertSecretVariablesFunc UpsertSecretVariablesFunc
UpsertSecretVariablesFuncInvoked bool
@@ -7581,6 +7586,13 @@ func (s *DataStore) CleanUpMDMManagedCertificates(ctx context.Context) error {
return s.CleanUpMDMManagedCertificatesFunc(ctx)
}
func (s *DataStore) RenewMDMManagedCertificates(ctx context.Context) error {
s.mu.Lock()
s.RenewMDMManagedCertificatesFuncInvoked = true
s.mu.Unlock()
return s.RenewMDMManagedCertificatesFunc(ctx)
}
func (s *DataStore) UpsertSecretVariables(ctx context.Context, secretVariables []fleet.SecretVariable) error {
s.mu.Lock()
s.UpsertSecretVariablesFuncInvoked = true
+7 -5
View File
@@ -4383,11 +4383,13 @@ func preprocessProfileContents(
return ctxerr.Wrap(ctx, err, "replacing Fleet variable for DigiCert password")
}
managedCertificatePayloads = append(managedCertificatePayloads, &fleet.MDMBulkUpsertManagedCertificatePayload{
HostUUID: hostUUID,
ProfileUUID: profUUID,
NotValidAfter: &cert.NotValidAfter,
Type: fleet.CAConfigDigiCert,
CAName: caName,
HostUUID: hostUUID,
ProfileUUID: profUUID,
NotValidBefore: &cert.NotValidBefore,
NotValidAfter: &cert.NotValidAfter,
Type: fleet.CAConfigDigiCert,
CAName: caName,
Serial: &cert.SerialNumber,
})
default: