镜像安全
学习目标
- 理解容器镜像安全的重要性
- 掌握镜像扫描和验证方法
- 了解镜像安全的最佳实践
核心概念
一句话解释
镜像安全通过扫描、签名和策略控制,确保容器镜像不包含漏洞和恶意代码。
镜像安全威胁
┌─────────────────────────────────────────────────────────────┐
│ 镜像安全威胁 │
├─────────────────────────────────────────────────────────────┤
│ 漏洞风险 │
│ ├── 操作系统漏洞 │
│ ├── 应用依赖漏洞 │
│ └── 配置错误 │
├─────────────────────────────────────────────────────────────┤
│ 恶意代码 │
│ ├── 后门程序 │
│ ├── 挖矿程序 │
│ └── 数据窃取 │
├─────────────────────────────────────────────────────────────┤
│ 供应链攻击 │
│ ├── 基础镜像被篡改 │
│ ├── 依赖包被替换 │
│ └── 构建过程被污染 │
└─────────────────────────────────────────────────────────────┘
镜像安全层次
┌─────────────────────────────────────────────────────────────┐
│ 镜像安全层次 │
├─────────────────────────────────────────────────────────────┤
│ 构建时安全 │
│ ├── 使用可信基础镜像 │
│ ├── 最小化镜像 │
│ ├── 多阶段构建 │
│ └── 扫描漏洞 │
├─────────────────────────────────────────────────────────────┤
│ 存储时安全 │
│ ├── 私有镜像仓库 │
│ ├── 镜像签名 │
│ ├── 访问控制 │
│ └── 定期扫描 │
├─────────────────────────────────────────────────────────────┤
│ 运行时安全 │
│ ├── 镜像验证 │
│ ├── 准入控制 │
│ ├── 只读根文件系统 │
│ └── 非 root 运行 │
└─────────────────────────────────────────────────────────────┘
镜像扫描工具
| 工具 | 说明 | 特点 |
|---|---|---|
| Trivy | 开源扫描工具 | 快速、全面 |
| Clair | 开源扫描工具 | API 驱动 |
| Anchore | 企业级扫描 | 策略引擎 |
| Snyk | 开发者友好 | IDE 集成 |
| Docker Scout | Docker 官方 | 集成 Docker |
Trivy 扫描示例
# 安装 Trivy
brew install trivy # macOS
# 或
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# 扫描镜像
trivy image nginx:latest
# 扫描本地镜像
trivy image my-app:latest
# 扫描指定严重级别
trivy image --severity HIGH,CRITICAL nginx:latest
# 输出 JSON 格式
trivy image --format json --output result.json nginx:latest
# 扫描 Dockerfile
trivy config Dockerfile
镜像签名
# 使用 cosign 签名镜像
# 安装 cosign
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# 生成密钥对
cosign generate-key-pair
# 签名镜像
cosign sign --key cosign.key my-registry/my-image:tag
# 验证签名
cosign verify --key cosign.pub my-registry/my-image:tag
镜像验证策略
# 使用 Kyverno 验证镜像
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image
spec:
validationFailureAction: enforce
background: false
rules:
- name: verify-image-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "my-registry/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
镜像仓库安全
# 私有镜像仓库认证
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJteS1yZWdpc3RyeSI6eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJwQHNzdzByZCJ9fX0=
---
# Pod 使用私有镜像
apiVersion: v1
kind: Pod
metadata:
name: private-image-pod
spec:
containers:
- name: app
image: my-registry/my-app:latest
imagePullSecrets:
- name: registry-credentials
最小化镜像
# 多阶段构建
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o my-app
FROM scratch
COPY --from=builder /app/my-app /my-app
ENTRYPOINT ["/my-app"]
# 使用最小基础镜像
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
COPY my-app /my-app
ENTRYPOINT ["/my-app"]
# 使用 distroless 镜像
FROM gcr.io/distroless/static
COPY my-app /my-app
ENTRYPOINT ["/my-app"]
镜像安全最佳实践
1. 使用可信基础镜像
- 官方镜像
- 经过验证的镜像
- 定期更新基础镜像
2. 最小化镜像
- 多阶段构建
- 只包含必要组件
- 减少攻击面
3. 定期扫描漏洞
- CI/CD 集成扫描
- 定期重新扫描
- 及时修复漏洞
4. 镜像签名和验证
- 使用 cosign 签名
- 准入控制验证签名
- 防止镜像篡改
5. 私有镜像仓库
- 使用私有仓库
- 访问控制
- 定期清理旧镜像
CI/CD 集成
# GitLab CI 镜像扫描
image_scanning:
stage: test
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
only:
- main
---
# GitHub Actions 镜像扫描
name: Image Scan
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-image:latest'
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
实践练习
练习 1:扫描镜像漏洞
# 扫描 nginx 镜像
trivy image nginx:latest
# 扫描指定严重级别
trivy image --severity HIGH,CRITICAL nginx:latest
# 扫描本地构建的镜像
docker build -t my-app:latest .
trivy image my-app:latest
练习 2:创建安全的 Dockerfile
# 使用多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]
常见误解
1. 官方镜像一定安全
事实:官方镜像也可能包含漏洞
误解:使用官方镜像就安全了
解决:定期扫描所有镜像,包括官方镜像
2. 镜像扫描是一次性工作
事实:漏洞不断被发现,需要持续扫描
误解:构建时扫描一次就够了
解决:定期重新扫描,及时更新镜像
小结
| 要点 | 说明 |
|---|---|
| 镜像安全 | 扫描、签名、验证 |
| 扫描工具 | Trivy、Clair、Anchore |
| 镜像签名 | cosign、Notary |
| 最佳实践 | 可信基础镜像、最小化、定期扫描 |
| CI/CD 集成 | 构建时扫描、准入控制 |
练习编辑器
bash
Loading...