Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions bash/containers/falcon-container-sensor-pull/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Please refer to the [Deprecation](DEPRECATION.md) document for more information
- [Prerequisites](#prerequisites)
- [Auto-Discovery of Falcon Cloud Region](#auto-discovery-of-falcon-cloud-region)
- [Usage](#usage)
- [Troubleshooting](#troubleshooting)

## Multi-Architecture Support :rocket:

Expand Down Expand Up @@ -135,6 +136,10 @@ Optional Flags:
--get-cid Get the CID assigned to the API Credentials
--list-tags List all tags available for the selected sensor type and platform, sorted in ascending order
--allow-legacy-curl Deprecated. Accepted and ignored; no longer needed
--debug Print redacted progress markers to stderr (or set FALCON_DEBUG=1).
Step name, HTTP status, cloud/region and curl exit only; values are
dropped unless the key is on a fixed allow-list, so secrets cannot
appear. Do not use bash -x for support; it prints credentials.

Internal Flags:
--internal-build-stage <BUILD_STAGE> (Internal only) Falcon Build Stage [release|stage] (Default: release)
Expand Down Expand Up @@ -166,6 +171,7 @@ Help Options:
| `--get-cid` | N/A | `None` | Get the CID assigned to the API Credentials. |
| `--list-tags` | `$LISTTAGS` | `False` (Optional) | List all tags available for the selected sensor |
| `--allow-legacy-curl` | `$ALLOW_LEGACY_CURL` | `False` (Optional) | Deprecated. Accepted and ignored; no longer needed |
| `--debug` | `$FALCON_DEBUG` | `unset` (Optional) | Print redacted progress markers to stderr. Step, HTTP status, cloud/region and curl exit only; values are dropped unless the key is allow-listed, so secrets cannot appear. Do not use `bash -x` for support. |
| `-h`, `--help` | N/A | `None` | Display help message |

---
Expand Down Expand Up @@ -438,3 +444,30 @@ The following example will pull the `falcon-sensor` image for the `x86_64` platf
--type falcon-sensor \
--platform x86_64
```

---

## Troubleshooting

Use the redacted debug mode. It prints, to stderr: the detected OS, architecture,
kernel and package manager; the exact sensor query filter and how many installers
matched; which installer was selected, its size and SHA-256 check; the API route,
HTTP status and curl exit code for every call; and the installed sensor version
and AID. Values are dropped unless the key is on a fixed allow-list, so
credentials cannot appear in the output you send to support.

```shell
./falcon-container-sensor-pull.sh \
--client-id <FALCON_CLIENT_ID> \
--client-secret <FALCON_CLIENT_SECRET> \
--type falcon-sensor \
--debug
```

`FALCON_DEBUG=1` does the same thing, which is useful when the script runs from a
pipe or a job where you cannot add a flag.

Do **not** use `bash -x` for support. It prints every expanded command, including
`client_secret`, access tokens, the registry password and `Authorization`
headers. This script turns tracing off at startup and warns when it does, but a
trace enabled before that point can still expose credentials.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,54 @@ unset FALCON_CLIENT_SECRET
FALCON_CLIENT_SECRET=$falcon_client_secret
unset falcon_client_secret

# Opt-in redacted debug. Never re-enable set -x around credential paths.
falcon_debug_enabled() {
case "${FALCON_DEBUG:-}" in
1 | true) return 0 ;;
*) return 1 ;;
esac
}

# Allow-list. Only known-safe keys keep their value; everything else is dropped,
# so a future debug line cannot leak a secret by accident.
falcon_debug_filter() {
printf '%s\n' "$@" | awk '
BEGIN {
split("step source error stage \
cloud old_cloud new_cloud region region_hint sensor_cloud \
http_status curl_exit exit_code path filter sort \
os os_version os_arch os_family kernel pkg_manager distro_id run_as \
count index decrement version sensor_version policy_version file_type sha \
installer bytes sha_verify billing backend apd aid cid_source \
tags_count grouping_tags_count sensor_type param registry repository tag \
client_id_set client_secret_set access_token_set member_cid_set \
provisioning_token_set maintenance_token_set proxy_set policy_name_set \
tags_set grouping_tags_set", safe, " ")
for (i in safe) { ok[safe[i]] = 1 }
}
{
eq = index($0, "=")
if (eq < 2) { next }
key = substr($0, 1, eq - 1)
printf " %s=%s", key, (key in ok) ? substr($0, eq + 1) : "[DROPPED]"
}
'
}

falcon_debug() {
falcon_debug_enabled || return 0
local falcon_debug_label
falcon_debug_label=$1
shift
printf 'FALCON_DEBUG: %s%s\n' "$falcon_debug_label" "$(falcon_debug_filter "$@")" >&2
}

# Last HTTP status from a curl --dump-header file. Status only — no header dump.
falcon_debug_http_status() {
[ -f "$1" ] || return 0
grep -i '^HTTP/' "$1" 2>/dev/null | tail -n 1 | awk '{print $2}'
}

: <<'#DESCRIPTION#'
File: falcon-container-sensor-pull.sh
Description: Bash script to copy Falcon DaemonSet Sensor, Container Sensor, or Kubernetes Admission Controller images from CrowdStrike Container Registry.
Expand Down Expand Up @@ -72,6 +120,11 @@ Optional Flags:
--get-cid Get the CID assigned to the API Credentials
--list-tags List all tags available for the selected sensor type and platform, sorted in ascending order
--allow-legacy-curl Deprecated. Accepted and ignored; no longer needed
--debug Print redacted progress markers to stderr (or set FALCON_DEBUG=1).
Sensor type, resolved registry/repository/tag, how many tags matched,
the API route, HTTP status and curl exit code. Values are dropped
unless the key is on a fixed allow-list, so secrets cannot appear.
Do not use bash -x for support; it prints credentials.

Internal Flags:
--internal-build-stage <BUILD_STAGE> (Internal only) Falcon Build Stage [release|stage] (Default: release)
Expand Down Expand Up @@ -202,6 +255,9 @@ while [ $# != 0 ]; do
ALLOW_LEGACY_CURL=true
fi
;;
--debug)
FALCON_DEBUG=1
;;
-n | --node)
if [ -n "${1}" ]; then
deprecated "-n|--node"
Expand Down Expand Up @@ -243,6 +299,10 @@ while [ $# != 0 ]; do
shift
done

falcon_debug start "version=$VERSION" "cloud=${FALCON_CLOUD:-unset}" "sensor_type=${SENSOR_TYPE:-unset}" \
"client_id_set=$([ -n "${FALCON_CLIENT_ID}" ] && echo yes || echo no)" \
"client_secret_set=$([ -n "${FALCON_CLIENT_SECRET}" ] && echo yes || echo no)"

if ! command -v curl >/dev/null 2>&1; then
die "The 'curl' command is missing. Please install it before continuing. Aborting..."
fi
Expand All @@ -255,6 +315,8 @@ fi
handle_curl_error() {
local err_msg

falcon_debug handle_curl_error "curl_exit=$1"

if [ "$1" = "28" ]; then
err_msg="Operation timed out (exit code 28). If using a proxy, please check your proxy settings."
die "$err_msg"
Expand Down Expand Up @@ -284,14 +346,31 @@ handle_curl_error() {

curl_command() {
# Dash does not support arrays, so we have to pass the args as separate arguments
local token="$1" escaped_token auth_config headers body status hint old_host new_host arg rc
local token="$1" escaped_token auth_config headers body status hint old_host new_host arg rc req_path
shift
# The configuration value must be quoted, because it holds a space and a
# colon. curl processes backslash escapes inside a quoted value, so a
# backslash or a double quote in the token has to be escaped first.
escaped_token=$(printf '%s' "$token" | sed 's/\\/\\\\/g; s/"/\\"/g')
auth_config=$(printf 'header = "Authorization: Bearer %s"' "$escaped_token")

# API route only, for the debug marker. The query string is dropped: it can
# carry an installer id, and the route alone identifies the call.
req_path=""
for arg in "$@"; do
case "$arg" in
https://*)
req_path=${arg#https://}
case "$req_path" in
*/*) req_path=/${req_path#*/} ;;
*) req_path=/ ;;
esac
req_path=${req_path%%\?*}
break
;;
esac
done

headers=$(mktemp)
body=$(mktemp)
# No -L: the bearer token must never cross a redirect hop. The body is held
Expand All @@ -304,6 +383,7 @@ curl_command() {
# Re-issue against that region instead of following Location. The registry is
# a different host, so its URLs are never rewritten.
status=$(awk '/^HTTP\//{s=$2} END{print s}' "$headers")
falcon_debug curl_command "path=$req_path" "http_status=$status" "curl_exit=$rc"
case "$status" in
301 | 302 | 307 | 308)
hint=$(grep -i ^x-cs-region: "$headers" | head -n 1 | tr '[:upper:]' '[:lower:]' | tr -d '\r' | sed 's/^x-cs-region: //g')
Expand All @@ -325,6 +405,7 @@ curl_command() {
printf '%s\n' "$auth_config" |
curl -s --proto '=https' -K- "$@" >"$body"
rc=$?
falcon_debug curl_command "step=region_retry" "path=$req_path" "region=$hint" "curl_exit=$rc"
fi
fi
;;
Expand All @@ -336,6 +417,7 @@ curl_command() {
}

fetch_tags() {
falcon_debug fetch_tags "step=registry_token"
bearer_result=$(echo "-u $ART_USERNAME:$ART_PASSWORD" |
curl -s --proto '=https' \
"https://$cs_registry/v2/token?account=$ART_USERNAME&scope=repository:$registry_opts/$repository_name:pull&service=$cs_registry" -K-) || handle_curl_error $?
Expand Down Expand Up @@ -430,13 +512,26 @@ is_multi_arch() {
fi
}

# Runs a container tool command with set -e off just long enough to capture its
# real exit code for the debug marker, then returns that code unchanged.
run_container_cmd() {
local step="$1" rc
shift
set +e
"$@"
rc=$?
set -e
falcon_debug "$step" "exit_code=$rc"
return "$rc"
}

pull_image() {
local image_path="$1"
local platform_override="$2"
if [ -n "$platform_override" ]; then
"$CONTAINER_TOOL" pull --platform "$platform_override" "$image_path"
run_container_cmd pull_image "$CONTAINER_TOOL" pull --platform "$platform_override" "$image_path"
else
"$CONTAINER_TOOL" pull "$image_path"
run_container_cmd pull_image "$CONTAINER_TOOL" pull "$image_path"
fi
}

Expand All @@ -447,18 +542,18 @@ copy_image() {
if [ "$multi_arch_copy" = "true" ]; then
case "${CONTAINER_TOOL}" in
*skopeo)
"$CONTAINER_TOOL" copy --all "docker://$source_path" "docker://$destination_path"
run_container_cmd copy_image "$CONTAINER_TOOL" copy --all "docker://$source_path" "docker://$destination_path"
;;
*podman)
"$CONTAINER_TOOL" manifest create --all "$destination_path" "$source_path" >/dev/null
"$CONTAINER_TOOL" manifest push --all "$destination_path"
"$CONTAINER_TOOL" manifest rm "$destination_path" >/dev/null
run_container_cmd copy_image "$CONTAINER_TOOL" manifest create --all "$destination_path" "$source_path" >/dev/null &&
run_container_cmd copy_image "$CONTAINER_TOOL" manifest push --all "$destination_path" &&
"$CONTAINER_TOOL" manifest rm "$destination_path" >/dev/null
;;
*docker)
if ! "$CONTAINER_TOOL" buildx version >/dev/null 2>&1; then
die "Docker buildx is not installed/enabled. Please install/enable buildx before continuing."
else
"$CONTAINER_TOOL" buildx imagetools create --tag "$destination_path" "$source_path"
run_container_cmd copy_image "$CONTAINER_TOOL" buildx imagetools create --tag "$destination_path" "$source_path"
fi
;;
*)
Expand All @@ -467,8 +562,8 @@ copy_image() {
esac
else
# Copy the image to the desired registry
"$CONTAINER_TOOL" tag "$source_path" "$destination_path"
"$CONTAINER_TOOL" push "$destination_path"
run_container_cmd copy_image "$CONTAINER_TOOL" tag "$source_path" "$destination_path" &&
run_container_cmd copy_image "$CONTAINER_TOOL" push "$destination_path"
fi
}

Expand Down Expand Up @@ -594,14 +689,17 @@ match_sensor_version() {
local all_tags
local matched_tags
local version_pattern
local chosen

# Get all available tags by properly parsing JSON output from list_tags
all_tags=$(extract_raw_tags)

if [ -z "$requested_version" ]; then
# If no version specified, get the latest version
if [ -n "$all_tags" ]; then
echo "$all_tags" | sort -V | tail -1
chosen=$(echo "$all_tags" | sort -V | tail -1)
falcon_debug match_sensor_version "count=$(echo "$all_tags" | grep -c .)" "tag=$chosen"
echo "$chosen"
return 0
else
return 1
Expand All @@ -613,7 +711,9 @@ match_sensor_version() {
matched_tags=$(echo "$all_tags" | grep -E "$version_pattern")

if [ -n "$matched_tags" ]; then
echo "$matched_tags" | sort -V | tail -1
chosen=$(echo "$matched_tags" | sort -V | tail -1)
falcon_debug match_sensor_version "count=$(echo "$matched_tags" | grep -c .)" "tag=$chosen"
echo "$chosen"
return 0
fi

Expand All @@ -635,7 +735,9 @@ match_sensor_version() {
fi

if [ -n "$matched_tags" ]; then
echo "$matched_tags" | sort -V | tail -1
chosen=$(echo "$matched_tags" | sort -V | tail -1)
falcon_debug match_sensor_version "count=$(echo "$matched_tags" | grep -c .)" "tag=$chosen"
echo "$chosen"
return 0
fi

Expand Down Expand Up @@ -735,7 +837,9 @@ cs_falcon_oauth_token=$(

auth_payload="client_id=$FALCON_CLIENT_ID&client_secret=$FALCON_CLIENT_SECRET"

falcon_debug oauth2_token "step=request" "cloud=${FALCON_CLOUD:-unset}"
token_result=$(echo "$auth_payload" | oauth_token_request "$(cs_cloud)" "$response_headers") || handle_curl_error $?
falcon_debug oauth2_token "step=response" "http_status=$(falcon_debug_http_status "$response_headers")" "cloud=${FALCON_CLOUD:-unset}"
token=$(echo "$token_result" | json_value "access_token" | sed 's/ *$//g' | sed 's/^ *//g')
if [ -z "$token" ]; then
# Wrong region: retry against the x-cs-region hint instead of following
Expand All @@ -749,7 +853,9 @@ cs_falcon_oauth_token=$(
# Separate file: --dump-header truncates, and region_hint below
# still needs the original response.
retry_headers=$(mktemp)
falcon_debug oauth2_token "step=retry" "region=$hinted"
token_result=$(echo "$auth_payload" | oauth_token_request "$retry_host" "$retry_headers") || handle_curl_error $?
falcon_debug oauth2_token "step=retry_response" "http_status=$(falcon_debug_http_status "$retry_headers")" "region=$hinted"
rm -f "$retry_headers"
token=$(echo "$token_result" | json_value "access_token" | sed 's/ *$//g' | sed 's/^ *//g')
fi
Expand All @@ -762,6 +868,7 @@ cs_falcon_oauth_token=$(
)

region_hint=$(grep -i ^x-cs-region: "$response_headers" | head -n 1 | tr '[:upper:]' '[:lower:]' | tr -d '\r' | sed 's/^x-cs-region: //g')
falcon_debug oauth2_token "region_hint=${region_hint:-none}" "cloud=${FALCON_CLOUD:-unset}"
rm "${response_headers}"

if [ "${FALCON_CLOUD}" != "${region_hint}" ] && [ -n "${region_hint}" ]; then
Expand Down Expand Up @@ -1030,6 +1137,8 @@ fi
#Construct full image path
FULLIMAGEPATH="${REPOSITORY}:${LATESTSENSOR}"

falcon_debug main "registry=$cs_registry" "repository=$REPOSITORY" "tag=$LATESTSENSOR"

if [ "$GETIMAGEPATH" ]; then
echo "${FULLIMAGEPATH}"
exit 0
Expand Down Expand Up @@ -1065,7 +1174,7 @@ if [ "$(is_multi_arch "$FULLIMAGEPATH")" = "true" ]; then
if [ -n "$SENSOR_PLATFORM" ]; then
# If Skopeo is being used, the platform must be overridden
if grep -qw "skopeo" "$CONTAINER_TOOL"; then
"$CONTAINER_TOOL" copy --override-arch "$(platform_override)" --override-os linux "docker://$FULLIMAGEPATH" "docker://$COPYPATH"
run_container_cmd skopeo_copy "$CONTAINER_TOOL" copy --override-arch "$(platform_override)" --override-os linux "docker://$FULLIMAGEPATH" "docker://$COPYPATH"
else
# Podman/Docker can pull the specific platform
pf_override="linux/$(platform_override)"
Expand Down Expand Up @@ -1094,7 +1203,7 @@ You can either:
else
# Handle non-multi-arch images
if grep -qw "skopeo" "$CONTAINER_TOOL"; then
"$CONTAINER_TOOL" copy "docker://$FULLIMAGEPATH" "docker://$COPYPATH"
run_container_cmd skopeo_copy "$CONTAINER_TOOL" copy "docker://$FULLIMAGEPATH" "docker://$COPYPATH"
else
pull_image "$FULLIMAGEPATH"

Expand Down
Loading
Loading