Skip to content

CrowdStrike Case: 02501603 - #259

Merged
carlosmmatos merged 8 commits into
CrowdStrike:mainfrom
dwarrendxc:main
Jul 6, 2026
Merged

CrowdStrike Case: 02501603 #259
carlosmmatos merged 8 commits into
CrowdStrike:mainfrom
dwarrendxc:main

Conversation

@dwarrendxc

Copy link
Copy Markdown
Contributor

Summary

Microsoft has announced the deprecation of the HTTP Data Collector API, which is the mechanism currently used by the Falcon Integration Gateway to transmit detection data to Azure Log Analytics. Per the migration guidance, this API will be retired in the near future. This PR replaces it with the Azure Monitor Logs Ingestion API and transitions authentication from shared key access to RBAC-based identity access.

These changes have been tested in a lab environment and validated end-to-end.


Changes

init.py

  • Removed build_signature() and the SharedKey-based post_data() function
  • Added post_data() using the azure-monitor-ingestion SDK (LogsIngestionClient.upload())
  • Retained post_data_legacy() with the original HMAC implementation for backward compatibility
  • Runtime.__init__() now constructs the credential and LogsIngestionClient once at startup based on auth_method, rather than per event
  • Supports three authentication modes:
    • workload_identityDefaultAzureCredential, suitable for AKS with Azure Workload Federated Identity; no secrets required
    • client_secretClientSecretCredential with explicit tenant, client ID, and secret
    • legacy — original SharedKey path, emits a deprecation warning at startup

init.py

  • Added 6 new ENV_DEFAULTS entries: AZURE_AUTH_METHOD, AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_DCR_ENDPOINT, AZURE_DCR_IMMUTABLE_ID
  • Replaced the flat AZURE validation block in validate_backends() with mode-aware validation; required fields differ per auth method

defaults.ini

  • Added new [azure] keys: auth_method = legacy, tenant_id, client_id, client_secret, dcr_endpoint, dcr_immutable_id
  • Existing workspace_id and primary_key keys retained for legacy mode

requirements.txt

  • Added azure-monitor-ingestion and azure-identity

falcon-integration-gateway.yaml

  • Added a ServiceAccount resource with azure.workload.identity/client-id and azure.workload.identity/tenant-id annotations to support cross-tenant Workload Federated Identity
  • Added azure.workload.identity/use: "true" pod label and serviceAccountName reference to opt the pod into credential injection by the Azure Workload Identity webhook
  • Removed WORKSPACE_ID and PRIMARY_KEY as active secret/env references (retained as commented-out block for legacy mode users)
  • Updated ConfigMap config.ini to use the new [azure] keys
  • Added detailed prerequisite comments covering OIDC issuer retrieval, subject format, and multi-instance guidance

falcon-integration-gateway-dcr.bicep (new)
falcon-integration-gateway-table.bicep (new)

  • Bicep templates that automate creation of the FalconIntegrationGatewayLogs_CL custom table, the Data Collection Rule (kind: Direct), and the Monitoring Metrics Publisher role assignment scoped to the DCR
  • Cross-subscription/resource group workspace support via a module pattern
  • Outputs dcrEndpoint and dcrImmutableId for direct use in FIG config

README.md

  • Fully rewritten to document all three auth modes, Azure-side setup steps, AKS cluster prerequisites, per-instance ServiceAccount configuration, Bicep deployment instructions, and deprecation notice for legacy mode

init.py

  • Version bumped to 3.5.1

Bug fixes included

  • Submitter.log() return value corrected from dumps(json_data) (string) to json_data (list) — the SDK's upload() method expects a list, not a pre-serialised string
  • init.py — TLSSysLogHandler initialisation commented out at module level to prevent startup failure when WorkspaceOne is not configured; PROTOCOL_TLSv1_2 was removed in Python 3.12

Testing

  • workload_identity mode validated on AKS (cross-tenant) — detections confirmed in FalconIntegrationGatewayLogs_CL in Tenant B
  • client_secret mode validated with explicit credentials
  • legacy mode validated — deprecation warning logged, detections delivered via original API path
  • Bicep template deployed and outputs confirmed usable as FIG config values
  • Two concurrent FIG instances on the same cluster confirmed to deliver independently to separate workspaces

@carlosmmatos carlosmmatos left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — the migration is well put together. Building the credential and LogsIngestionClient once in Runtime.__init__(), the clean three-mode dispatch, the least-privilege DCR-scoped role assignment in the Bicep, and the rewritten README are all solid. A few things need to be addressed before this can merge.

Blocking

1. Legacy SharedKey path is broken by the log() return-type change

Submitter.log() was changed from return dumps(json_data) to return json_data so the new SDK's upload() receives a list. But post_data_legacy() still treats its body argument as a serialized JSON string:

content_length = len(body)                                    # body is a list -> 1, not the byte length
...
response = post(uri, data=body, headers=headers, timeout=60)  # requests gets a list of dicts

I ran the legacy path directly with the dependencies installed. It doesn't just sign a wrong Content-Length into the HMAC (which would 403) — requests.post(data=<list of dicts>) raises before anything is sent:

ValueError: too many values to unpack (expected 2)

So legacy mode crashes on the first detection. flake8 flags the same root cause independently: F401 'json.dumps' imported but unused — nothing serializes anymore. Re-serializing at the top of post_data_legacy() (e.g. body = dumps(body)) fixes both the crash and the unused-import error.

2. flake8 fails the CI gate (flake8 fig exits non-zero)

12 violations, all in the two files below:

  • fig/backends/azure/__init__.py: F401 'json.dumps' imported but unused (resolved by fixing #1)
  • fig/backends/workspaceone/serverlog/__init__.py: 5× F401 unused imports + 6× E265 block-comment formatting, from commenting out the handler block (see #3)

Please split out of this PR

3. WorkspaceONE serverlog change is an unrelated regression

fig/backends/workspaceone/serverlog/__init__.py comments out the entire TLSSysLogHandler setup at module level, which silently disables syslog delivery for all WorkspaceONE users. The stated rationale — PROTOCOL_TLSv1_2 was removed in Python 3.12 — doesn't hold up: the constant is still present (deprecated, not removed) on 3.13/3.14, and this project pins python_requires='>=3.6, <3.12' with CI and the container both on 3.11, so 3.12 isn't a target. This is unrelated to the Azure migration and accounts for 11 of the 12 flake8 failures. Recommend pulling it into its own PR; if WS1 genuinely needs TLS-version handling, switching to ssl.SSLContext / ssl.TLSVersion would be the forward-compatible approach rather than disabling the handler.

4. Version bump to 3.5.1

The fig/__init__.py bump is bundled into a feature PR. The repo's convention is a standalone version-bump PR (e.g. #257). Please drop it here and let the release process own it.

Minor

  • Pin the new dependencies. azure-monitor-ingestion and azure-identity are unpinned, unlike their neighbors. Suggest azure-monitor-ingestion==1.0.4 and azure-identity>=1.25.3 (the versions that resolve and install cleanly on 3.11).
  • Python floor. Both packages require Python ≥3.8, which conflicts with the >=3.6 floor in setup.py. Since CI and Docker only build 3.11, recommend updating the documented support range to >=3.8.
  • post_data() only catches HttpResponseError. Credential failures (ClientAuthenticationError) raised by upload() will propagate uncaught. A broader catch or a brief note would be worth adding.

Verified and looking good

  • Mode-aware config validation in fig/config/__init__.py — required fields enforced correctly per auth method, invalid auth_method rejected.
  • Bicep: Monitoring Metrics Publisher role GUID is correct, role assignment scoped to the DCR only (true least privilege), kind: 'Direct' with the DCR-level logsIngestion endpoint is the right pattern, and the cross-subscription module pattern is sound.
  • k8s manifest: ServiceAccount + workload-identity annotations/labels are correct, and the matchLabels indentation fix is a nice catch.
  • bandit: clean. pylint: clean under the project config (only a non-blocking R0914 in the legacy function).

Once the legacy serialization is fixed, the WS1 change and version bump are split out, and the deps are pinned, this should be in good shape. Thanks again for tackling the Data Collector API deprecation.

@dwarrendxc

dwarrendxc commented Jun 23, 2026 via email

Copy link
Copy Markdown
Contributor Author

@carlosmmatos

Copy link
Copy Markdown
Contributor

Hi David — yep, that's the list:

  1. Fix legacy mode by re-serializing in post_data_legacy() (body = dumps(body) or similar) so it's back to a JSON string. That also kills the F401 'json.dumps' unused flake8 error.
  2. Pin the new deps — azure-monitor-ingestion==1.0.4 and azure-identity>=1.25.3.
  3. Drop the 3.5.1 bump from fig/__init__.py — release tooling handles versioning separately.
  4. Revert the WorkspaceONE serverlog change.

And honestly no worries on the WS1 slip — easy mistake. Since it was just a local-testing leftover and not a fix you actually need, don't bother splitting it into its own PR; just put the original handler block back. If a real TLS issue with WS1 ever crops up we can deal with it then. Bonus: that revert clears 11 of the 12 flake8 errors on its own, so between it and #1 the lint gate should go green.

Ping me for another review when those are in. Thanks!

@carlosmmatos carlosmmatos added the enhancement New feature or request label Jun 23, 2026
@dwarrendxc

dwarrendxc commented Jun 23, 2026 via email

Copy link
Copy Markdown
Contributor Author

@dwarrendxc

dwarrendxc commented Jun 24, 2026 via email

Copy link
Copy Markdown
Contributor Author

@carlosmmatos
carlosmmatos self-requested a review July 6, 2026 13:58
…gnature helper

CI installs via 'pip install -e .[devel]', which reads setup.py's
install_requires rather than requirements.txt, so the azure SDK packages
were missing during linting and pylint failed with import-errors on the
azure imports. Add azure-monitor-ingestion and azure-identity to
install_requires to match requirements.txt.

Also restore the build_signature() helper (inlined by an earlier commit)
so post_data_legacy stays under the pylint too-many-locals threshold,
which was the remaining non-zero pylint exit.
@carlosmmatos
carlosmmatos merged commit 4d5879f into CrowdStrike:main Jul 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants