Security Multi-cloud

Deploy CrowdStrike Falcon Sensor to Linux Fleets and Kubernetes via Helm DaemonSet

Your SOC lead walks over on a Monday with a number from the last audit: of roughly 1,800 Linux hosts across three clouds and four Kubernetes clusters, telemetry coverage sits at 61%. The EDR agent was deployed by hand two years ago, drifted as fleets churned, and the gaps are exactly where an attacker would want them — short-lived autoscaled nodes, a forgotten staging cluster, the bastion someone rebuilt last quarter. The mandate is blunt: get CrowdStrike Falcon runtime protection onto every Linux host and every Kubernetes node, make coverage self-healing as nodes come and go, and prove it with a dashboard the auditor can see. This guide is the runbook to do exactly that — sensor on VMs via Ansible, a Helm-managed DaemonSet on the clusters, sensor update policies so upgrades are controlled rather than chaotic, and the identity, secrets, and pipeline wiring that make it an operated system instead of a one-off script.

CrowdStrike Falcon is an EDR (endpoint detection and response) platform: a lightweight sensor runs on each host, streams process, file, and network telemetry to the Falcon cloud, and the cloud applies behavioral detection and threat intelligence to surface and block attacks. On Linux it runs either as a kernel module or in user-mode/eBPF backend — the latter is the safe default on modern kernels because it survives kernel upgrades without a matching module. The DaemonSet pattern is the natural fit for Kubernetes: exactly one sensor pod per node, automatically scheduled onto every node that joins, so coverage tracks the fleet instead of lagging it.

Prerequisites

Target topology

Deploy CrowdStrike Falcon Sensor to Linux Fleets and Kubernetes via Helm DaemonSet — topology

There are two delivery planes that share one control plane in the Falcon cloud. The VM plane covers standalone Linux hosts — bastions, build agents, databases, virtual appliances — where the sensor is installed as a system package and configured with your CID; Ansible is the rollout engine, idempotently installing and registering the sensor across inventory. The Kubernetes plane covers cluster nodes, where the sensor runs as a privileged DaemonSet managed by the official falcon-sensor Helm chart, so one sensor pod lands on every node and new nodes are covered the moment kubelet registers them.

Both planes report into the same Falcon tenant, where sensor update policies govern which version each host group runs and how upgrades roll. Around that core sit the operating-model integrations: Entra ID (or Okta) federates engineer SSO into the Falcon console so access is governed by your IdP and conditional access, not local console passwords; HashiCorp Vault holds the Falcon API client secret and the pull token, leased to CI rather than baked into manifests; Wiz (with Wiz Code scanning IaC in pull requests) provides agentless CSPM that cross-checks Falcon’s runtime coverage and flags any host or cluster missing a sensor; Dynatrace or Datadog ingests sensor health and node metrics so a silent sensor pages someone; ServiceNow receives a change request for each fleet-wide rollout and an incident when a detection fires; and GitHub Actions with Argo CD drives the GitOps deploy of the Helm release, while Jenkins runs the Ansible plays against the VM fleet. Akamai is unrelated to the sensor itself but is named here for one thing: its edge logs are a correlation source the SOC joins against Falcon detections during an investigation.

1. Mint API credentials and store the secret in Vault

Never paste the Falcon client_secret or CID into a manifest or a play. Create the API client in the console, then put the secret in HashiCorp Vault so CI and Ansible lease it at runtime.

# Write Falcon API + install credentials into Vault (KV v2)
vault kv put secret/security/crowdstrike/falcon \
  client_id="$FALCON_CLIENT_ID" \
  client_secret="$FALCON_CLIENT_SECRET" \
  cid="$FALCON_CID" \
  cloud="us-2"

# Verify (shows keys, not values, in CI logs)
vault kv get -format=json secret/security/crowdstrike/falcon | jq '.data.data | keys'

Grant the CI service and the Ansible runner a narrowly-scoped Vault policy that allows only read on this path. This is the single source of truth for the credential; rotating it in Vault rotates it everywhere downstream.

2. Pull the sensor and validate your API access

Use the Falcon API to confirm the credentials work and to fetch the installer for the VM plane. The falconctl and image-pull steps both depend on this working first.

# Acquire an OAuth2 token
export FALCON_CLOUD="us-2"
TOKEN=$(curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=${FALCON_CLIENT_ID}&client_secret=${FALCON_CLIENT_SECRET}" \
  | jq -r '.access_token')

# List the newest available Linux sensor builds for your CID
curl -s -G "https://api.${FALCON_CLOUD}.crowdstrike.com/sensors/combined/installers/v2" \
  -H "Authorization: Bearer ${TOKEN}" \
  --data-urlencode 'filter=os:"*RHEL*"+platform:"linux"' \
  --data-urlencode 'sort=version|desc' | jq -r '.resources[0:3][] | "\(.name)  \(.version)  sha256=\(.sha256)"'

A clean JSON list confirms scopes and connectivity. If you get a 403, the API client is missing Sensor Download (Read); if you get a 401, the token call failed — recheck client_id/client_secret.

3. Roll the sensor to the Linux VM fleet with Ansible

For standalone hosts, install the sensor as a package and register it with your CID. CrowdStrike publishes a maintained Ansible collection (crowdstrike.falcon) that handles download, install, and registration idempotently. Drive it from Jenkins against your inventory.

# falcon-linux.yml — run with: ansible-playbook -i inventory/linux falcon-linux.yml
- name: Install and register Falcon sensor on Linux fleet
  hosts: linux_servers
  become: true
  vars:
    # Pulled at runtime from Vault by the Jenkins job, exported as env/extra-vars
    falcon_client_id: "{{ lookup('env', 'FALCON_CLIENT_ID') }}"
    falcon_client_secret: "{{ lookup('env', 'FALCON_CLIENT_SECRET') }}"
    falcon_cid: "{{ lookup('env', 'FALCON_CID') }}"
    falcon_cloud: "us-2"
  roles:
    - role: crowdstrike.falcon.falcon_install   # downloads + installs the right build
    - role: crowdstrike.falcon.falcon_configure  # writes CID, sets backend, starts svc
  post_tasks:
    - name: Force the user-mode/eBPF backend (kernel-upgrade safe)
      ansible.builtin.command: /opt/CrowdStrike/falconctl -s --backend=bpf
      notify: restart falcon
    - name: Tag hosts so the console can group them
      ansible.builtin.command: >
        /opt/CrowdStrike/falconctl -s --tags="env/prod,fleet/linux-vm,team/platform"
      notify: restart falcon
  handlers:
    - name: restart falcon
      ansible.builtin.service: { name: falcon-sensor, state: restarted }

The --tags values are sensor grouping tags: they let you build dynamic host groups in the console (step 6) so policy follows the tag, not a hand-maintained list. Run the play in a canary batch first using Ansible’s --limit and serial:

# Canary: 20 hosts at a time, abort the batch if more than 1 fails
ansible-playbook -i inventory/linux falcon-linux.yml \
  --limit 'linux_servers:&canary' --extra-vars "ansible_serial=20" --forks 20

4. Install the Falcon sensor DaemonSet via Helm

For each Kubernetes cluster, deploy the node sensor with the official chart. Two prerequisites first: a pull token so nodes can pull the sensor image, and the chart repo.

# Add the CrowdStrike Helm repo
helm repo add crowdstrike https://crowdstrike.github.io/falcon-helm
helm repo update

# Create the namespace and the image pull secret (token from Vault)
kubectl create namespace falcon-system
kubectl create secret docker-registry crowdstrike-falcon-pull \
  --namespace falcon-system \
  --docker-server="registry.crowdstrike.com" \
  --docker-username="<falcon-pull-token-user>" \
  --docker-password="${FALCON_PULL_TOKEN}"

Define the release values explicitly rather than relying on defaults — pin the image, point at your CID, and choose the eBPF backend.

# falcon-daemonset-values.yaml
node:
  enabled: true
  daemonset:
    # Tolerate every taint so control-plane and dedicated nodes are covered too
    tolerations:
      - operator: Exists
    updateStrategy: RollingUpdate
  image:
    repository: registry.crowdstrike.com/falcon-sensor/us-2/release/falcon-sensor
    tag: "7.20.0-17306-1.falcon-linux.Release.US-2"   # pin; do not float :latest
    pullSecret: crowdstrike-falcon-pull
  backend: bpf            # user-mode/eBPF; survives node kernel upgrades
falcon:
  cid: "<YOUR-CID-WITH-CHECKSUM>"
  tags: "env/prod,fleet/k8s,cluster/prod-eu-1"
container:
  enabled: false          # this release is the node sensor, not the container sensor

Install (or upgrade) the release:

helm upgrade --install falcon-sensor crowdstrike/falcon-sensor \
  --namespace falcon-system \
  --values falcon-daemonset-values.yaml \
  --atomic --timeout 5m

--atomic rolls the release back automatically if the DaemonSet does not become healthy in five minutes, so a bad values file never leaves you half-deployed. Repeat per cluster, changing only falcon.tags and the region in the image repository. In production, do not run helm by hand for every cluster — commit falcon-daemonset-values.yaml to the cluster’s GitOps repo and let Argo CD reconcile it (step 8).

5. Configure sensor update policies and host groups

This is the step teams skip and regret. Sensor update policies decide which sensor version each group of hosts runs and how it upgrades — without them, every host tracks the latest build and a bad sensor release can ripple across the fleet at once. Define groups by tag, then bind a staged policy to each.

# Create a dynamic host group keyed off the grouping tag set in steps 3 & 4
GROUP_ID=$(curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/devices/entities/host-groups/v1" \
  -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
  -d '{"resources":[{
        "name":"linux-prod",
        "group_type":"dynamic",
        "assignment_rule":"tags:'\''SensorGroupingTags/fleet/linux-vm'\''"
      }]}' | jq -r '.resources[0].id')

# Create a sensor update policy pinned to "n-1" with auto-uninstall protection
curl -s -X POST "https://api.${FALCON_CLOUD}.crowdstrike.com/policy/entities/sensor-update/v2" \
  -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
  -d '{"resources":[{
        "name":"linux-prod-n-minus-1",
        "platform_name":"Linux",
        "settings":{"build":"n-1|tagged","uninstall_protection":"ENABLED"}
      }]}' | jq -r '.resources[0].id'

Run three rings: an early-access policy on a canary group that takes the latest build, a prod policy pinned to n-1 (one release behind latest — battle-tested), and a critical policy pinned to a specific frozen build for sensitive hosts like virtual appliances. Promote a build from canary to prod only after it has soaked for a week with no node regressions. Enable uninstall protection so a compromised host cannot silently remove its own sensor.

6. Wire SSO, change management, and observability

Bring the operating-model tools into the loop so this is governed and monitored.

7. Validate coverage end to end

Prove the sensor is present, healthy, and actually detecting — do not trust “the Helm release succeeded.”

# Every node has exactly one sensor pod, all Ready
kubectl -n falcon-system get daemonset falcon-sensor-falcon-sensor \
  -o custom-columns='DESIRED:.status.desiredNumberScheduled,READY:.status.numberReady'

# No node was missed (compare to node count)
kubectl get nodes --no-headers | wc -l

# On a VM: the sensor is loaded and shows the right CID + backend
sudo /opt/CrowdStrike/falconctl -g --cid --rfm-state --backend
sudo systemctl is-active falcon-sensor

Then run the official, harmless detection test so you confirm telemetry reaches the cloud and a detection appears in the console:

# CrowdStrike's safe EICAR-style behavioral test (does NO harm; triggers a detection)
cp /bin/true /tmp/falcon-test && \
  /tmp/falcon-test 2>/dev/null; \
  echo 'aWQ6Y3Jvd2RzdHJpa2U7' | base64 -d   # marker the test guide has you run

Within a minute or two a detection should appear in the Falcon console for that hostname, tagged with the grouping tags you set. Confirm the host shows up in the correct host group and is bound to the expected sensor update policy. Coverage is real only when all four are true: pod Ready, service active, host in group, detection seen.

8. Make it self-healing with GitOps

The whole point was coverage that tracks the fleet. The DaemonSet gives you that inside a cluster; GitHub Actions plus Argo CD gives you that across clusters.

# Argo CD Application — reconciles the Helm release from Git, self-heals drift
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: falcon-sensor-prod-eu-1
  namespace: argocd
spec:
  project: security-baseline
  source:
    repoURL: https://github.com/kloudvin/k8s-security-baseline.git
    targetRevision: main
    path: clusters/prod-eu-1/falcon
    helm:
      valueFiles: [falcon-daemonset-values.yaml]
  destination:
    server: https://kubernetes.default.svc
    namespace: falcon-system
  syncPolicy:
    automated: { prune: true, selfHeal: true }   # re-applies if someone deletes it

A GitHub Actions workflow lints the values, runs Wiz Code against the manifests, and on merge to main Argo CD syncs the change with selfHeal: true — so if anyone deletes the DaemonSet, it comes straight back. New clusters get coverage by adding one directory and one Application. For the VM plane, Jenkins runs the Ansible play on a schedule against fresh inventory so newly-built hosts are enrolled within a day, not whenever someone remembers.

Rollback and teardown

Have an exit before you need one. To roll back a bad sensor build, repoint the sensor update policy to the previous build (or n-1) in the console or via API — the fleet downgrades on its next check-in, no redeploy required. To roll back the Helm release itself:

# Revert to the previous good revision
helm history falcon-sensor -n falcon-system
helm rollback falcon-sensor <PREVIOUS_REVISION> -n falcon-system

# Full removal from a cluster (note: uninstall protection on VMs must be lifted first)
helm uninstall falcon-sensor -n falcon-system
kubectl delete namespace falcon-system

For a VM, uninstall via the same Ansible collection (falcon_uninstall role) or the package manager after disabling uninstall protection with the maintenance token from the console — falconctl will refuse to uninstall otherwise, which is by design. If you tear a cluster down entirely, also delete its Argo CD Application so it does not try to re-sync into a dead API server.

Common pitfalls

Security notes

The sensor runs privileged by necessity — it needs host-level visibility into processes, files, and network — so treat the supply chain accordingly: pin the image by tag (ideally by digest), restrict who can edit the Helm values via repo CODEOWNERS, and let Wiz Code block a PR that would weaken the sensor. Keep the Falcon API client secret and pull token in HashiCorp Vault, leased to CI, never in a committed manifest or a Kubernetes Secret authored by hand. Govern console access through Entra ID/Okta SSO with MFA so there is no standalone credential to phish, and enable uninstall protection so a foothold on a host cannot blind your EDR by removing it. Detections route to ServiceNow for an auditable incident trail, and the SOC correlates them against Akamai edge logs during investigations.

Cost notes

Falcon is licensed per endpoint/host, so cost scales with the number of hosts reporting, not with traffic — which makes decommissioning discipline a real lever: a sensor still reporting from a host you deleted is a license you are paying for, so let the dynamic host groups and Wiz reconciliation retire stale hosts promptly. The DaemonSet’s own compute footprint is small (a fraction of a core and modest memory per node), but on very large clusters set explicit resource requests/limits in the Helm values so the sensor pods are scheduled predictably and do not contend with workloads. There is no per-GB telemetry charge for the core EDR sensor, so the dominant cost knob is simply accurate host count — keep it clean and the bill follows coverage rather than drift.

CrowdStrikeFalconKubernetesEDRDaemonSetLinux
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading