fix(falcon): preserve CrowdStrike OTA updates across reboots - #1899
Conversation
Root cause: CrowdStrike cloud pushed an OTA update from build 18410 to 19004, but falcon-init.sh rsync'd the old 18410 binaries back on boot, clobbering the update. The running 19004 sensor then failed to exec helper binaries (falcon-fxpredict) with ENOENT, causing CS_PREVENTION_MISSING. The init script now compares installed vs packaged build numbers and skips the rsync when the installed version is newer. Also reverts the kernel pin from 6.17 back to 6.18 (#1898 was a misdiagnosis).
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
Disabled knowledge base sources:
📝 WalkthroughSummary by CodeRabbit
WalkthroughUpdated the matic NixOS host to pin the kernel to version 6.18 for CrowdStrike Falcon compatibility. Enhanced the Falcon binary initialization script with conditional rsync logic that compares build numbers between installed and packaged falconctl versions, skipping synchronization when the installed build is newer. ChangesCrowdStrike Falcon host compatibility
Estimated code review effort🎯 2 (Simple) | ⏱️ ~15 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates the pinned kernel version to 6.18 for CrowdStrike Falcon compatibility and modifies the falcon-init.sh script to skip the rsync process if the installed build is newer than the packaged build, preventing OTA updates from being clobbered. Feedback suggests replacing the external grep pipeline in falcon-init.sh with Bash's built-in regular expression matching to improve robustness and efficiency.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if [ -x "$installed_ver" ]; then | ||
| pkg_build=$(readlink -f "$pkg_ver" | grep -oP '\d+$' || echo "0") | ||
| inst_build=$(readlink -f "$installed_ver" | grep -oP '\d+$' || echo "0") | ||
| if [ "$inst_build" -gt "$pkg_build" ] 2>/dev/null; then | ||
| echo "falcon-init: installed build $inst_build is newer than packaged $pkg_build, skipping rsync" | ||
| need_sync=false | ||
| fi | ||
| fi |
There was a problem hiding this comment.
Using external grep -oP in a pipeline under set -euo pipefail can be fragile and spawns unnecessary subprocesses. Additionally, if the extracted build numbers are not valid integers, the -gt comparison will throw a bash evaluation error (which is currently suppressed with 2>/dev/null).
We can make this much more robust and efficient by using Bash's built-in regular expression matching ([[ ... =~ ... ]]). This avoids spawning grep processes, guarantees that the variables are valid integers, and eliminates the need to suppress comparison errors.
| if [ -x "$installed_ver" ]; then | |
| pkg_build=$(readlink -f "$pkg_ver" | grep -oP '\d+$' || echo "0") | |
| inst_build=$(readlink -f "$installed_ver" | grep -oP '\d+$' || echo "0") | |
| if [ "$inst_build" -gt "$pkg_build" ] 2>/dev/null; then | |
| echo "falcon-init: installed build $inst_build is newer than packaged $pkg_build, skipping rsync" | |
| need_sync=false | |
| fi | |
| fi | |
| if [ -x "$installed_ver" ]; then | |
| pkg_target=$(readlink -f "$pkg_ver" 2>/dev/null || true) | |
| inst_target=$(readlink -f "$installed_ver" 2>/dev/null || true) | |
| pkg_build=0 | |
| if [[ "$pkg_target" =~ ([0-9]+)$ ]]; then | |
| pkg_build=${BASH_REMATCH[1]} | |
| fi | |
| inst_build=0 | |
| if [[ "$inst_target" =~ ([0-9]+)$ ]]; then | |
| inst_build=${BASH_REMATCH[1]} | |
| fi | |
| if [ "$inst_build" -gt "$pkg_build" ]; then | |
| echo "falcon-init: installed build $inst_build is newer than packaged $pkg_build, skipping rsync" | |
| need_sync=false | |
| fi | |
| fi |
|
|
||
| if [ -x "$installed_ver" ]; then | ||
| pkg_build=$(readlink -f "$pkg_ver" | grep -oP '\d+$' || echo "0") | ||
| inst_build=$(readlink -f "$installed_ver" | grep -oP '\d+$' || echo "0") |
There was a problem hiding this comment.
Version detection silently no-ops on a plain-file falconctl. readlink -f /opt/CrowdStrike/falconctl only ends in build digits when CrowdStrike's installer keeps falconctl as a symlink to falconctl<build> (the documented layout per yaleman.org's CrowdStrike cleanup post, and what the .deb ships). If that ever isn't true on this host — partial OTA, manual reinstall, future packaging change — grep -oP '\d+$' misses, || echo "0" kicks in, inst_build=0, 0 -gt $pkg_build is false, and the rsync below clobbers the OTA exactly as before. There's no log of the detected values on the sync path either (only the skip branch logs), so the degradation is invisible in the journal.
Reproduced locally:
$ readlink -f /tmp/test/opt/CrowdStrike/falconctl # plain file
/tmp/test/opt/CrowdStrike/falconctl
$ readlink -f /tmp/test/opt/CrowdStrike/falconctl | grep -oP '\d+$' || echo 0
0
Consider either (a) calling falconctl -g --version (or reading the build from the sensor directly) instead of inferring from the symlink target, or at minimum (b) echo "falcon-init: installed=$inst_build packaged=$pkg_build" unconditionally so a regression is obvious in journalctl -u falcon-sensor.
| echo "falcon-init: installed build $inst_build is newer than packaged $pkg_build, skipping rsync" | ||
| need_sync=false | ||
| fi | ||
| fi |
There was a problem hiding this comment.
Consider logging the detected builds unconditionally (e.g. echo "falcon-init: installed=$inst_build packaged=$pkg_build" right after the if [ -x "$installed_ver" ] block) so the next time someone debugs a clobbered OTA they can see whether the comparison ran correctly or silently fell back to 0.
| # version is not newer than the package, otherwise the rsync clobbers the | ||
| # update and the running sensor can't find its helper binaries (ENOENT). | ||
| pkg_ver="@falcon@/opt/CrowdStrike/falconctl" | ||
| installed_ver=/opt/CrowdStrike/falconctl |
There was a problem hiding this comment.
Nit: pkg_ver / installed_ver are misleading — they're paths to falconctl, not version strings. Renaming to pkg_path / installed_path would line up with the pkg_build / inst_build variables that do hold versions.
| if [ -x "$installed_ver" ]; then | ||
| pkg_build=$(readlink -f "$pkg_ver" | grep -oP '\d+$' || echo "0") | ||
| inst_build=$(readlink -f "$installed_ver" | grep -oP '\d+$' || echo "0") | ||
| if [ "$inst_build" -gt "$pkg_build" ] 2>/dev/null; then |
There was a problem hiding this comment.
Nit: the 2>/dev/null is redundant — pkg_build and inst_build are both normalized to integers by the || echo "0" fallback, so [ -gt ] can't print a non-integer error here. Removing it makes the intent ("this is a plain integer compare") clearer.
| if [ "$inst_build" -gt "$pkg_build" ] 2>/dev/null; then | |
| if [ "$inst_build" -gt "$pkg_build" ]; then |
Summary
falcon-init.shrsync'd old binaries back on boot, causingCS_PREVENTION_MISSINGRoot cause
Audit logs showed
falcon-sensor-bpf19004failing to exec/opt/CrowdStrike/falcon-fxpredict(ENOENT) because the rsync restored 18410-versioned symlinks over the OTA-updated 19004 binaries.Test plan
sudo nixos-rebuild switchsudo systemctl restart falcon-sensorCS_PREVENTION_MISSINGresolves in Drata/Falcon consoleSummary by cubic
Prevents CrowdStrike Falcon OTA updates from being overwritten on reboot by skipping rsync when a newer build is already installed. Also reverts the kernel pin to 6.18 for sensor compatibility and resolves CS_PREVENTION_MISSING.
falcon-init.sh: compare installed vs packaged build numbers viafalconctland skip rsync when installed is newer, preserving OTA updates (e.g., 19004).Written for commit e7386a4. Summary will update on new commits.