Skip to content

Deploy certificates from NDES CA on Windows #33421

Closed
1 of 1 issue completed
Closed

Description

@marko-lisica

Goal

User story
As an IT admin,
I want to deploy certificates from my NDES certificate authority (CA) on my Windows hosts (device and user scope)
so that I can grant end users access to Wi-Fi and VPN.

Changes

Product

  • UI changes: No changes. The existing NDES CA configuration UI (Settings > Integrations > Certificates > Microsoft NDES) already works for both macOS and Windows. Windows profiles are uploaded via Controls > OS settings > Custom settings, which already supports SyncML profiles with Fleet variables.
  • CLI (fleetctl) usage changes: No changes. fleetctl apply already supports uploading Windows SyncML profiles with Fleet variables.
  • YAML changes: No changes. The existing NDES SCEP proxy YAML configuration (certificate_authorities.ndes_scep_proxy) applies to both macOS and Windows.
  • REST API changes: No changes. The existing profile upload endpoint (POST /api/v1/fleet/mdm/profiles) already accepts Windows SyncML profiles. The only change is that NDES Fleet variables ($FLEET_VAR_NDES_SCEP_CHALLENGE, $FLEET_VAR_NDES_SCEP_PROXY_URL) are now accepted in Windows profiles, where they were previously rejected.
  • Fleet's agent (fleetd) changes: No changes. Certificate enrollment is handled by the Windows MDM client via the ClientCertificateInstall CSP, not by fleetd.
  • GitOps mode UI changes: No changes.
  • GitOps generation changes: No changes.
  • Activity changes: No changes. Profile upload and deployment activities already cover Windows profiles.
  • Permissions changes: No changes. Same permissions as existing Windows profile management (admin, maintainer, GitOps).
  • Changes to paid features or tiers: Fleet Premium. NDES SCEP is already a premium-only feature. No changes to the pricing page.
  • My device and fleetdm.com/better changes: No changes.
  • Usage statistics: No changes.
  • Other reference documentation changes: Update the certificate deployment guide to change "macOS and Windows (coming soon)" to "macOS and Windows" for NDES, and add a Windows SyncML example profile under the Microsoft NDES section.
  • First draft of test plan added
  • Once shipped, requester has been notified
  • Once shipped, dogfooding issue has been filed

Engineering

  • Test plan is finalized
  • Contributor API changes: No changes.
  • Feature guide changes: Update articles/connect-end-user-to-wifi-with-certificate.md — add a Windows subsection under Microsoft NDES with a SyncML example profile using $FLEET_VAR_NDES_SCEP_CHALLENGE, $FLEET_VAR_NDES_SCEP_PROXY_URL, and $FLEET_VAR_SCEP_RENEWAL_ID in the ClientCertificateInstall CSP format (both Device and User scope examples).
  • Database schema migrations: None required. Existing tables (mdm_windows_configuration_profiles, host_mdm_windows_profiles, mdm_managed_certificates) already support this.
  • This is a premium only feature: Yes

Implementation summary

This extends the existing Windows Custom SCEP flow to also support NDES. All NDES infrastructure (challenge retrieval, SCEP proxy, certificate renewal) and all Windows SCEP infrastructure (SyncML validation, ClientCertificateInstall CSP, variable replacement) already exist. The work connects these two systems:

  1. Profile validation (server/service/windows_mdm_profiles.go): Allow $FLEET_VAR_NDES_SCEP_CHALLENGE and $FLEET_VAR_NDES_SCEP_PROXY_URL in Windows profiles. Add an NDES-specific validation callback to ensure the variables appear in the correct SyncML locations (Challenge in Install/Challenge, proxy URL in Install/ServerURL, renewal ID in SubjectName).

  2. Variable replacement at deployment time (server/mdm/microsoft/profile_variables.go): Add NDES variable replacement cases. When a Windows profile with NDES variables is deployed to a host, Fleet fetches a one-time challenge from the NDES admin URL (57-minute TTL) and builds a SCEP proxy URL immediately before sending the SyncML to the device. A managed certificate record is created for renewal tracking.

  3. Error handling: Surface descriptive errors when NDES challenge retrieval fails (invalid credentials, password cache full, insufficient permissions) as profile delivery failures visible in Host details > OS settings.

  4. Documentation: Add a Windows NDES SyncML example profile and update the certificate deployment guide.

The SCEP proxy (/mdm/scep/proxy/{identifier}) is already platform-agnostic and handles NDES proxying. Certificate renewal via mdm_managed_certificates already works for Windows. No database migrations needed.

ℹ️ Please read this issue carefully and understand it. Pay special attention to UI wireframes, especially "dev notes".

QA

Risk assessment

  • Risk level: Low
  • All infrastructure exists on both sides (NDES and Windows SCEP). This extends an existing pattern (Custom SCEP on Windows) to a new CA type (NDES). The SCEP proxy is platform-agnostic and already handles NDES. The main area to verify is that the Windows SCEP CSP correctly enrolls via Fleet's SCEP proxy URL.

Test plan

Prerequisites: Fleet Premium license, Windows host enrolled via MDM, NDES server configured and accessible.

  1. Deploy the root CA certificate (from the guide).

Setup

  1. Configure NDES CA in Fleet: go to Settings > Integrations > Certificates, add Microsoft NDES with SCEP URL, Admin URL, username, and password. Verify CA appears in the list.

Device scope

  1. Upload a Windows SyncML NDES SCEP profile using ./Device/... LocURIs containing $FLEET_VAR_NDES_SCEP_CHALLENGE, $FLEET_VAR_NDES_SCEP_PROXY_URL, $FLEET_VAR_SCEP_RENEWAL_ID, and $FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID. Verify it is accepted.
  2. Assign the profile to a team with a Windows host. Wait for profile deployment.
  3. Verify the profile status transitions to "Verifying" then "Verified" in Host details > OS settings.
  4. On the Windows host, verify the certificate appears in Cert:\LocalMachine\My using PowerShell: Get-ChildItem -Path Cert:\LocalMachine\My.

User scope

  1. Upload a Windows SyncML NDES SCEP profile using ./User/... LocURIs. Verify it is accepted.
  2. Assign and deploy to a Windows host. Verify profile status reaches "Verified".
  3. On the Windows host, verify the certificate appears in Cert:\CurrentUser\My.

Validation errors

  1. Upload a Windows profile with $FLEET_VAR_NDES_SCEP_CHALLENGE but without $FLEET_VAR_NDES_SCEP_PROXY_URL. Verify it is rejected with an appropriate error message.
  2. Upload a Windows profile with $FLEET_VAR_NDES_SCEP_PROXY_URL but without $FLEET_VAR_NDES_SCEP_CHALLENGE. Verify it is rejected.
  3. Upload a Windows profile with NDES variables but no NDES CA configured. Verify it is rejected.
  4. Upload a Windows profile with both NDES and Custom SCEP variables. Verify it is rejected.
  5. Upload a Windows profile with NDES variables but missing $FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID. Verify it is rejected.
  6. Verify a non-premium instance rejects profiles with NDES variables.

Error scenarios

  1. Configure NDES CA with incorrect credentials. Upload and deploy a valid NDES profile. Verify the profile shows "Failed" status with a message about invalid credentials.
  2. Fix the NDES credentials. Resend the profile. Verify it succeeds.

Renewal

  1. Deploy a Windows NDES certificate. After it reaches "Verified" status, update the not_valid_after column in mdm_managed_certificates to a date within 30 days. Verify Fleet re-deploys the profile with a new challenge on the next renewal cron cycle.

GitOps

  1. Apply a Windows NDES SCEP profile via fleetctl gitops with GitOps YAML. Verify it is accepted and deployed.

Testing notes

  • A Windows NDES test environment is required.
    • The NDES server in Proxmox keeps having the wrong time (~9 hours in the future). This causes cert delivery to fail. I used a Powershell command to set it.
  • The SCEP proxy URL format for NDES is: {MDMUrl}/mdm/scep/proxy/{hostUUID},{profileUUID},NDES

Confirmation

  1. Engineer: Added comment to user story confirming successful completion of test plan.
  2. QA: Added comment to user story confirming successful completion of test plan.

Demo video

https://www.youtube.com/watch?v=-mpW8o4vqu0

Docs

https://github.com/fleetdm/fleet/pull/41496/changes

Activity

  1. added
    storyA user story defining an entire feature
    #g-apple-at-workProduct group focused on Apple devices
    on Sep 24, 2025
  2. modified the milestone: 4.79.0 on Oct 8, 2025
  3. added
    ~contextFeature that customers consider mission critical for their particular buying situation.
    on Feb 3, 2026
  4. added this to the 4.84.0 milestone on Feb 4, 2026
  5. added and removed
    #g-apple-at-workProduct group focused on Apple devices
    on Feb 17, 2026
  6. self-assigned this
    on Feb 20, 2026
  7. added
    :productProduct Design department (shows up on 🦢📨🎉 Product design intake & outtake board)
    on Feb 20, 2026
  8. 21 remaining items

  9. getvictor commented on Mar 16, 2026

    @getvictor
    Member

    Latest guide PR: #41730

  10. self-assigned this
    on Mar 26, 2026
  11. Brajim20 commented on Mar 28, 2026

    @Brajim20
    Member

    Tested on Fleet v4.83.0-rc, Windows 11 (ARM), NDES CA configured via Okta

    • Step 1 — NDES CA configured in Fleet (Settings > Integrations > Certificates)
    • Step 2 — Windows SyncML NDES SCEP profile with all required Fleet variables uploaded and accepted
    • Step 3 — Profile assigned to team with Windows host
    • Step 4 — Profile status reached Verified in Host details > OS settings
    • Step 5 — Certificate appears in Cert:\LocalMachine\My — blocked (wrong CA thumbprint)
    • Step 6 — User scope profile uploaded and accepted
    • Step 7 — User scope profile reaches Verified, cert in Cert:\CurrentUser\My
    • Step 9 — Profile missing $FLEET_VAR_NDES_SCEP_PROXY_URL correctly rejected
    • Step 10 — Profile missing $FLEET_VAR_NDES_SCEP_CHALLENGE correctly rejected
    • Step 11 — Profile with NDES vars but no NDES CA configured — skipped (would require removing CA)
    • Step 12 — Profile with mixed NDES and Custom SCEP vars — skipped (no custom SCEP CA configured)
    • Step 13 — Profile missing $FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID correctly rejected
    • Step 15/16 — Wrong credentials error scenario
    • Step 17 — Certificate renewal
    • Step 18 — GitOps apply
  12. moved this from ✔️Awaiting QA to ✅ Ready for release in 🛡️ #g-supply-chainon Mar 28, 2026
  13. added
    :productProduct Design department (shows up on 🦢📨🎉 Product design intake & outtake board)
    and removed on Apr 1, 2026
  14. moved this from 📨 Inbox to ✅ 🎉 Confirm and celebrate in 🦢📨🎉 Product design intake & outtakeon Apr 1, 2026
  15. moved this from ✅ 🎉 Confirm and celebrate to Done in 🦢📨🎉 Product design intake & outtakeon Apr 7, 2026
  16. fleet-release commented on Apr 7, 2026

    @fleet-release
    Contributor

    NDES deployment shines,
    Windows hosts gain swift access,
    Wi-Fi and VPN lines.

    SyncML's embrace,
    In clouds above, certificates trace,
    Managed with Fleet's grace.

    No changes required,
    In glass city admired,
    IT admin's load, lightened, admired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

#g-supply-chainSupply Chain product group:productProduct Design department (shows up on 🦢📨🎉 Product design intake & outtake board)customer-numastoryA user story defining an entire feature~activation-blockerBlocks a customer activation~contextFeature that customers consider mission critical for their particular buying situation.

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions