M365 Email Connector β Docker Composeπ
This guide explains how to run the m365-mail-connector (Outlook / Exchange Online) with Docker Compose for demos and local development.
Prerequisitesπ
- Docker installed and a network shared with the dsx-connect stack (for example,
dsx-connect-network). - The dsx-connect Docker Compose bundle (
dsx-connect-compose-bundle-<core_version>.tar.gz) downloaded and extracted locally. Examples below assume the extracted folder isdsx-connect-<core_version>/. Bundles are published at dsx-connect releases. - dsx-connect API running (Docker Compose or K8S); note the base URL exposed to the connector.
- Microsoft Graph application (client credentials flow) with the required permissions (see Reference β Azure Credentials for detailed steps):
Mail.Read(orMail.ReadWriteif remediation actions are enabled)Files.Read.Allif you plan to downloadreferenceAttachments (optional for v1)- Service principal credentials: supply via
M365_TENANT_ID,M365_CLIENT_ID,M365_CLIENT_SECRET. - List of mailbox UPNs or mailbox folders to monitor via
M365_MAILBOX_UPNS(comma-separated). - Optional public HTTPS base URL for Graph change notifications (e.g., ngrok/Cloudflare Tunnel). Microsoft Graph must be able to reach
https://<public-host>/m365-mail-connector/webhook/event.
Compose Fileπ
In the extracted bundle, start from dsx-connect-<core_version>/m365-mail-connector-<connector_version>/docker-compose-m365-mail-connector.yaml. It references the published connector image and binds port 8650 (container + host) so you can forward webhooks easily.
Core connector env (common across connectors)π
| Variable | Description |
|---|---|
DSXCONNECTOR_DSX_CONNECT_URL |
Base URL for dsx-connect (e.g., http://dsx-connect-api:8586 on the shared Docker network). |
DSXCONNECTOR_CONNECTOR_URL |
Internal callback URL dsx-connect uses to reach this connector (defaults to the service name inside the Docker network, e.g., http://m365-mail-connector:8650). |
DSXCONNECTOR_ASSET |
Alias for M365_MAILBOX_UPNS; comma-separated mailbox or mailbox/folder entries (e.g., user@contoso.com/Inbox). |
DSXCONNECTOR_FILTER |
Optional rsync-style filters for attachment names under the asset (see Reference β Filters). |
DSXCONNECTOR_ITEM_ACTION |
What dsx-connect should do on malicious verdicts (nothing, delete, move, move_tag). Set to move/move_tag when you want the connector to remediate mail. |
DSXCONNECTOR_ITEM_ACTION_MOVE_METAINFO |
Optional string that accompanies move/tag actions (defaults to dsxconnect-quarantine; keep unless you have connector-specific logic). |
M365-specific settingsπ
| Variable | Description |
|---|---|
DSXCONNECTOR_M365_MAILBOX_UPNS |
Comma-separated list of mailbox UPNs (e.g., user@contoso.com,groupscan@contoso.com). |
DSXCONNECTOR_M365_TENANT_ID, DSXCONNECTOR_M365_CLIENT_ID, DSXCONNECTOR_M365_CLIENT_SECRET |
Microsoft Graph app registration credentials. |
DSXCONNECTOR_M365_CLIENT_STATE |
Optional shared secret for webhook clientState validation. Required if you want the connector to reject spoofed notifications. |
DSXCONNECTOR_M365_WEBHOOK_URL |
Optional public HTTPS base URL for Graph webhooks. Set the base URL only; the connector appends /m365-mail-connector/webhook/event when it registers subscriptions. |
DSXCONNECTOR_DELTA_RUN_INTERVAL_SECONDS |
Background delta backfill cadence (defaults to 600). |
DSXCONNECTOR_M365_TRIGGER_DELTA_ON_NOTIFICATION |
When true, run a delta pass immediately after each webhook (default false). |
Example:
cp dsx-connect-<core_version>/m365-mail-connector-<connector_version>/sample.m365-mail.env \
dsx-connect-<core_version>/m365-mail-connector-<connector_version>/.env
# edit dsx-connect-<core_version>/m365-mail-connector-<connector_version>/.env (tenant/client creds, mailbox scope, optional webhook URL)
docker compose --env-file dsx-connect-<core_version>/m365-mail-connector-<connector_version>/.env \
-f dsx-connect-<core_version>/m365-mail-connector-<connector_version>/docker-compose-m365-mail-connector.yaml up -d
Assets, Filters, and Shardingπ
DSXCONNECTOR_ASSETmaps directly to the mailbox scope:- Entire mailbox:
user@contoso.com - Specific folder:
user@contoso.com/Finance - Use multiple connector instances when sharding large estates (per mailbox or per folder). Each instance should receive a distinct asset and mailbox list.
- Apply
DSXCONNECTOR_FILTERwhen you want to include/exclude attachment names (e.g.,**/*.zip,-tmp/). Filters are evaluated relative to the assetβs mailbox/folder path.
See Reference β Assets & Filters for sharding patterns.
Webhook Exposureπ
Microsoft Graph must reach https://<public-host>/m365-mail-connector/webhook/event.
- Expose the containerβs port
8650via an HTTPS tunnel or reverse proxy (ngrok, Cloudflare Tunnel, etc.). The tunnel terminates on the Docker host and forwards traffic tolocalhost:8650. - Set
DSXCONNECTOR_M365_WEBHOOK_URL=https://<public-host>and, optionally,DSXCONNECTOR_M365_CLIENT_STATE=<random-uuid>. - Leave
DSXCONNECTOR_CONNECTOR_URLpointing at the Docker-network hostname (e.g.,http://m365-mail-connector:8650) so dsx-connect can reach the connector internally. - Start the connector and look for
Subscriptions reconciledin the logs.
Compose vs. Kubernetesπ
- Docker Compose
DSXCONNECT_AUTH__ENABLEDremainsfalse; dsx-connect does not require enrollment tokens or DSX-HMAC inbound signatures.- The connector stores Graph credentials only in memory; no Kubernetes Secret orchestration is needed.
- Delta tokens are persisted through dsx-connectβs KV API, but the calls are unsigned in local dev.
- Kubernetes
- Enable dsx-connect enrollment + HMAC so every connector POST/GET is signed.
- Mount enrollment tokens through Secrets (
DSXCONNECT_ENROLLMENT_TOKEN) and setauth_dsxconnect.enabled=true(plusauth_dsxconnect.enrollmentSecretName) in both charts. - Ingress/NetworkPolicy defaults expose only
/webhook_eventpublicly and restrict other paths to dsx-connect.
Use Compose for local validation and switch to the Helm charts (connectors/m365_mail/deploy/helm/) for production-grade deployments with enrollment and DSX-HMAC enforced.
Azure Credentials Referenceπ
See Reference β Azure Credentials for a portal walkthrough, CLI automation, and Graph API fallback commands for registering the application, capturing tenant/client IDs, generating client secrets, and granting Microsoft Graph application permissions.
Exposing the Webhook Locally (ngrok example)π
Microsoft Graph delivers notifications only to publicly reachable HTTPS endpoints. For local testing:
- Install ngrok and run
ngrok http 8650. ngrok prints both HTTP and HTTPS URLs (e.g.,https://<random>.ngrok-free.app). - Keep
DSXCONNECTOR_CONNECTOR_URL=http://127.0.0.1:8650so dsx-connect calls the connector over localhost. - Set
DSXCONNECTOR_M365_WEBHOOK_URL=https://<random>.ngrok-free.appso the connector registers the ngrok address with Graph. - Set
DSXCONNECTOR_M365_CLIENT_STATE=<random-uuid>if you want the connector to validate theclientStateechoed by Graph. - Restart the connector. Subscription reconciliation will now succeed, and Graph notifications will arrive at the tunneled endpoint.
Any secure tunnel (Cloudflare Tunnel, Azure Relay, etc.) works similarly: expose port 8650, note the HTTPS URL, and place it in DSXCONNECTOR_M365_WEBHOOK_URL.
Faster Scanning After Notificationsπ
- The connector relies on Graph delta queries for durability. By default it waits
DSXCONNECTOR_DELTA_RUN_INTERVAL_SECONDS(600s) between runs. During that interval you may see multiple webhook events, but attachments are processed when the next delta pass runs. - To reduce latency, either lower the interval (e.g.,
DSXCONNECTOR_DELTA_RUN_INTERVAL_SECONDS=30) or setDSXCONNECTOR_M365_TRIGGER_DELTA_ON_NOTIFICATION=trueso the connector runs a delta pass immediately after each webhook. - Even with the trigger enabled, the periodic delta loop stays active to recover from missed notifications.
Operational Notesπ
- The background delta runner and
@connector.full_scanreuse the same Graph delta code path. Trigger a manual pass withPOST /dsx-connect/api/v1/connectors/full_scan/{uuid}(optional?limit=N). - Delta tokens live under the
m365/delta:<upn>namespace in dsx-connectβs KV store. For Compose, the connector automatically initializes them when the API is reachable. - Webhooks deliver near real-time attachment notifications; delta backfill handles drift and initial load. Keep the docker container running so subscription renewals (30-minute reconciliation loop) continue.
- Remediation actions kick in as soon as
DSXCONNECTOR_ITEM_ACTIONis set todelete,move, ormove_tag; no extra toggle is required (the legacyDSXCONNECTOR_ENABLE_ACTIONSvariable is ignored unless explicitly set tofalsefor compatibility).
TLS Optionsπ
See Deploying with SSL/TLS for Docker Compose examples (core + connectors), including runtime-mounted certs and local-dev self-signed cert generation.
Example override file (bundle root):
# dsx-connect-<core_version>/m365-mail-connector-<connector_version>/docker-compose.tls.override.yaml
services:
m365_mail_connector:
volumes:
- ../certs:/app/certs:ro
Then run with both compose files:
docker compose --env-file dsx-connect-<core_version>/m365-mail-connector-<connector_version>/.env \
-f dsx-connect-<core_version>/m365-mail-connector-<connector_version>/docker-compose-m365-mail-connector.yaml \
-f dsx-connect-<core_version>/m365-mail-connector-<connector_version>/docker-compose.tls.override.yaml up -d
Asset vs Filterπ
- Asset defines the coarse scan boundary (e.g. bucket, path, container, mailbox).
- Filters apply include/exclude rules under that boundary.
If DSX-Connect Core is using TLS, set DSXCONNECTOR_DSX_CONNECT_URL protocol to https:
DSXCONNECTOR_DSX_CONNECT_URL=https://dsx-connect-api:8586