Container Image Security(容器镜像安全)
支持的版本: Trivy 0.56+, Kubernetes 1.31, 1.32, 1.33 最后更新: February 22, 2026
容器镜像安全是 Kubernetes 安全的第一道防线。本文档介绍镜像扫描、签名、验证以及供应链安全。
目录
- 镜像扫描概述
- Trivy
- Amazon ECR 镜像扫描
- 使用 Cosign/Sigstore 进行镜像签名
- Admission Control 中的镜像验证
- 供应链安全
- 基础镜像选择
- Image Registry 最佳实践
- CI/CD Pipeline 集成
镜像扫描概述
Shift-Left Security
镜像安全应从开发流程的早期开始。
┌─────────────────────────────────────────────────────────────────────────┐
│ Shift-Left Image Security │
│ │
│ Development Phase Build Phase Runtime Phase │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ IDE │ │ CI/CD │ │ K8s │ │
│ │ Scanning│──────────────▶│ Scanning│──────────────▶│ Scanning│ │
│ │ │ │ │ │ │ │
│ │ • Local │ │ • Build │ │ • Runtime│ │
│ │ scan │ │ gate │ │ monitor│ │
│ │ • Deps │ │ • Regis-│ │ • Policy │ │
│ │ check │ │ try │ │ enforce│ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ ◀─────────────────── Cost & Ease of Fix ──────────────────────▶ │
│ Low High │
└─────────────────────────────────────────────────────────────────────────┘扫描目标
| 目标 | 描述 | 工具 |
|---|---|---|
| OS Packages | 操作系统包漏洞 | Trivy, Grype, Clair |
| Language Dependencies | npm、pip、go modules 等 | Trivy, Snyk |
| Misconfigurations | Dockerfile、K8s manifests | Trivy, Checkov |
| Secrets | 硬编码的敏感信息 | Trivy, Trufflehog |
| Licenses | 开源许可证 | Trivy, FOSSA |
Trivy
Trivy 概述
Trivy 是一个面向 containers、filesystems、Git repositories 等的综合安全扫描器。
Trivy 安装
bash
# macOS
brew install trivy
# Linux (Debian/Ubuntu)
sudo apt-get install wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy
# Docker
docker pull aquasec/trivy:latest镜像扫描
bash
# Basic image scan
trivy image nginx:latest
# Severity filtering
trivy image --severity HIGH,CRITICAL nginx:latest
# JSON output
trivy image --format json --output results.json nginx:latest
# SARIF format (GitHub Security integration)
trivy image --format sarif --output results.sarif nginx:latest
# Ignore unfixed vulnerabilities
trivy image --ignore-unfixed nginx:latest
# Include secret scanning
trivy image --scanners vuln,secret nginx:latestFilesystem 扫描
bash
# Scan project directory
trivy fs --scanners vuln,secret,config .
# Scan Dockerfile
trivy config Dockerfile
# Scan Kubernetes manifests
trivy config ./k8s/
# Scan Helm charts
trivy config ./charts/my-app/Trivy 配置文件
yaml
# trivy.yaml
severity:
- HIGH
- CRITICAL
scan:
scanners:
- vuln
- secret
- config
vulnerability:
ignore-unfixed: true
type:
- os
- library
secret:
config: /path/to/secret-config.yaml
misconfiguration:
helm:
values:
- values.yaml
ignore:
- CVE-2023-12345 # Ignore specific CVE
- secret-rule-id # Ignore specific secret ruleTrivy Operator(Kubernetes 集成)
bash
# Install Trivy Operator
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system \
--create-namespace \
--set trivy.ignoreUnfixed=trueyaml
# Check VulnerabilityReport
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
name: deployment-nginx-nginx
namespace: default
spec:
# Auto-generated by Trivy Operator
report:
scanner:
name: Trivy
version: 0.56.0
summary:
criticalCount: 2
highCount: 5
mediumCount: 10
lowCount: 15
vulnerabilities:
- vulnerabilityID: CVE-2024-12345
severity: CRITICAL
title: "Buffer overflow in libssl"
installedVersion: "1.1.1"
fixedVersion: "1.1.2"Amazon ECR 镜像扫描
Basic Scanning 与 Enhanced Scanning
| 功能 | Basic Scanning | Enhanced Scanning (Inspector) |
|---|---|---|
| Scan Engine | Clair | Amazon Inspector |
| Scan Frequency | Push 时 | 持续扫描 |
| Vulnerability DB | CVE | CVE + Amazon 威胁情报 |
| Cost | 免费 | Inspector 定价 |
| Language Packages | 有限 | 广泛支持 |
| AWS Integration | 基础 | Security Hub, EventBridge |
启用 Enhanced Scanning
bash
# Configure registry scanning
aws ecr put-registry-scanning-configuration \
--scan-type ENHANCED \
--rules '[
{
"repositoryFilters": [
{"filter": "production/*", "filterType": "WILDCARD"}
],
"scanFrequency": "CONTINUOUS_SCAN"
},
{
"repositoryFilters": [
{"filter": "development/*", "filterType": "WILDCARD"}
],
"scanFrequency": "SCAN_ON_PUSH"
}
]'获取扫描结果
bash
# Get scan results
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=latest \
--query 'imageScanFindings.findings[?severity==`CRITICAL`]'
# Vulnerability summary
aws ecr describe-image-scan-findings \
--repository-name my-app \
--image-id imageTag=latest \
--query 'imageScanFindings.findingSeverityCounts'通过 EventBridge 发送通知
yaml
# CloudFormation/SAM template
Resources:
ECRScanRule:
Type: AWS::Events::Rule
Properties:
Name: ecr-scan-findings
EventPattern:
source:
- aws.ecr
detail-type:
- ECR Image Scan
detail:
scan-status:
- COMPLETE
finding-severity-counts:
CRITICAL:
- exists: true
Targets:
- Id: SNSTarget
Arn: !Ref AlertTopic
InputTransformer:
InputPathsMap:
repo: "$.detail.repository-name"
tag: "$.detail.image-tags[0]"
critical: "$.detail.finding-severity-counts.CRITICAL"
InputTemplate: |
"ECR Scan Alert: Found <critical> CRITICAL vulnerabilities in <repo>:<tag>"使用 Cosign/Sigstore 进行镜像签名
Cosign 概述
Cosign 是 Sigstore 项目的一部分,是用于签名和验证 container images 的工具。
┌─────────────────────────────────────────────────────────────────────────┐
│ Cosign Image Signing Workflow │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Build │────▶│ Sign │────▶│ Push to Registry │ │
│ │ Image │ │ (Cosign) │ │ (Image + Signature) │ │
│ └─────────────┘ └─────────────┘ └───────────┬─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Container Registry │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Image │ │ Signature │ │ │
│ │ │ sha256:abc... │ │ sha256:xyz... │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Kubernetes Cluster │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Admission │────▶│ Verify │────▶│ Deploy │ │ │
│ │ │ Controller │ │ Signature │ │ Pod │ │ │
│ │ │ (Kyverno) │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘Cosign 安装
bash
# macOS
brew install cosign
# Linux
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# Version check
cosign version基于密钥的签名
bash
# Generate key pair
cosign generate-key-pair
# Sign image
cosign sign --key cosign.key myregistry.io/myapp:v1.0.0
# Verify signature
cosign verify --key cosign.pub myregistry.io/myapp:v1.0.0Keyless Signing(基于 OIDC)
bash
# Keyless signing in GitHub Actions
# GITHUB_TOKEN automatically provides OIDC token
cosign sign myregistry.io/myapp:v1.0.0
# Verify keyless signature
cosign verify \
--certificate-identity=https://github.com/myorg/myrepo/.github/workflows/build.yaml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
myregistry.io/myapp:v1.0.0GitHub Actions 集成
yaml
name: Build, Sign and Push
on:
push:
branches: [main]
jobs:
build-sign-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Required for keyless signing
steps:
- uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v5
id: build
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign Image (Keyless)
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: |
cosign sign --yes ghcr.io/${{ github.repository }}@${DIGEST}
- name: Verify Signature
run: |
cosign verify \
--certificate-identity-regexp="https://github.com/${{ github.repository }}/*" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/${{ github.repository }}:${{ github.sha }}Admission Control 中的镜像验证
Kyverno imageVerify
yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- count: 1
entries:
- keyless:
subject: "https://github.com/myorg/*/.github/workflows/*@refs/heads/main"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
---
# Key-based verification
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-key
spec:
validationFailureAction: Enforce
rules:
- name: verify-key-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry.io/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----Connaisseur
yaml
# Connaisseur installation
helm repo add connaisseur https://sse-secure-systems.github.io/connaisseur/charts
helm install connaisseur connaisseur/connaisseur \
-n connaisseur \
--create-namespaceyaml
# Connaisseur configuration
# values.yaml
validators:
- name: cosign
type: cosign
trustRoots:
- name: production
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
policy:
- pattern: "ghcr.io/myorg/*"
validator: cosign
with:
trustRoot: production
- pattern: "*"
validator: deny # Deny other images供应链安全
SBOM (Software Bill of Materials) 生成
bash
# Generate SBOM with Syft
syft myregistry.io/myapp:latest -o spdx-json > sbom.spdx.json
syft myregistry.io/myapp:latest -o cyclonedx-json > sbom.cyclonedx.json
# Generate SBOM with Trivy
trivy image --format spdx-json --output sbom.json myregistry.io/myapp:latest
# Attach SBOM to image (Cosign)
cosign attach sbom --sbom sbom.spdx.json myregistry.io/myapp:latest基于 SBOM 的漏洞扫描
bash
# Scan SBOM for vulnerabilities
trivy sbom sbom.spdx.json
# Scan SBOM with Grype
grype sbom:sbom.spdx.jsonSLSA (Supply chain Levels for Software Artifacts)
yaml
# Generate SLSA Provenance in GitHub Actions
name: SLSA Build
on:
push:
branches: [main]
jobs:
build:
outputs:
digest: ${{ steps.build.outputs.digest }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and Push
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
provenance:
needs: build
permissions:
actions: read
id-token: write
packages: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
with:
image: ghcr.io/${{ github.repository }}
digest: ${{ needs.build.outputs.digest }}
secrets:
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}基础镜像选择
镜像类型对比
| 镜像类型 | 大小 | 漏洞 | 调试 | 使用场景 |
|---|---|---|---|---|
| distroless | 非常小 | 极少 | 困难 | Production |
| Alpine | 小 (~5MB) | 少 | 可行 | 轻量级 apps |
| Chainguard | 小 | 极少 | 有限 | 安全优先 |
| Ubuntu/Debian | 大 | 多 | 容易 | Dev/Legacy |
| Scratch | 最小 | 无 | 不可能 | 静态 binaries |
使用 Distroless Images
dockerfile
# Multi-stage build with distroless
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server
# Distroless runtime image
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]使用 Chainguard Images
dockerfile
# Chainguard Python image
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER nonroot
CMD ["python", "app.py"]Alpine 安全加固
dockerfile
FROM alpine:3.19
# Install minimal packages only
RUN apk add --no-cache \
python3 \
py3-pip \
&& rm -rf /var/cache/apk/*
# Create non-root user
RUN addgroup -g 1000 app && \
adduser -u 1000 -G app -s /bin/sh -D app
WORKDIR /app
COPY --chown=app:app . .
USER app
CMD ["python3", "app.py"]Image Registry 最佳实践
使用私有 Registries
yaml
# ImagePullSecrets configuration
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
namespace: production
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
# Set default ImagePullSecret on ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: production
imagePullSecrets:
- name: registry-credentialsImage Pull Policies
yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
containers:
- name: app
image: myregistry.io/myapp@sha256:abc123... # Use digest
imagePullPolicy: Always # Always verify latestImmutable Tag Policy (Kyverno)
yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-digest
spec:
validationFailureAction: Enforce
rules:
- name: require-digest
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Images must use digest instead of tags"
pattern:
spec:
containers:
- image: "*@sha256:*"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: disallow-latest
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Using 'latest' tag is not allowed"
pattern:
spec:
containers:
- image: "!*:latest"CI/CD Pipeline 集成
完整的镜像安全 Pipeline
yaml
# .github/workflows/secure-build.yaml
name: Secure Image Build
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# 1. Code scanning
code-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner (fs mode)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'
# 2. Build and scan
build-scan:
needs: code-scan
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build image for scanning
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: ${{ env.IMAGE_NAME }}:scan
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.IMAGE_NAME }}:scan'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
- name: Check for critical vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.IMAGE_NAME }}:scan'
exit-code: '1'
severity: 'CRITICAL'
# 3. Push and sign
push-sign:
needs: build-scan
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
outputs:
digest: ${{ steps.push.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Sign image with Cosign
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.push.outputs.digest }}
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.push.outputs.digest }}
format: spdx-json
output-file: sbom.spdx.json
- name: Attach SBOM to image
run: |
cosign attach sbom --sbom sbom.spdx.json ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.push.outputs.digest }}
# 4. Generate SLSA Provenance
provenance:
needs: push-sign
permissions:
actions: read
id-token: write
packages: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
with:
image: ghcr.io/${{ github.repository }}
digest: ${{ needs.push-sign.outputs.digest }}
secrets:
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}总结
容器镜像安全的关键方面:
- 扫描: 使用 Trivy、ECR enhanced scanning 检测漏洞
- 签名: 使用 Cosign/Sigstore 确保镜像完整性
- 验证: 使用 Kyverno 只允许已签名镜像
- 供应链: 使用 SBOM、SLSA 实现透明度
- 基础镜像: 使用 distroless、Chainguard
- CI/CD 集成: 构建自动化安全 pipelines
建议
- 对所有镜像应用漏洞扫描
- 始终签名生产镜像
- 使用 digests 而不是
latesttag - 使用 distroless 或最小化基础镜像
- 自动化 SBOM 生成和管理