Skip to content

Instantly share code, notes, and snippets.

@yannhowe
Created November 19, 2025 16:24
Show Gist options
  • Select an option

  • Save yannhowe/dc976c3531b5c1304482159ef20a3ef0 to your computer and use it in GitHub Desktop.

Select an option

Save yannhowe/dc976c3531b5c1304482159ef20a3ef0 to your computer and use it in GitHub Desktop.
CrowdStrike 容器镜像和软件包扫描器 (中文版)
#!/bin/bash
# CrowdStrike 容器镜像和软件包扫描器 (JSON 输出)
#
# 此脚本从 CrowdStrike Falcon Cloud Security APIs 获取容器镜像详情和软件包 (SBOM)
# 并以结构化 JSON 格式输出。
#
# 作者: CrowdStrike
# 版本: 1.0
# 许可证: MIT
set -e
# 配置
FALCON_CLOUD=${FALCON_CLOUD:-"us-1"}
BASE_URL="https://api.crowdstrike.com"
# 显示详细帮助的函数
show_help() {
cat << 'EOF'
================================================================================
CrowdStrike 容器镜像和软件包扫描器 (JSON 输出)
================================================================================
描述:
从 CrowdStrike Falcon Cloud Security APIs 获取容器镜像详情和软件包 (SBOM)
并以结构化 JSON 格式输出。
前提条件:
1. 具有 'falcon-container-image:read' 权限的 CrowdStrike API 凭据
2. jq 命令行 JSON 处理器
3. curl 用于 API 请求
设置:
export FALCON_CLIENT_ID="your-client-id"
export FALCON_CLIENT_SECRET="your-client-secret"
export FALCON_CLOUD="us-1" # 可选: us-1, us-2, eu-1, us-gov-1
用法:
./get-image-packages-json-zh.sh [选项]
选项:
--repository REPO 按仓库名称过滤
--registry REG 按注册表过滤
--tag TAG 按标签过滤
--digest DIGEST 按镜像摘要过滤
--limit NUM 限制镜像数量 (默认: 3, 最大: 100)
--package-limit NUM 限制每个镜像的软件包数量 (默认: 1000)
--format FORMAT 输出格式 (默认: combined)
-h, --help 显示此帮助
输出格式:
combined - 镜像及其软件包 (默认)
images-only - 仅镜像元数据
packages-only - 仅第一个找到的镜像的软件包
示例:
# 获取 2 个镜像及其软件包 (组合格式)
./get-image-packages-json-zh.sh --limit 2
# 获取 nginx 镜像及其软件包
./get-image-packages-json-zh.sh --repository nginx --limit 1
# 仅获取 Docker Hub 的镜像元数据
./get-image-packages-json-zh.sh --registry docker.io --format images-only
# 获取特定镜像的软件包
./get-image-packages-json-zh.sh --digest sha256:abc123... --format packages-only
# 按多个条件过滤
./get-image-packages-json-zh.sh --registry docker.io --tag latest --limit 5
输出结构 (组合格式):
{
"images": [
{
"registry": "docker.io",
"repository": "nginx",
"tag": "latest",
"digest": "sha256:...",
"base_os": "Debian GNU",
"config": {"architecture": "amd64"},
"packages": [
{
"package_name_version": "nginx-1.21.6-1",
"type": "apt",
"license": "BSD-2-Clause",
"vulnerability_count": 0,
"vulnerabilities": []
}
]
}
],
"total_images": 1,
"generated_at": "2025-11-19T15:54:48Z"
}
过滤:
可用的过滤字段:
- registry: 容器注册表 (docker.io, gcr.io 等)
- repository: 仓库名称 (nginx, alpine 等)
- tag: 镜像标签 (latest, stable, v1.0 等)
- digest: SHA256 摘要
- 多个过滤器使用 AND 逻辑组合
使用的 API 端点:
- POST /oauth2/token (身份验证)
- GET /container-security/combined/images/detail/v1 (镜像详情)
- GET /container-security/combined/packages/v1 (软件包清单)
故障排除:
1. "获取访问令牌失败":
- 验证 FALCON_CLIENT_ID 和 FALCON_CLIENT_SECRET
- 检查 API 客户端是否具有 'falcon-container-image:read' 权限
2. "jq: 命令未找到":
- 安装 jq: apt-get install jq (Ubuntu) 或 brew install jq (macOS)
3. 空结果:
- 检查过滤器是否过于严格
- 验证您的环境中是否存在容器镜像
4. 速率限制:
- 减少 --limit 和 --package-limit 值
- 如果多次运行,在请求之间添加延迟
支持:
CrowdStrike 开发者门户: https://falcon.crowdstrike.com/documentation
容器安全 APIs: https://falcon.crowdstrike.com/documentation/page/container-security-apis
================================================================================
EOF
}
# 显示用法摘要的函数
show_usage() {
cat << 'EOF'
CrowdStrike 容器镜像和软件包扫描器 (JSON 输出)
用法: ./get-image-packages-json-zh.sh [选项]
常用示例:
./get-image-packages-json-zh.sh --limit 2
./get-image-packages-json-zh.sh --repository nginx --limit 1
./get-image-packages-json-zh.sh --registry docker.io --format images-only
需要设置:
export FALCON_CLIENT_ID="your-client-id"
export FALCON_CLIENT_SECRET="your-client-secret"
使用 --help 查看详细文档。
EOF
}
# 检查是否未提供参数
if [[ $# -eq 0 ]]; then
show_usage
exit 0
fi
# 检查必需的环境变量
if [[ -z "$FALCON_CLIENT_ID" || -z "$FALCON_CLIENT_SECRET" ]]; then
echo "❌ 错误: 缺少 CrowdStrike API 凭据" >&2
echo "" >&2
echo "必需的环境变量:" >&2
echo " export FALCON_CLIENT_ID=\"your-client-id\"" >&2
echo " export FALCON_CLIENT_SECRET=\"your-client-secret\"" >&2
echo "" >&2
echo "可选:" >&2
echo " export FALCON_CLOUD=\"us-1\" # us-1, us-2, eu-1, us-gov-1" >&2
echo "" >&2
echo "从此处获取 API 凭据: https://falcon.crowdstrike.com/api-clients-and-keys" >&2
echo "必需权限: falcon-container-image:read" >&2
echo "" >&2
echo "使用 --help 查看完整文档。" >&2
exit 1
fi
# 检查 jq 依赖
if ! command -v jq &> /dev/null; then
echo "❌ 错误: 需要 jq 但未安装" >&2
echo "" >&2
echo "安装 jq:" >&2
echo " Ubuntu/Debian: sudo apt-get install jq" >&2
echo " macOS: brew install jq" >&2
echo " RHEL/CentOS: sudo yum install jq" >&2
exit 1
fi
# 获取 OAuth2 令牌的函数
get_token() {
local response
response=$(curl -s -X POST "$BASE_URL/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=$FALCON_CLIENT_ID&client_secret=$FALCON_CLIENT_SECRET")
local token
token=$(echo "$response" | jq -r '.access_token')
if [[ "$token" == "null" ]]; then
echo "获取访问令牌失败:" >&2
echo "$response" | jq '.' >&2
exit 1
fi
echo "$token"
}
# 获取镜像详情的函数
get_image_details() {
local filter="$1"
local limit="${2:-3}"
local params="limit=$limit&with_config=true"
if [[ -n "$filter" ]]; then
params="$params&filter=$filter"
fi
curl -s -X GET "$BASE_URL/container-security/combined/images/detail/v1?$params" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json"
}
# 获取特定镜像软件包的函数
get_image_packages() {
local image_digest="$1"
local limit="${2:-1000}"
local filter="image_digest:'$image_digest'"
local params="limit=$limit&filter=$filter"
curl -s -X GET "$BASE_URL/container-security/combined/packages/v1?$params" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json"
}
# 解析命令行参数
REPOSITORY=""
REGISTRY=""
TAG=""
IMAGE_DIGEST=""
LIMIT=3
PACKAGE_LIMIT=1000
OUTPUT_FORMAT="combined" # combined, images-only, packages-only
while [[ $# -gt 0 ]]; do
case $1 in
--repository)
REPOSITORY="$2"
shift 2
;;
--registry)
REGISTRY="$2"
shift 2
;;
--tag)
TAG="$2"
shift 2
;;
--digest)
IMAGE_DIGEST="$2"
shift 2
;;
--limit)
LIMIT="$2"
shift 2
;;
--package-limit)
PACKAGE_LIMIT="$2"
shift 2
;;
--format)
OUTPUT_FORMAT="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知选项: $1" >&2
exit 1
;;
esac
done
# 获取访问令牌 (将输出抑制到 stderr)
ACCESS_TOKEN=$(get_token)
# 为镜像构建过滤字符串
FILTERS=()
if [[ -n "$REPOSITORY" ]]; then
FILTERS+=("repository:'$REPOSITORY'")
fi
if [[ -n "$REGISTRY" ]]; then
FILTERS+=("registry:'$REGISTRY'")
fi
if [[ -n "$TAG" ]]; then
FILTERS+=("tag:'$TAG'")
fi
if [[ -n "$IMAGE_DIGEST" ]]; then
FILTERS+=("image_digest:'$IMAGE_DIGEST'")
fi
FILTER_STRING=""
if [[ ${#FILTERS[@]} -gt 0 ]]; then
FILTER_STRING=$(IFS='+'; echo "${FILTERS[*]}")
fi
# 获取镜像详情
IMAGE_RESPONSE=$(get_image_details "$FILTER_STRING" "$LIMIT")
# 检查错误
if echo "$IMAGE_RESPONSE" | jq -e '.errors | length > 0' > /dev/null 2>&1; then
echo "$IMAGE_RESPONSE" | jq '{error: "API 错误", details: .errors}'
exit 1
fi
# 处理不同的输出格式
case $OUTPUT_FORMAT in
"images-only")
echo "$IMAGE_RESPONSE"
exit 0
;;
"packages-only")
# 获取第一个镜像摘要并仅返回软件包
FIRST_DIGEST=$(echo "$IMAGE_RESPONSE" | jq -r '.resources[0].digest // empty')
if [[ -n "$FIRST_DIGEST" ]]; then
get_image_packages "$FIRST_DIGEST" "$PACKAGE_LIMIT"
else
echo '{"error": "未找到镜像或摘要不可用", "resources": []}'
fi
exit 0
;;
"combined")
# 默认: 将镜像与其软件包组合
;;
*)
echo '{"error": "无效格式。使用: combined, images-only, 或 packages-only"}' >&2
exit 1
;;
esac
# 处理镜像并与软件包组合
COMBINED_RESULT='{"images": []}'
# 处理每个镜像
IMAGES=$(echo "$IMAGE_RESPONSE" | jq -r '.resources[] | @base64')
if [[ -z "$IMAGES" ]]; then
echo '{"images": [], "message": "未找到符合条件的镜像"}'
exit 0
fi
for image_data in $IMAGES; do
# 解码镜像数据
IMAGE_JSON=$(echo "$image_data" | base64 --decode)
# 获取摘要
DIGEST=$(echo "$IMAGE_JSON" | jq -r '.digest // ""')
if [[ -n "$DIGEST" && "$DIGEST" != "null" ]]; then
# 获取此镜像的软件包
PACKAGE_RESPONSE=$(get_image_packages "$DIGEST" "$PACKAGE_LIMIT")
# 检查软件包错误
if echo "$PACKAGE_RESPONSE" | jq -e '.errors | length > 0' > /dev/null 2>&1; then
PACKAGES='{"error": "获取软件包失败", "details": []}'
else
PACKAGES=$(echo "$PACKAGE_RESPONSE" | jq '.resources')
fi
else
PACKAGES='[]'
fi
# 将镜像与软件包组合
COMBINED_IMAGE=$(echo "$IMAGE_JSON" | jq --argjson packages "$PACKAGES" '. + {packages: $packages}')
# 添加到结果
COMBINED_RESULT=$(echo "$COMBINED_RESULT" | jq --argjson image "$COMBINED_IMAGE" '.images += [$image]')
done
# 添加元数据
TOTAL_IMAGES=$(echo "$IMAGE_RESPONSE" | jq '.resources | length')
COMBINED_RESULT=$(echo "$COMBINED_RESULT" | jq --argjson total "$TOTAL_IMAGES" '. + {total_images: $total, generated_at: now | strftime("%Y-%m-%dT%H:%M:%SZ")}')
# 输出最终 JSON
echo "$COMBINED_RESULT" | jq '.'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment