Shell脚本如何验证容器镜像来源

wen 实用脚本 1

本文目录导读:

Shell脚本如何验证容器镜像来源

  1. 核心概念:摘要 (Digest) — 最可靠的指纹
  2. 使用 Notary / Docker Content Trust (DCT)
  3. 使用 cosign (Sigstore) — 云原生标准
  4. 从私有仓库 API 获取元数据验证
  5. 针对特定数据源的验证(例如:从安全公告库)

在Shell脚本中验证容器镜像来源,核心思路是利用镜像的签名、Digest)和可信仓库的元数据进行校验,以下是几种主流且实用的方法:

核心概念:(Digest) — 最可靠的指纹

镜像的Digest(格式为 sha256:...)是镜像内容的加密哈希值,只要镜像内容不变,Digest就唯一不变。验证来源的本质是验证 Digest。

  • 获取官方/可信 Digest:从官方仓库(如 Docker Hub, Harbor, ECR)的描述页或API获取。
  • 本地计算并比对:拉取镜像后,计算其Digest并与可信值对比。

脚本示例:验证 Digest 是否匹配

#!/bin/bash
set -euo pipefail
# 1. 定义可信的镜像名称和其预期的 Digest
TRUSTED_IMAGE="docker.io/library/nginx:1.25"
EXPECTED_DIGEST="sha256:abc123...你的可信摘要"
# 2. 拉取镜像(如果本地没有)
docker pull "${TRUSTED_IMAGE}" > /dev/null 2>&1
# 3. 获取本地镜像的真实 Digest
LOCAL_DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${TRUSTED_IMAGE}" | cut -d'@' -f2)
# 4. 比对
if [ "${LOCAL_DIGEST}" == "${EXPECTED_DIGEST}" ]; then
    echo "✓ 镜像来源验证通过: Digest匹配"
else
    echo "✗ 镜像来源验证失败! 期望: ${EXPECTED_DIGEST}, 实际: ${LOCAL_DIGEST}"
    exit 1
fi

使用 Notary / Docker Content Trust (DCT)

Docker 官方的签名机制,启用后,docker pull 会自动验证签名,但脚本中需要显式处理。

脚本示例:使用 DCT 强制验证

#!/bin/bash
set -euo pipefail
# 环境变量:启用内容信任
export DOCKER_CONTENT_TRUST=1
# 指定信任的根角色(可选,默认使用 Docker Hub 的公钥)
# export DOCKER_CONTENT_TRUST_SERVER=https://notary.docker.io
IMAGE_NAME="your-registry.com/your-image:tag"
echo "正在使用 DCT 拉取并验证: ${IMAGE_NAME}"
if docker pull "${IMAGE_NAME}"; then
    echo "✓ DCT 签名验证通过"
else
    echo "✗ DCT 签名验证失败或镜像未签名"
    exit 1
fi

注意:DCT 要求镜像必须在推送时启用签名,否则拉取会失败。

使用 cosign (Sigstore) — 云原生标准

Sigstore 是目前更现代的签名方案,广泛用于 Kubernetes 生态。

前提:安装 cosign CLI。

脚本示例:验证 cosign 签名

#!/bin/bash
set -euo pipefail
IMAGE="ghcr.io/your-org/your-image:tag"
PUBLIC_KEY_PATH="/path/to/cosign.pub"  # 官方公钥
# 方式1:验证公钥签名
if cosign verify --key "${PUBLIC_KEY_PATH}" "${IMAGE}"; then
    echo "✓ cosign 签名验证通过 (使用公钥)"
else
    echo "✗ cosign 验证失败"
    exit 1
fi
# 方式2:验证 Keyless 签名(使用OIDC身份)
if cosign verify "${IMAGE}" --certificate-identity-regexp ".*@yourdomain.com" --certificate-oidc-issuer https://accounts.google.com; then
    echo "✓ cosign keyless 验证通过"
fi

从私有仓库 API 获取元数据验证

对于 Harbor, Amazon ECR, Azure ACR 等,可以调用其API获取镜像的digestcreated时间。

脚本示例:从 Harbor API 获取 Digest

#!/bin/bash
set -euo pipefail
REGISTRY="harbor.yourcompany.com"
PROJECT="myproject"
REPO="nginx"
TAG="1.25"
HARBOR_USER="admin"
HARBOR_PASS="your_password"
# 获取 API token 或 使用 Basic Auth
TOKEN=$(echo -n "${HARBOR_USER}:${HARBOR_PASS}" | base64)
# 查询镜像信息
RESPONSE=$(curl -s -H "Authorization: Basic ${TOKEN}" \
  "https://${REGISTRY}/api/v2.0/projects/${PROJECT}/repositories/${REPO}/artifacts/${TAG}")
# 提取 digest
SERVER_DIGEST=$(echo "${RESPONSE}" | jq -r '.digest')
# 比对本地 digest
LOCAL_DIGEST=$(docker inspect --format='{{.Id}}' "${REGISTRY}/${PROJECT}/${REPO}:${TAG}" | cut -d: -f2-)
if [ "${LOCAL_DIGEST}" == "${SERVER_DIGEST}" ]; then
    echo "✓ 镜像来源验证通过(与 Harbor 元数据一致)"
else
    echo "✗ 镜像来源验证失败"
    exit 1
fi

针对特定数据源的验证(从安全公告库)

如果您的镜像来自一个已知的安全基镜像(如 alpine:3.19),可以查询其官方摘要。

脚本示例:查询 Docker Hub 官方镜像的摘要

#!/bin/bash
set -euo pipefail
IMAGE="library/alpine"
TAG="3.19"
DOCKER_HUB_TOKEN_URL="https://auth.docker.io/token?service=registry.docker.io&scope=repository:${IMAGE}:pull"
# 获取 token
TOKEN=$(curl -s "${DOCKER_HUB_TOKEN_URL}" | jq -r '.token')
# 获取 manifest 信息
MANIFEST=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
  "https://registry-1.docker.io/v2/${IMAGE}/manifests/${TAG}" \
  -H "Accept: application/vnd.docker.distribution.manifest.v2+json")
# 提取 digest
EXPECTED_DIGEST=$(echo "${MANIFEST}" | jq -r '.config.digest')
echo "Docker Hub 官方摘要: ${EXPECTED_DIGEST}"
# 后续与本地比对...
方法 适用场景 安全性 脚本复杂度
Digest 比对 任何来源,最基础可靠 高(取决于可信摘要的存储方式)
Docker Content Trust Docker Hub / 私有 Notary 高(密码学签名)
Cosign (Sigstore) 云原生、CI/CD 流水线 非常高(灵活、无中心化)
私有仓库 API 企业内部 Harbor/ECR 等 高(需凭证管理)
Docker Hub API 查询 验证官方基础镜像 中(依赖 HTTPS)

推荐策略:在CI/CD脚本中,应组合使用摘要比对 + 数字签名验证,先通过 cosign verify 验证签名,再检查其有效负载中的 digest 是否与预期一致,这样既保证了镜像的完整性,也验证了其发布者的身份。

安全提示:永远不要在脚本中硬编码密钥或密码,应使用密钥管理服务(如 HashiCorp Vault, AWS Secrets Manager)或 CI/CD 环境中注入的变量。

抱歉,评论功能暂时关闭!