Fleet versions
💥 Actual behavior
Fleet backdates every Windows MDM client certificate by the full renewal period, so the certificate is issued already 180 days old and expires 185 days after enrollment instead of the 365 days Fleet advertises.
populateClientCert computes NotBefore by subtracting the renewal period from the current time, then adds a hardcoded 365 days to get NotAfter:
certRenewalPeriodInSecsInt, err := strconv.Atoi(syncml.PolicyCertRenewalPeriodInSecs) // 15552000s = 180 days
notBeforeDuration := time.Now().Add(time.Duration(certRenewalPeriodInSecsInt) * -time.Second) // now - 180d
yearDuration := 365 * 24 * time.Hour
NotBefore: notBeforeDuration, // now - 180d
NotAfter: notBeforeDuration.Add(yearDuration), // now + 185d
Three separate problems fall out of this.
The certificate lives half as long as advertised. Fleet tells Windows one thing and issues another.
The renewal window is already open when the certificate is installed. The certificate is renewal-eligible from the instant it is issued, 180 days before the device even enrolled. Windows then retries every RetryInterval = 4 days for the entire life of the enrollment. This is why the EnrollmentState = 3 symptom in #50611 shows up on freshly enrolled hosts rather than only on hosts approaching expiry.
RenewPeriod and the advertised renewal period disagree. GetPolicies advertises RenewalPeriodSeconds = 15552000 (180 days) while the provisioning doc sends RenewPeriod = 365 days. The backdating has to be fixed for the renewal window to land anywhere sensible.
🛠️ Expected behavior
Fleet should issue a certificate whose validity matches what it advertises, and whose renewal window opens near the end of that validity rather than at the start.
- Set
NotBefore to the current time. If a clock-skew allowance is wanted, use minutes or a few hours, not the 180 day renewal period.
- Derive
NotAfter from syncml.PolicyCertValidityPeriodInSecs (the same constant Fleet advertises in GetPolicies) instead of the hardcoded yearDuration, so the advertised policy and the issued certificate cannot drift apart.
- Make the provisioning doc's
RenewPeriod agree with PolicyCertRenewalPeriodInSecs, so a 365 day certificate with a 180 day renewal period opens its renewal window at day 185 rather than at day 0.
This bug is independent of the ROBO question. #51468 set WstepROBOSupport = "false" to stop Windows from attempting a renewal Fleet cannot service, and #52492 tracks the fact that Windows attempts renewal anyway. Even with ROBO fully implemented, the certificate lifetime and the renewal window would still be wrong.
🧑💻 Steps to reproduce
These steps:
- Turn on Windows MDM on a Fleet server and enroll a Windows 10 or 11 host.
- On the host, open
certlm.msc and find the Fleet-issued client certificate in Personal > Certificates (issued by the Fleet WSTEP identity CA, with OU = Fleet). Alternatively run Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -like '*Fleet*' } | Select-Object Subject, NotBefore, NotAfter.
- Observe that
NotBefore is roughly 180 days in the past, dated well before the host enrolled, and that NotAfter is roughly 185 days in the future rather than 365 days.
- Read the provisioning doc values Fleet sent by checking
HKLM\SOFTWARE\Microsoft\Enrollments\<GUID> for the enrollment with ProviderID = Fleet, and confirm the WSTEP renew settings show a 365 day RenewPeriod against a certificate with 185 days of remaining life.
- Confirm that Windows treats the certificate as renewal-eligible immediately: the scheduled task "Schedule created by enrollment client for renewal of certificate warning" under
\Microsoft\Windows\EnterpriseMgmt\<GUID> runs and attempts renewal within the first RetryInterval window rather than waiting until the certificate is near expiry.
🕯️ More info (optional)
Fleet versions
💥 Actual behavior
Fleet backdates every Windows MDM client certificate by the full renewal period, so the certificate is issued already 180 days old and expires 185 days after enrollment instead of the 365 days Fleet advertises.
populateClientCertcomputesNotBeforeby subtracting the renewal period from the current time, then adds a hardcoded 365 days to getNotAfter:Three separate problems fall out of this.
The certificate lives half as long as advertised. Fleet tells Windows one thing and issues another.
The renewal window is already open when the certificate is installed. The certificate is renewal-eligible from the instant it is issued, 180 days before the device even enrolled. Windows then retries every
RetryInterval = 4days for the entire life of the enrollment. This is why theEnrollmentState = 3symptom in #50611 shows up on freshly enrolled hosts rather than only on hosts approaching expiry.RenewPeriodand the advertised renewal period disagree. GetPolicies advertisesRenewalPeriodSeconds = 15552000(180 days) while the provisioning doc sendsRenewPeriod = 365days. The backdating has to be fixed for the renewal window to land anywhere sensible.🛠️ Expected behavior
Fleet should issue a certificate whose validity matches what it advertises, and whose renewal window opens near the end of that validity rather than at the start.
NotBeforeto the current time. If a clock-skew allowance is wanted, use minutes or a few hours, not the 180 day renewal period.NotAfterfromsyncml.PolicyCertValidityPeriodInSecs(the same constant Fleet advertises in GetPolicies) instead of the hardcodedyearDuration, so the advertised policy and the issued certificate cannot drift apart.RenewPeriodagree withPolicyCertRenewalPeriodInSecs, so a 365 day certificate with a 180 day renewal period opens its renewal window at day 185 rather than at day 0.This bug is independent of the ROBO question. #51468 set
WstepROBOSupport = "false"to stop Windows from attempting a renewal Fleet cannot service, and #52492 tracks the fact that Windows attempts renewal anyway. Even with ROBO fully implemented, the certificate lifetime and the renewal window would still be wrong.🧑💻 Steps to reproduce
These steps:
certlm.mscand find the Fleet-issued client certificate inPersonal > Certificates(issued by the Fleet WSTEP identity CA, withOU = Fleet). Alternatively runGet-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -like '*Fleet*' } | Select-Object Subject, NotBefore, NotAfter.NotBeforeis roughly 180 days in the past, dated well before the host enrolled, and thatNotAfteris roughly 185 days in the future rather than 365 days.HKLM\SOFTWARE\Microsoft\Enrollments\<GUID>for the enrollment withProviderID = Fleet, and confirm the WSTEP renew settings show a 365 dayRenewPeriodagainst a certificate with 185 days of remaining life.\Microsoft\Windows\EnterpriseMgmt\<GUID>runs and attempts renewal within the firstRetryIntervalwindow rather than waiting until the certificate is near expiry.🕯️ More info (optional)