fleet icon indicating copy to clipboard operation
fleet copied to clipboard

Microsoft (Intune) compliance partner: fleetdm.com proxy

Open noahtalerman opened this issue 9 months ago • 12 comments

Goal

User story
As a Fleet contributor,
I want to hit fleetdm.com to get Fleet's Microsoft API token
so that I can use the API token, without hardcoding it, to build the Intune compliance partner features.

Key result

Become a Microsoft compliance partner to enable first-party ZTA (zero-trust access) integration

Original requests

  • #24423

Context

  • Product designer: @noahtalerman + @lucasmrod

Changes

Product

  • [ ] fleetdm.com changes:
  • Add three new environment variables:
    • Fleet's multi-tenant Entra ID “application ID” (aka client_id).
    • Fleet's multi-tenant Entra ID "client secret" (aka client_secret).
    • "MS-API-Key" to allow only cloud instances to use the proxy.
  • Create skeleton code for the proxy so that many engineers can collaborate on the implementation of the endpoints.
  • Add documentation on how to run the proxy locally on workstations (for Fleet server development which will use the APIs).
  • Add the APIs defined below.

0. Authentication

All Microsoft Compliance API endpoints will require an authentication bearer token, "MS-API-Key". This is to prevent use of the MS APIs from non-Cloud instances.

The proxy will use the HTTP Origin header and store that on the integration record ("entra_tenant_id" and "origin URL" will be unique).

1. Add new "Create Microsoft compliance partner" API endpoint

POST fleetdm.com/api/v1/microsoft-compliance-partner

This endpoint creates the integration on the proxy database, but doesn't communicate with Microsoft yet. It checks the origin header of requests to get the Fleet server URL and returns a badRequest response if it is not set. If a database record exists for that Fleet server URL, it will return a 409 response if the tenant is marked as setupCompleted = true, or delete the incomplete tenant record if setup has not been completed. It then creates a database record with the following information:

  {
    fleetServerSecret: '....',// Randomly generated string
    entraTenantId: '....',// Provided in the request body
    fleetInstanceUrl: '....',// The value of the origin header
    setupCompleted: false,
    adminConsented: false,
  }

It then responds to the Fleet server with the provided entra_tenant_id and randomly generated fleet_server_secret All subsequent requests must send the "entra_tenant_id" + "fleet_server_secret" and on the request body/query.

Request from Fleet server:

{
  "entraTenantId": "abc123"
}

Response to Fleet server:

{
  "fleet_server_secret": "def456",
  "entra_tenant_id": "abc123",
}

2. Add new "Get settings" API endpoint

GET /api/v1/microsoft-compliance-partner/settings?entra_tenant_id=abc123&fleet_server_secret=def456

Response to Fleet server:

{
  "entra_tenant_id": "abc123",
  "setup_done": true/false,
  "admin_consented": true/false,
  "admin_consent_url": "https://login.microsoftonline.com/{entraTenantId}/adminconsent?entraApplicationId=foo123&state=12345&redirect_uri=https%3A%2F%2Ffleetdm.com%2Fapi%2Fv1%2Fmicrosoft-compliance-partner%2Fadminconsent" // always set, regardless of admin_consented value.
  "setup_error": "...."// Only set if there was an error when provisioning the tenant after the admin consents.
}
  • Returns HTTP 404 if the integration associated to the provided fleet_server_secret does not exist.
  • If admin_consented is true, then "admin_consent_url" is not set in the response (not needed anymore).
  • If admin_consented=false,The response will always include an admin consent URL, the Fleet server will redirect the user to the provided admin_consent_url.
  • Fields in the admin_consent_url:
    • entraTenantId
    • entraApplicationId this is the "client_id" (configured in the proxy).
    • redirect_uri: Is the URI of the webhook: https://fleetdm.com/api/v1/microsoft-compliance-partner/adminconsent (see webhook API endpoint number 8).
    • state: should be a random token to authenticate the webhook request (see webhook API endpoint number 8). It should be stored on a column the integration row.
  • If an error occurred when the user was redirected to the admin consent webhook (Either because there was an error setting up the new tenant or because the admin did not consent), it will be sent in the response body as a setup_error.

3. Add new API endpoint for the admin consent webhook

GET fleetdm.com/api/v1/microsoft-compliance-partner/adminconsent?tenant=aaaabbbb-0000-cccc-1111-dddd2222eeee&state=state=12345&admin_consent=True

Webhook fields:

  • tenant: entraTenantId.
  • state: This is used to authenticate the webook request.

After receiving this request, integration is marked as admin_consented=true, the state token is cleared from the database, and setups the created integration by using the Microsoft APIs to (1) provision the tenant and (2) create the necessary objects for the Compliance partner integration. If the setup is done correctly then a 200 is returned without any response body and the proxy marks the integration as done (setup_done=true). If there's an issue during the setup process, then a non-200 is returned and a "setupError" is set on the website record to that will be included in the response from the /api/v1/microsoft-compliance-partner/settings endpoint.

After the setup has been completed, the endpint redirects the user to their Fleet instance.

More information about this webhook can be found here.

4. Add new "DELETE Microsoft compliance partner" API endpoint

DELETE fleetdm.com/api/v1/microsoft-compliance-partner?entra_tenant_id=abc123&fleet_server_secret=def456

Response to Fleet server:

{
  "error": "..."
}

The endpoint “deprovisions” the integration using the same API as the one used in the POST fleetdm.com/api/v1/microsoft-compliance-partner but with the “provisioned” argument set to “2” (meaning “deprovisioned”). Thereafter, it deletes the integration from the fleetdm.com database. Response is 200 if it was successfully deprovisioned and deleted. If there's an issue on the deletion then a non-200 is returned and an "error" field specifies the error in detail.

5. Add new API endpoint to "set the compliance status for the device on Intune":

Send compliance status for the device using the APIs outlined in “2.3 Partner Device Data Sync API”.

POST fleetdm.com/api/v1/microsoft-compliance-partner/device

Request from Fleet server:

{
  "fleetServerSecret": "def456",
  "entraTenantId": "abc123",
  "deviceId": "bar123" // Entra ID “device ID”
  "deviceName": "foobar123", // device friendly name (hosts.display_name)
  "os": "macOS",
  "osVersion": "15.3.1",
  "userId": "user123" // Entra ID “user ID”.
  "compliant": true/false,
  "lastCheckInTime": 1740691819 // from hosts.details_updated_at timestamp
}

Response to Fleet server:

{
  "message_id": "message123" // OperationLocation from the Microsoft API.
}

The proxy will need to store the "message_id" in the database with the status of the message ("not started", "running", "completed", "failed", etc.) and its "OperationLocation" which is to be used to check the status of the message.

6. Add new API endpoint to "get the result of setting the compliance status for the device on Intune"

Intune API is asynchronous thus we need to check if the POST fleetdm.com/api/v1/microsoft-compliance-partner/device was successful or not.

GET fleetdm.com/api/v1/microsoft-compliance-partner/device/message?entraTenantID=abc123&fleetServerSecret=def456&messageID=message123

Response to Fleet server:

{
  "message_id": "message123",
  "status": "completed",
  "detail": "..." // detail is set if there's a failure
}

7. Add heartbeat implementation to the proxy

Implement heartbeat cron that runs daily which will use the “2.2.5 Tenant Heartbeat API” to keep all integrations alive.


  • [x] UI changes: No changes.
  • [x] CLI (fleetctl) usage changes: No changes.
  • [x] YAML changes: No changes.
  • [x] REST API changes: No changes.
  • [x] Fleet's agent (fleetd) changes: No changes.
  • [x] GitOps mode changes: No changes.
  • [x] Activity changes: No changes.
  • [x] Permissions changes: No changes.
  • [x] Changes to paid features or tiers: Fleet Premium
  • [x] Transparency changes: No changes.
  • [x] First draft of test plan added: @noahtalerman: No test plan needed.
  • [x] Other reference documentation changes: No changes.
  • [x] Once shipped, requester has been notified
  • [ ] Once shipped, dogfooding issue has been filed

Engineering

Remove this section because there's no Fleet product changes in this story.

QA

No test plan for this proxy work.

noahtalerman avatar Feb 21 '25 15:02 noahtalerman

@lucasmrod passing this to you to add fleetdm.com API wireframes/specs.

Here's what we're doing for Android for reference: https://github.com/fleetdm/fleet/issues/26270

noahtalerman avatar Feb 26 '25 15:02 noahtalerman

FYI @lucasmrod I updated URLs to /microsoft-compliance-partner instead of /microsoft/compliance-partner because I'm assuming we learn towards less nesting (flatter) for fleetdm.com API endpoints.

If we add API endpoints for another Microsoft service in the future we can do something like /microsoft-service-x

I could be wrong though. I think up to @eashaw.

noahtalerman avatar Feb 28 '25 21:02 noahtalerman

Check to see if Fleet server is cloud-managed

Hey @lucasmrod just checking, do we know how this is going to work? Probably want to update fleetdm.com database to map between license key and cloud-managed true/false.

noahtalerman avatar Feb 28 '25 21:02 noahtalerman

Left a comment in the other issue, but so we have it tracked here: we should make sure the fleetdm.com proxy has a record of which licenses belong to cloud-managed Fleet instances and throw an error if we try to use the integration for any self-hosted instances.

rachaelshaw avatar Mar 12 '25 21:03 rachaelshaw

Left a comment in the other issue, but so we have it tracked here: we should make sure the fleetdm.com proxy has a record of which licenses belong to cloud-managed Fleet instances and throw an error if we try to use the integration for any self-hosted instances.

Thanks! We agreed the proxy will be allowing only one integration per cloud origin URL.

lucasmrod avatar Mar 17 '25 17:03 lucasmrod

We met with @getvictor and @eashaw and agreed that the two proxies will look the same around using "fleet_server_key" as the secret that identifies each integration/customer (returned on the first POST to create the integration).

But the one difference with the Android proxy is that the fleetdm.com proxy for MS compliance partner (due to requirements) will require all requests to provide a shared API key as authentication bearer token (HTTP header).

We've updated the description to account for this.

/cc @noahtalerman

lucasmrod avatar Mar 17 '25 17:03 lucasmrod

I've created https://github.com/fleetdm/fleet/issues/27200 to answer some questions during the development of the integration. (There's no public information to answer these, so we'll need to test the assumptions during the development of the integration.)

lucasmrod avatar Mar 17 '25 19:03 lucasmrod

@lucasmrod For "7. Add new API endpoint for the admin consent webhook": Does the webhook need to do anything else after the database record for the tenant is updated, or should it just return a 200 response?

eashaw avatar Mar 20 '25 17:03 eashaw

@lucasmrod For "7. Add new API endpoint for the admin consent webhook": Does the webhook need to do anything else after the database record for the tenant is updated, or should it just return a 200 response?

That should be it. Update columns in the integration (set admin_consented=true and clear state), and return 200. The UI + Fleet server will be using the GET settings to "poll" when the user loads the integration settings.

lucasmrod avatar Mar 20 '25 17:03 lucasmrod

That should be it. Update columns in the integration (set admin_consented=true and clear state), and return 200. The UI + Fleet server will be using the GET settings to "poll" when the user loads the integration settings.

@lucasmrod Sounds good! How would you like the webhook to respond if the admin does not grant permissions? https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#error-response

I currently have it returning a bad request response.

eashaw avatar Mar 20 '25 17:03 eashaw

Sounds good! How would you like the webhook to respond if the admin does not grant permissions?

I believe we want to record the error in the integration row, like a admin_consented_error string or something like that and also return HTTP 200 (to indicate to Microsoft that we have processed the webhook successfully).

And we could improve the UI in the future to differentiate whether the admin has not yet consented or has denied.

lucasmrod avatar Mar 20 '25 17:03 lucasmrod

@eashaw I see this is in the "In review" column but I don't see an associated PR. Would you please link this story to it? Thanks!

lukeheath avatar Apr 03 '25 16:04 lukeheath

@xpkoala @sharon-fdm I am leaving this on the Orchestration project board and assigned to milestone 4.70.0 because it technically shipped and was announced. Once this reaches "Ready for release" state, please move it to the Drafting project and the "Confirm and celebrate" column. Thank you!

lukeheath avatar Jun 30 '25 23:06 lukeheath

API tokens flow like rivers, Security in cloud's embrace, Fleet and Microsoft, together.

fleet-release avatar Jul 31 '25 21:07 fleet-release