Files
docker_practice/18_security/18.6_image_security.md

557 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 18.6 容器镜像安全扫描与供应链安全
DevOps 流程中容器镜像安全已经成为不容忽视的关键环节从开发构建存储到部署镜像的整个生命周期都需要安全防护本节深入讨论镜像漏洞扫描软件物料清单SBOM镜像签名验证等供应链安全实践
### 18.6.1 容器镜像漏洞扫描工具对比
#### Trivy - 轻量级通用扫描器
Trivy 是由 Aqua Security 开发的开源漏洞扫描器以其轻量级快速准确而闻名已成为业界标准
**优点**
- 零依赖单个二进制文件
- 扫描速度快秒级
- 支持镜像文件系统Git 仓库多种扫描源
- 数据库每日自动更新
- 支持多种输出格式JSON表格SBOM
**安装与基本使用**
```bash
# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# 扫描本地镜像
trivy image nginx:latest
# 生成 JSON 格式报告
trivy image -f json -o report.json nginx:latest
# 扫描文件系统
trivy fs /path/to/project
# 扫描 Git 仓库
trivy repo https://github.com/aquasecurity/trivy
```
** CI/CD 中集成**
```bash
# 设置严重程度过滤
trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
myregistry.com/myapp:v1.0.0
```
#### Grype - 支持多种软件包的扫描器
Grype Anchore 开发支持更广泛的软件包管理器和语言
**优点**
- 支持 JavaPythonGoRubyJavaScript 等多种语言的依赖检测
- SyftSBOM 生成器配合效果好
- 可自定义漏洞数据库源
- 支持离线扫描模式
**安装与使用**
```bash
# 安装 Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# 扫描镜像
grype docker:nginx:latest
# 与 Syft 配合生成 SBOM
syft docker:nginx:latest -o json > sbom.json
grype sbom:sbom.json
# 扫描特定目录
grype dir:/path/to/app
```
#### Snyk - 完整的安全平台
Snyk 提供了商业级的安全扫描服务特别适合企业环境
**特点**
- 支持开源漏洞和许可证扫描
- 与多个 Git 平台深度集成GitHubGitLabBitbucket
- 提供修复建议和自动化修复 PR
- 支持 Kubernetes 部署后安全监控
**基本使用**
```bash
# 安装 Snyk CLI
npm install -g snyk
# 认证
snyk auth
# 扫描镜像
snyk container test docker-archive://image.tar
# 监控仓库
snyk monitor --docker
```
**工具对比表**
| 特性 | Trivy | Grype | Snyk |
|------|-------|-------|------|
| 零依赖 | | | |
| 离线模式 | | | |
| 许可证扫描 | | | |
| 自动修复 | | | |
| 开源免费 | | | 部分 |
| IDE 集成 | | | |
### 18.6.2 SBOM软件物料清单生成与管理
SBOMSoftware Bill of Materials是一份详细列表记录了软件中使用的所有组件依赖库及其版本信息SBOM 在供应链安全中至关重要特别是在发现新的安全漏洞时能快速定位受影响的应用
#### Syft - SBOM 生成工具
Syft Anchore 推出的专业 SBOM 生成工具
**安装**
```bash
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
```
**生成 SBOM**
```bash
# 从镜像生成 SBOM多种格式
syft docker:nginx:latest -o json > sbom.json
syft docker:nginx:latest -o spdx > sbom.spdx
syft docker:nginx:latest -o cyclonedx > sbom.xml
# 从本地文件系统生成
syft dir:/path/to/app -o json > sbom.json
# 从 OCI 镜像档案生成
syft oci-archive:image.tar -o json > sbom.json
```
#### CycloneDX SPDX 格式
两种主流的 SBOM 格式
**CycloneDX 格式示例**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" version="1">
<components>
<component type="library">
<name>openssl</name>
<version>1.1.1k</version>
<purl>pkg:deb/debian/openssl@1.1.1k-1+deb11u5</purl>
</component>
<component type="library">
<name>curl</name>
<version>7.74.0-1.3+deb11u1</version>
<purl>pkg:deb/debian/curl@7.74.0-1.3+deb11u1</purl>
</component>
</components>
</bom>
```
**SPDX 格式示例**
```json
{
"SPDXID": "SPDXRef-DOCUMENT",
"spdxVersion": "SPDX-2.2",
"creationInfo": {
"created": "2024-03-01T12:00:00Z",
"creators": ["Tool: syft"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package-openssl",
"name": "openssl",
"versionInfo": "1.1.1k",
"downloadLocation": "NOASSERTION"
}
]
}
```
#### SBOM 的应用场景
**漏洞关联**
当新的 CVE 被发现时可快速查询受影响的应用
```bash
# 使用 Grype 针对 SBOM 进行漏洞扫描
grype sbom:sbom.json --add-cpes-if-none
```
**合规性报告**
SBOM 保存为构建产物用于审计和合规性检查
**依赖升级决策**
通过分析 SBOM 中的依赖版本制定安全升级计划
### 18.6.3 镜像签名与验证Cosign/Notary
镜像签名确保镜像的来源可信且未被篡改两种主流方案是 Cosign Notary
#### Cosign - 现代签名解决方案
Cosign Sigstore 项目的核心工具支持无密钥签名适合现代 CI/CD 流程
**安装**
```bash
wget 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
```
**生成密钥对传统方式**
```bash
cosign generate-key-pair
# 生成 cosign.key 和 cosign.pub
```
**签名镜像**
```bash
# 使用私钥签名(推送到仓库前)
cosign sign --key cosign.key myregistry.com/myapp:v1.0.0
# 系统会提示输入私钥密码
```
**验证签名**
```bash
# 使用公钥验证
cosign verify --key cosign.pub myregistry.com/myapp:v1.0.0
# 输出结果示例
# Verification successful!
# {
# "critical": {
# "identity": {...},
# "image": {...},
# "type": "cosign container image signature"
# },
# "optional": {...}
# }
```
**Keyless 签名推荐用于 CI/CD**
```bash
# 在 GitHub Actions 等 CI 中无需存储密钥
cosign sign --yes myregistry.com/myapp:v1.0.0
# 验证时自动使用 OIDC 令牌验证身份
cosign verify myregistry.com/myapp:v1.0.0 \
--certificate-identity https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
```
#### Docker Content TrustDCT Notary
Docker Content Trust 使用 Notary 实现镜像签名 Docker 官方的签名解决方案
**启用 DCT**
```bash
# 在环境中启用 DCT
export DOCKER_CONTENT_TRUST=1
# 此后所有 docker push/pull 都需要签名
docker push myregistry.com/myapp:v1.0.0
# 如果镜像未签名,操作会被拒绝
# 禁用 DCT仅用于特定操作
docker push --disable-content-trust myregistry.com/myapp:v1.0.0
```
**签名密钥管理**
```bash
# 首次推送时会提示创建 Delegation Key
# 密钥存储在 ~/.docker/trust/private/root_keys/ 和 ~/.docker/trust/private/tuf_keys/
# 查看签名信息
docker inspect --format='{{.RepoDigests}}' myregistry.com/myapp:v1.0.0
```
### 18.6.4 供应链安全最佳实践
#### 1. 基础镜像安全
```dockerfile
# ❌ 不推荐:使用 latest 标签
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl
# ✓ 推荐:固定基础镜像版本和摘要
FROM ubuntu:22.04@sha256:a6d2b38300ce017add71440577d5b0a90460d0e6e0e14...(完整 64 位哈希)
RUN apt-get update && apt-get install -y curl=7.68.0-1ubuntu1
```
#### 2. 构建时扫描
Dockerfile 中集成安全扫描
```dockerfile
FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY . .
# 使用 Trivy 扫描源代码
RUN apk add --no-cache curl && \
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin && \
trivy fs . --exit-code 1 --severity HIGH,CRITICAL
RUN go build -o app .
FROM alpine:3.17@sha256:abcd1234...(请替换为实际完整的 64 位摘要哈希)
COPY --from=builder /app/app /app
```
#### 3. 运行时镜像扫描策略
```bash
# 镜像构建完成后立即扫描
trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
--timeout 30m \
$IMAGE_NAME:$IMAGE_TAG
# 定期扫描已部署的镜像
trivy image --scanners vuln,misconfig registry:5000/myapp:latest
```
#### 4. 镜像仓库安全配置
**Harbor私有镜像仓库的安全扫描**
```yaml
# harbor.yml 配置示例
trivy:
enabled: true
# 启用镜像扫描
image_source: "Official"
# 默认扫描配置
scan_on_push: true # 推送时自动扫描
scan_all: true # 扫描仓库中的所有镜像
```
#### 5. 政策执行Admission Controller
Kubernetes 环境中使用 Admission Webhook 强制镜像签名和扫描
```yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: image-security-policy
webhooks:
- name: image-security.example.com
clientConfig:
service:
name: image-security-webhook
namespace: security
path: "/validate"
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
```
### 18.6.5 CI/CD 中集成安全扫描
#### GitHub Actions 工作流示例
```yaml
name: Build and Scan Image
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-scan:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: cyclonedx-json
output-file: sbom-cyclonedx.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom-cyclonedx.json
- name: Sign image with Cosign
if: github.event_name == 'push'
run: |
cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Login to Registry and Push
if: github.event_name == 'push'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
```
#### GitLab CI 工作流示例
```yaml
stages:
- build
- scan
- sign
- push
variables:
REGISTRY: registry.gitlab.com
IMAGE_NAME: $REGISTRY/$CI_PROJECT_PATH
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker save $IMAGE_NAME:$CI_COMMIT_SHA > image.tar
scan:trivy:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 docker-archive://image.tar
allow_failure: false
scan:grype:
stage: scan
image: anchore/grype:latest
script:
- grype docker-archive://image.tar
generate:sbom:
stage: scan
image: anchore/syft:latest
script:
- syft docker-archive://image.tar -o cyclonedx > sbom.xml
artifacts:
reports:
sbom: sbom.xml
sign:
stage: sign
image: gcr.io/projectsigstore/cosign:latest
script:
- cosign sign --key $COSIGN_KEY $IMAGE_NAME:$CI_COMMIT_SHA
only:
- main
push:
stage: push
image: docker:latest
services:
- docker:dind
script:
- docker load < image.tar
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
only:
- main
```
### 18.6.6 常见问题与最佳实践
**Q: 扫描报告中有过时的 CVE如何处理**
A: 某些 CVE 可能已经被修复但数据库未更新可以
- 手动验证安全补丁是否已应用
- 使用工具的忽略列表功能 Trivy `.trivyignore`
- 定期更新扫描工具和漏洞数据库
**Q: 如何平衡镜像大小和安全性**
A:
- 使用多阶段构建减少最终镜像大小
- 使用精简基础镜像AlpineDistroless
- 定期更新依赖而不是一味求小
- 优先安全性体积次之
**Q: 如何管理和轮换签名密钥**
A:
- 在密钥管理系统 HashiCorp Vault中存储密钥
- 定期轮换密钥建议每 90
- 使用 Keyless 签名消除密钥管理复杂性
- 保留密钥轮换的审计日志