151 lines
5.2 KiB
Markdown
151 lines
5.2 KiB
Markdown
|
|
---
|
|||
|
|
tags: [docker, image-security, trivy, distroless, registry]
|
|||
|
|
create time: 2026-05-18 00:45
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 镜像安全 — 扫描、基线 & 仓库管理
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
镜像安全不仅仅是"不要有 CVE"——它是一个完整的供应链安全体系,涵盖基础镜像选型、漏洞扫描、仓库分级管理和推送门禁。更多内容参见 [[../01-容器化/01-Dockerfile最佳实践]]。
|
|||
|
|
|
|||
|
|
## 镜像扫描流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
Scan["镜像扫描 (Trivy/Snyk)"] --> Clean{"漏洞等级?"}
|
|||
|
|
|
|||
|
|
Clean -- "CRITICAL/HIGH" --> Block["❌ 阻止推送"]
|
|||
|
|
Clean -- "MEDIUM" --> Review["⚠️ 人工审核"]
|
|||
|
|
Clean -- "LOW/INFO" --> Allow["✅ 允许推送"]
|
|||
|
|
|
|||
|
|
style Block fill:#ffebee
|
|||
|
|
style Review fill:#fff3e0
|
|||
|
|
style Allow fill:#e8f5e9
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Trivy 常用命令
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 扫描镜像
|
|||
|
|
trivy image harbor.example.com/app/server:v1.2.3
|
|||
|
|
|
|||
|
|
# 只报告 HIGH 及以上级别
|
|||
|
|
trivy image --severity HIGH,CRITICAL harbor.example.com/app/server:v1.2.3
|
|||
|
|
|
|||
|
|
# 输出 JSON 供 CI 集成
|
|||
|
|
trivy image --format json --output report.json harbor.example.com/app/server:v1.2.3
|
|||
|
|
|
|||
|
|
# 扫描 Dockerfile 中的潜在问题
|
|||
|
|
trivy config Dockerfile
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 安全检查清单
|
|||
|
|
|
|||
|
|
- [ ] 不使用 `latest` tag(永远用具体版本号)→ 精确回滚
|
|||
|
|
- [ ] 不安装不必要的软件包(`apk del --purge .build-deps`)→ 最小攻击面
|
|||
|
|
- [ ] 定期更新基础镜像(修复 CVE)→ 跟上安全补丁节奏
|
|||
|
|
- [ ] 使用非 root 用户运行 → `USER appuser`
|
|||
|
|
- [ ] 镜像不包含密钥、密码、私钥 → CI/CD Secret 挂载代替 ENV
|
|||
|
|
- [ ] 使用最小基础镜像(scratch / distroless)→ 减少暴露面
|
|||
|
|
|
|||
|
|
## Distroless 镜像
|
|||
|
|
|
|||
|
|
Google 推出的**无 shell、无包管理器**的极简运行时镜像:
|
|||
|
|
|
|||
|
|
```dockerfile
|
|||
|
|
# 最极致的精简
|
|||
|
|
FROM gcr.io/distroless/static-debian12
|
|||
|
|
COPY --from=builder /app/server .
|
|||
|
|
USER nonroot
|
|||
|
|
CMD ["./server"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> ⚠️ 注意:distroless 镜像没有 shell (`/bin/sh`),调试时需要额外工具(如 debug 镜像或 `kubectl exec` 到 sidecar)。
|
|||
|
|
|
|||
|
|
### 常用 Distroless 变种
|
|||
|
|
|
|||
|
|
| 镜像 | 适用场景 | 大小 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| `gcr.io/distroless/static-debian12` | 纯静态二进制 | ~2MB |
|
|||
|
|
| `gcr.io/distroless/base-debian12` | 需要 glibc 的动态链接程序 | ~30MB |
|
|||
|
|
| `gcr.io/distroless/java17` | Java 应用(含 JRE) | ~150MB |
|
|||
|
|
| `gcr.io/distroless/nodejs18` | Node.js 应用 | ~100MB |
|
|||
|
|
|
|||
|
|
## 镜像压缩对比参考
|
|||
|
|
|
|||
|
|
| 基础镜像 | 典型 Go 服务大小 | 备注 |
|
|||
|
|
|---------|-----------------|------|
|
|||
|
|
| `ubuntu:22.04` | ~35 MB | 完整发行版 |
|
|||
|
|
| `debian:bookworm-slim` | ~20 MB | 裁剪后较常用 |
|
|||
|
|
| `alpine:3.19` | ~7 MB | musl libc,偶有兼容问题 |
|
|||
|
|
| `distroless/static` | ~5 MB | 无 shell,生产推荐 |
|
|||
|
|
| `scratch` | <取决于二进制 | 仅静态链接二进制可运行 |
|
|||
|
|
|
|||
|
|
> [!warning] Alpine 与 musl libc
|
|||
|
|
>
|
|||
|
|
> Alpine 使用 musl libc 而非 glibc。某些 C 扩展(如 `node-gyp`、部分 Python wheel)可能无法编译或运行时行为不一致。Go 服务通过 `CGO_ENABLED=0` 规避了此问题,但 Node.js/Python 应用需谨慎评估。
|
|||
|
|
|
|||
|
|
## 镜像仓库管理与流转
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
Dev["开发者本地"] -->|"docker push"| DEV_REPO["dev 仓库<br/>v1.2.3-dev"]
|
|||
|
|
|
|||
|
|
Staging["Staging 验证"] -->|"通过"| PROD_REPO["prod 仓库<br/>v1.2.3"]
|
|||
|
|
|
|||
|
|
K8s["K8s Cluster"] -->|"pull"| PROD_REPO
|
|||
|
|
|
|||
|
|
PR["PR Merge"] --> Tag["打标签 v1.2.3"]
|
|||
|
|
Tag --> ProdRepoMove["移动到 prod 仓库"]
|
|||
|
|
|
|||
|
|
style DEV_REPO fill:#fff3e0
|
|||
|
|
style PROD_REPO fill:#e8f5e9
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 仓库方案 | 特点 | 适用场景 |
|
|||
|
|
|---------|------|---------|
|
|||
|
|
| **Harbor** | 自托管,RBAC + 扫描 | 国内企业首选 |
|
|||
|
|
| **ECR / ACR / GCR** | 云厂商托管 | 全云环境 |
|
|||
|
|
| **Docker Hub** | 公共免费 | 开源项目 |
|
|||
|
|
|
|||
|
|
### Harbor 最佳实践
|
|||
|
|
|
|||
|
|
| 实践 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **项目隔离** | dev/prod 独立项目,不同团队有不同权限 |
|
|||
|
|
| **自动扫描** | 推送时自动触发 Trivy 扫描,阻断高危漏洞 |
|
|||
|
|
| **复制规则** | dev 仓库 → prod 仓库自动复制已通过测试的镜像 |
|
|||
|
|
| **生命周期策略** | 自动清理过期镜像和垃圾数据 |
|
|||
|
|
|
|||
|
|
## 补充:镜像签名与 Cosign
|
|||
|
|
|
|||
|
|
为了防止中间人攻击或被篡改的镜像被拉到集群,可以使用 Sigstore Cosign 对镜像签名:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 签名镜像
|
|||
|
|
cosign sign --key cosign.key harbor.example.com/app/server:v1.2.3
|
|||
|
|
|
|||
|
|
# 在 CI 中验证签名
|
|||
|
|
cosign verify --key cosign.pub harbor.example.com/app/server:v1.2.3
|
|||
|
|
|
|||
|
|
# 不依赖密钥的验证( rekor 透明日志)
|
|||
|
|
cosign verify --certificate-identity=email@company.com \
|
|||
|
|
--certificate-oidc-issuer=https://accounts.google.com \
|
|||
|
|
harbor.example.com/app/server:v1.2.3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!info] 镜像签名 vs 漏洞扫描
|
|||
|
|
>
|
|||
|
|
> 两者解决不同问题:
|
|||
|
|
> - **漏洞扫描** = 这个镜像有没有已知漏洞?
|
|||
|
|
> - **镜像签名** = 这个镜像确实是我们构建的,没有被篡改?
|
|||
|
|
>
|
|||
|
|
> 生产环境应该两者都有。签名可以作为 Gatekeeper/Admission Webhook 的一部分,在 Pod 启动前强制验证镜像签名。
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[../01-容器化/01-Dockerfile最佳实践]] — Dockerfile 中的安全加固
|
|||
|
|
- [[../01-容器化/02-多架构构建]] — Buildx 构建全平台镜像
|
|||
|
|
- [[../hhs/MS/05-部署运维/03-CICD与GitOps]] — CI 流水线中的安全扫描环节
|