diff --git a/articles/enable-okta-verify-on-windows-using-a-scep-configuration-profile.md b/articles/enable-okta-verify-on-windows-using-a-scep-configuration-profile.md index 8b7c29690f..8c5d11ed7f 100644 --- a/articles/enable-okta-verify-on-windows-using-a-scep-configuration-profile.md +++ b/articles/enable-okta-verify-on-windows-using-a-scep-configuration-profile.md @@ -2,216 +2,173 @@ ## Introduction -This guide explains how to enable [Okta Verify](https://help.okta.com/en-us/content/topics/mobile/okta-verify-overview.htm) on Windows using a SCEP client certificate delivered by the Windows **ClientCertificateInstall** CSP and then applied using an **Exec** command. This pattern is useful when your MDM payload cannot send **Add or Replace** nodes together with an **Exec** in one transaction. +This guide explains how to enable Okta Verify on Windows using a SCEP client certificate delivered by the Windows ClientCertificateInstall CSP. Fleet supports Exec commands in configuration profiles, allowing you to deploy the SCEP configuration and trigger enrollment in a single profile. -You will deploy the SCEP profile first, then call **Enroll** via Exec to request the client certificate. +## Files -**Files** -* [Profile XML](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/configuration-profiles/install%20Okta%20attestation%20certificate%20-%20%5BBundle%5D.xml) -* [Powershell script](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/scripts/trigger%20scep%20enrollment.ps1) +**Profile XML**: [install Okta attestation certificate - [Bundle].xml](https://github.com/fleetdm/fleet/blob/main/docs/solutions/windows/configuration-profiles/install%20Okta%20attestation%20certificate%20-%20%5BBundle%5D.xml) ---- - -## Order at a glance - -1. Get your CA **thumbprint**, choose **{yourCertName}**, and locate your SCEP **URL** and **Challenge**. -2. Create Fleet **secrets** for URL, Challenge, CA thumbprint, and API token. -3. Use the Fleet repo XML CSP profile and replace only the required placeholders. -4. Deploy the profile to devices. -5. Update the **Exec** script to use the same `{yourCertName}` and your secrets, then run it. -6. Verify the certificate is installed. -7. Plan and automate **renewal**. - ---- +The profile is ready to use as-is. Fleet will replace the `$FLEET_SECRET_*` variables with your actual values when deploying to each device. ## Prerequisites -* Windows devices enrolled to Fleet MDM -* Okta SCEP endpoint with a static challenge -* Root CA certificate thumbprint for the SCEP issuing CA -* Fleet API token stored as a secret -* Optional GitOps workflow if you manage Fleet configuration as code +### 1. Gather your Okta details ---- +Collect from your Okta tenant: -## Step 1. Collect your values +* **SCEP URL**: Your Okta SCEP endpoint +* **SCEP Challenge**: Your static SCEP challenge (plain text, avoid special characters) +* **CA Thumbprint**: The SHA-256 thumbprint of your Okta CA certificate -### 1.1 Get the CA thumbprint +### 2. Get your CA thumbprint -**Windows PowerShell** -```powershell -Get-FileHash -Path "C:\Path\To\ca.cer" -Algorithm SHA256 | Select-Object -ExpandProperty Hash -``` +Download your Okta CA certificate and extract the SHA-256 thumbprint. -**macOS or Linux** +**macOS/Linux**: ```bash openssl x509 -in ~/Downloads/ca.cer -noout -fingerprint -sha256 -# Output looks like: -# SHA256 Fingerprint=E2:18:D7:A7:B0:DF:ED:79:B2:05:73:BA:79:CB:14:B1:FE:EA:D2:7B -# Remove the colons: -# E218D7A7B0DFED79B20573BA79CB14B1FEEAD27B ``` -Use the hex string without colons or spaces in the secret you will create below. - -### 1.2 Choose your SCEP node name - -Pick a simple value for `{yourCertName}`, for example `OKTA` or `OKTAVERIFY`. You will use this exact value: -* in the XML profile path `.../SCEP/{yourCertName}/Install/...` -* in the Exec path `.../SCEP/{yourCertName}/Install/Enroll` - -### 1.3 Get your SCEP URL and Challenge - -* `{yourScepUrl}` is your Okta SCEP endpoint. -* `{yourScepChallenge}` is your static SCEP challenge. This profile expects **plain text**. Avoid special characters that can break XML or transport. Recommended: letters, numbers, underscore. If your challenge contains characters such as `! @ # $ % ^ & * ( )`, rotate to a simpler value. - -**Quick checklist** -* {yourCertName} chosen -* {yourScepUrl} confirmed -* {yourScepChallenge} validated (plain text, simple characters) -* {yourScepCAThumbprint} ready (no colons, no spaces) - ---- - -## Step 2. Create Fleet secrets - -Follow Fleet’s guide: https://fleetdm.com/guides/secrets-in-scripts-and-configuration-profiles - -Create these secrets in Fleet (Controls → Variables) or via GitOps: - -| Secret name | Value you collected | -|---|---| -| `FLEET_SECRET_OKTA_SCEP_URL` | `{yourScepUrl}` | -| `FLEET_SECRET_OKTA_SCEP_CHALLENGE` | `{yourScepChallenge}` (plain text, simple characters) | -| `FLEET_SECRET_OKTA_CA_THUMBPRINT` | SHA256 thumbprint with no colons, no spaces -| `FLEET_SECRET_API` | Fleet API token used by the Exec script | - -Optional convenience secret: -* `FLEET_SECRET_OKTA_CERT_NAME` set to `{yourCertName}` - -**Security notes** -* Fleet does not hide the secret in script results. Don't print/echo your secrets to the console output. - ---- - -## Step 3. Use Fleet’s XML CSP profile - -Source file in the Fleet repo: -``` -docs/solutions/Windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml +**Windows**: +```powershell +certutil -hashfile ca.cer SHA256 ``` -Only change the following placeholders: - -* `{yourCertName}` set to the SCEP node name you chose in Step 1.2 -* `{yourScepUrl}` replaced with `$FLEET_SECRET_OKTA_SCEP_URL` -* `{yourScepChallenge}` replaced with `$FLEET_SECRET_OKTA_SCEP_CHALLENGE` (plain text, simple characters) -* `{yourScepCAThumbprint}` replaced with `$FLEET_SECRET_OKTA_CA_THUMBPRINT` (no colons, no spaces) - -**Important** -Use the same `{yourCertName}` in both the profile path and the Exec path. If they differ, the Exec will 404. - -### Replace just these lines in the profile - -```xml - - - - ./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/ServerURL - - chr - $FLEET_SECRET_OKTA_SCEP_URL - - - - - - ./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/Challenge - - chr - $FLEET_SECRET_OKTA_SCEP_CHALLENGE - - - - - - ./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/CAThumbprint - - chr - $FLEET_SECRET_OKTA_CA_THUMBPRINT - +Output will look like: +``` +SHA256 Fingerprint=E2:18:D7:A7:B0:DF:ED:79:B2:05:73:BA:79:CB:14:B1:FE:EA:D2:7B ``` -Keep the other defaults from the file (KeyLength 2048, KeyUsage 160, HashAlgorithm `SHA-1`, SubjectName `CN=$FLEET_VAR_HOST_UUID managementAttestation`, EKUMapping, RetryCount, RetryDelay). - -Deploy the profile to your Windows hosts using Fleet. - ---- - -## Step 4. Update the Exec script and run Enroll - -Script location in repo: -`docs/solutions/Windows/scripts/trigger-scep-enrollment.ps1` - -Your Exec must target the same `{yourCertName}` as in the profile. Example path: +Remove the colons: ``` -./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/Enroll +E218D7A7B0DFED79B20573BA79CB14B1FEEAD27B ``` -Update your PowerShell script to read the API token from the secret, set your node name, and build the correct LocURI. +### 3. SCEP challenge requirements + +* Your SCEP challenge should be plain text +* Avoid special characters that can break XML or transport +* **Recommended**: letters, numbers only +* If your challenge contains `! @ # $ % ^ & * ( ) _`, rotate to a simpler value + +## Quick checklist + +* SCEP URL confirmed +* SCEP challenge validated (plain text, simple characters) +* CA thumbprint ready (no colons, no spaces) + +## Deployment + +### 1. Create Fleet secrets + +Follow Fleet's guide: https://fleetdm.com/guides/secrets-in-scripts-and-configuration-profiles + +Create these secrets in Fleet (**Controls** > **Variables**) or via GitOps: + +| Secret name | Value | +|-------------|-------| +| `OKTA_SCEP_URL` | Your SCEP endpoint URL | +| `OKTA_SCEP_CHALLENGE` | Your challenge (plain text, simple characters) | +| `OKTA_CA_THUMBPRINT` | Your thumbprint (no colons, no spaces) | + +### 2. Deploy the profile + +1. Download the profile XML (link above) +2. Navigate to **Controls** > **OS settings** > **Custom settings** in Fleet +3. Upload the XML file (no editing required) +4. Assign to your team or hosts + +Fleet automatically replaces `$FLEET_SECRET_OKTA_SCEP_URL`, `$FLEET_SECRET_OKTA_SCEP_CHALLENGE`, and `$FLEET_SECRET_OKTA_CA_THUMBPRINT` when deploying. The certificate ID is automatically managed by Fleet using `$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID`. + +## Verify the certificate + +### Check the User cert store + +Open PowerShell as the logged-in user (not administrator): ```powershell -# ----- USER SETTINGS ----- -# Add your secrets in Fleet (Controls > Variables) or via GitOps. -# The variable named "API" becomes FLEET_SECRET_API -# Full guidance: https://fleetdm.com/guides/secrets-in-scripts-and-configuration-profiles - -$NODE_NAME = "OKTA" # must match {yourCertName} in the XML -$FLEET_API = "$FLEET_SECRET_API" # injected by Fleet - -$locUri = "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$NODE_NAME/Install/Enroll" -# ...construct and send the Exec command body referencing $locUri... +Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object {$_.Subject -like "*managementAttestation*"} ``` -Run the script from Fleet so secrets inject automatically. +Expected output: +``` +Thumbprint Subject +---------- ------- +A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0 CN= managementAttestation +``` ---- +### Check the device management logs -## Step 5. Verify enrollment - -**PowerShell** ```powershell -Get-ChildItem Cert:\LocalMachine\My | - Where-Object { $_.Subject -like "*managementAttestation*" } | - Format-List Subject, Thumbprint, NotAfter +Get-WinEvent -LogName Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin -MaxEvents 50 ``` -**GUI** -* Open `certlm.msc` -* Personal > Certificates -* Confirm a certificate whose Subject contains `managementAttestation` +### Verify in Okta ---- - -## Step 6. Renewal - -* Automated workflow. Use a Fleet query to find certificates expiring within 30 days and trigger the Exec command for those hosts. - -Find certs expiring within 30 days: -```TODO! -``` - ---- +1. Log in to Okta Admin Console +2. Navigate to **Reports** > **System Log** +3. Filter for device attestation events +4. Confirm the device appears as managed ## Troubleshooting -* Exec returns 404: node name mismatch. Ensure `{yourCertName}` in XML equals `$NODE_NAME` in the script. -* Enrollment fails immediately: check `ServerURL`, `CAThumbprint` format, and that the device can reach the SCEP URL. -* Challenge rejected: try a simpler plain text challenge, or base64 encode and update the XML ``. -* Nothing in `Cert:\LocalMachine\My`: review Device Management logs - ```powershell - Get-WinEvent -LogName Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin -MaxEvents 50 - ``` +### Exec returns 404 + +* Check that all three Fleet secrets exist (`OKTA_SCEP_URL`, `OKTA_SCEP_CHALLENGE`, `OKTA_CA_THUMBPRINT`) +* Verify the profile was uploaded correctly +* Review Device Management logs for details + +### Enrollment fails immediately + +Check: +* ServerURL is correct +* CAThumbprint format (no colons or spaces) +* Device can reach the SCEP URL (network/firewall) + +### Challenge rejected + +* Try a simpler plain text challenge (alphanumeric only) +* Avoid special characters, especially underscores +* If your challenge contains `! @ # $ % ^ & * ( ) _`, rotate to a simpler value in Okta + +### Nothing in Cert:\LocalMachine\My + +**Note**: Okta requires certificates in the **User** store (`Cert:\CurrentUser\My`), not the Device store. + +Review Device Management logs: +```powershell +Get-WinEvent -LogName Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin -MaxEvents 50 +``` + +## Plan and automate renewal + +### Monitor expiration + +Use a Fleet policy to identify devices with certificates expiring within 30 days: + +```sql +SELECT 1 +FROM certificates +WHERE + common_name LIKE '%managementAttestation%' + AND julianday(not_valid_after) - julianday('now') < 30; +``` + +This policy will: +- **Fail**: When a certificate exists and expires within 30 days (needs renewal) +- **Pass**: When no certificate exists yet, or certificate is valid for more than 30 days + +### Automated workflow + +To renew certificates, you can: + +**Manual redeployment**: Redeploy the same configuration profile to trigger renewal + +## Important notes + +* **Fleet secrets**: Fleet does not hide secrets in profile results. Make sure all three secrets are created before deploying (`OKTA_SCEP_URL`, `OKTA_SCEP_CHALLENGE`, `OKTA_CA_THUMBPRINT`). +* **User vs Device store**: Okta requires certificates in the User store. The profile uses `./User/` paths. If you use `./Device`, the device will **not** be marked as managed in Okta. +* **Certificate ID**: Fleet automatically manages the certificate node name using `$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID` - no manual configuration needed. --- diff --git a/docs/solutions/windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml b/docs/solutions/windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml index 34b003981a..e94f0bec8a 100644 --- a/docs/solutions/windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml +++ b/docs/solutions/windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml @@ -1,9 +1,9 @@ - + - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}} + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID node @@ -14,7 +14,7 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/RetryCount + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/RetryCount int @@ -26,7 +26,7 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/RetryDelay + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/RetryDelay int @@ -38,7 +38,7 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/KeyUsage + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/KeyUsage int @@ -50,7 +50,7 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/KeyLength + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/KeyLength int @@ -62,7 +62,7 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/HashAlgorithm + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/HashAlgorithm chr @@ -74,19 +74,19 @@ - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/SubjectName + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/SubjectName chr - CN=$FLEET_VAR_HOST_UUID managementAttestation + CN=$FLEET_VAR_HOST_HARDWARE_SERIAL managementAttestation - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/EKUMapping + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/EKUMapping chr @@ -95,39 +95,47 @@ - + - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/ServerURL + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/ServerURL chr - {{yourScepUrl}} + $FLEET_SECRET_OKTA_SCEP_URL - + - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/Challenge + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Challenge chr - {{yourScepChallenge}} + $FLEET_SECRET_OKTA_SCEP_CHALLENGE - + - ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/{{yourCertName}}/Install/CAThumbprint + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/CAThumbprint chr - {{yourScepCAThumbprint}} + $FLEET_SECRET_OKTA_CA_THUMBPRINT - \ No newline at end of file + + + + + + ./User/Vendor/MSFT/ClientCertificateInstall/SCEP/$FLEET_VAR_SCEP_WINDOWS_CERTIFICATE_ID/Install/Enroll + + +