容器安全加固实战:镜像漏洞扫描+运行时零信任防护完整指南
文章介绍了容器环境安全加固的方法,包括CI/CD漏洞检测、镜像供应链安全及运行时防护。涉及Trivy、Cosign等工具的安装配置,并提供Kubernetes安全上下文和网络策略指导。 2025-10-18 12:34:20 Author: www.freebuf.com(查看原文) 阅读量:12 收藏

1. 适用场景 & 前置条件

适用场景 :生产容器环境安全加固、CI/CD 漏洞自动化检测、镜像供应链安全、运行时攻击防御。 前置条件

  • • OS:RHEL 8+/Ubuntu 20.04+;内核 5.4+(支持 seccomp/AppArmor)
  • • 容器运行时:Docker 20.10+/containerd 1.6+/CRI-O 1.24+
  • • Kubernetes:1.25+(PSP 已废弃,使用 Pod Security Admission)
  • • 权限:root 或 docker/kubectl admin;镜像仓库读写权限
  • • 网络:能访问 NVD、GitHub Advisory 等漏洞数据库(或离线镜像)
  • • 工具:Trivy 0.48+、Cosign 2.0+、Falco 0.36+(可选)

2. 环境与版本矩阵

组件 RHEL 8/9 Ubuntu 20.04/22.04 版本要求 最小规格
OS 内核 4.18+ 5.4+ 支持 seccomp-bpf -
Docker 20.10.23+ 24.0+ 含 BuildKit 2C/4G/50G
containerd 1.6.20+ 1.7+ 需 runc 1.1+ 2C/4G
Kubernetes 1.25-1.30 1.25-1.30 含 PSA 特性 Master: 4C/8G
Trivy 0.48+ 0.48+ DB 自动更新 1C/2G/10G(DB)
Grype 0.74+ 0.74+ Syft 0.100+ 1C/2G
Cosign 2.0+ 2.0+ 需 Rekor 服务 1C/1G
Falco 0.36+(可选) 0.36+ 内核模块/eBPforming 2C/4G
Harbor 2.8+ 2.8+ 集成 Trivy 4C/8G/100G

3. 快速清单(Checklist)

    1. 安装扫描工具(Trivy/Grype)并验证漏洞检测
    1. 配置 Dockerfile 安全基线(多阶段构建、非 root 用户)
    1. 集成 CI/CD 流水线阻断高危漏洞镜像
    1. 部署镜像签名与验证(Cosign + OPA Gatekeeper)
    1. 配置 Kubernetes SecurityContext(readOnlyRootFilesystem/runAsNonRoot)
    1. 实施 Network Policy 最小化网络暴露
    1. 启用 Pod Security Admission(Restricted 策略)
    1. 部署运行时防护(Falco 规则 + 告警)
    1. 配置镜像仓库安全(Harbor 漏洞扫描 + 签名验证)
    1. 建立定期扫描与修复流程(CVE 追踪 + 镜像重建)

4. 实施步骤

Step 1:安装与配置 Trivy 扫描工具

RHEL/CentOS:

# 安装 Trivysudo rpm -ivh https://github.com/aquasecurity/trivy/releases/download/v0.48.3/trivy_0.48.3_Linux-64bit.rpm# 验证安装trivy --version# 更新漏洞数据库trivy image --download-db-only# 检查数据库路径ls -lh ~/.cache/trivy/db/

Ubuntu/Debian:

# 安装 Trivywget https://github.com/aquasecurity/trivy/releases/download/v0.48.3/trivy_0.48.3_Linux-64bit.debsudo dpkg -i trivy_0.48.3_Linux-64bit.deb# 离线环境配置(可选)trivy image --download-db-only --cache-dir /opt/trivy-dbexport TRIVY_CACHE_DIR=/opt/trivy-db

关键参数:

  • --severity CRITICAL,HIGH:仅扫描高危漏洞
  • --exit-code 1:发现漏洞时返回非零退出码(用于 CI 阻断)
  • --ignore-unfixed:忽略暂无修复版本的 CVE

验证扫描功能:

# 扫描官方 Nginx 镜像trivy image nginx:latest# 仅输出 CRITICAL 和 HIGH 级别trivy image --severity CRITICAL,HIGH nginx:latest# JSON 格式输出trivy image -f json -o nginx-scan.json nginx:latest# 检查输出jq '.Results[0].Vulnerabilities | length' nginx-scan.json

预期输出示例:

nginx:latest (debian 12.2)Total: 87 (CRITICAL: 2, HIGH: 15, MEDIUM: 70)┌────────────┬───────────────┬──────────┬──────────────────┬───────────────┐│  Library   │ Vulnerability │ Severity │ Installed Version│ Fixed Version │├────────────┼───────────────┼──────────┼──────────────────┼───────────────┤│ openssl    │ CVE-2023-5678 │ CRITICAL │ 3.0.9-1          │ 3.0.11-1      │└────────────┴───────────────┴──────────┴──────────────────┴───────────────┘

Step 2:配置 Dockerfile 安全基线

多阶段构建 + 最小镜像 + 非 root 运行:

# 构建阶段:使用完整镜像FROM golang:1.21-alpine AS builderWORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o myapp# 运行阶段:使用 distrolessFROM gcr.io/distroless/static-debian12:nonroot# 使用非特权用户(UID 65532)USER nonroot:nonrootWORKDIR /app# 只读根文件系统(需要在 /tmp 挂载可写卷)COPY --from=builder --chown=nonroot:nonroot /app/myapp .EXPOSE8080ENTRYPOINT ["/app/myapp"]

关键安全实践:

  • 多阶段构建 :分离构建依赖与运行依赖,减少攻击面
  • Distroless 镜像 :无 shell/包管理器,减少 70% 漏洞
  • 非 root 用户 :UID ≥ 10000,防止容器逃逸提权

扫描验证:

# 构建镜像docker build -t myapp:secure .# 扫描对比trivy image --severity HIGH,CRITICAL myapp:secure# 检查用户配置docker inspect myapp:secure | jq '.[0].Config.User'# 预期输出:"nonroot"

Alpine 基础镜像替代方案:

FROM alpine:3.19RUN apk add --no-cache ca-certificates && \    addgroup -S appgroup && adduser -S appuser -G appgroupUSER appuserCOPY --chown=appuser:appgroup myapp /app/ENTRYPOINT ["/app/myapp"]

Step 3:集成 CI/CD 流水线(GitLab CI 示例)

.gitlab-ci.yml配置:

stages:-build-scan-sign-deployvariables:IMAGE_NAME:myappIMAGE_TAG:$CI_COMMIT_SHORT_SHAREGISTRY:registry.example.combuild:stage:buildimage:docker:24-dindscript:-dockerbuild-t$REGISTRY/$IMAGE_NAME:$IMAGE_TAG.-dockerpush$REGISTRY/$IMAGE_NAME:$IMAGE_TAGsecurity-scan:stage:scanimage:aquasec/trivy:latestscript:-trivyimage--exit-code1--severityCRITICAL$REGISTRY/$IMAGE_NAME:$IMAGE_TAG-trivyimage--severityHIGH,CRITICAL--formatjson--outputscan-report.json$REGISTRY/$IMAGE_NAME:$IMAGE_TAGartifacts:reports:container_scanning:scan-report.jsonexpire_in:30daysallow_failure:false# 发现 CRITICAL 漏洞时阻断流水线sign-image:stage:signimage:gcr.io/projectsigstore/cosign:v2.2script:-cosignsign--keycosign.key$REGISTRY/$IMAGE_NAME:$IMAGE_TAGonly:-main

关键配置:

  • --exit-code 1:CRITICAL 漏洞阻断部署
  • allow_failure: false:强制修复后才能继续
  • • 签名仅在主分支执行

GitHub Actions 等效配置:

-name:ScanwithTrivyuses:aquasecurity/trivy-action@masterwith:image-ref:${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{github.sha}}severity:'CRITICAL,HIGH'exit-code:1

Step 4:镜像签名与验证(Cosign)

生成签名密钥:

# 生成 Cosign 密钥对cosign generate-key-pair# 输出:cosign.key(私钥)+ cosign.pub(公钥)# 私钥存储到 GitLab/GitHub Secrets# 签名镜像cosign sign --key cosign.key registry.example.com/myapp:v1.0# 验证签名cosign verify --key cosign.pub registry.example.com/myapp:v1.0

Kubernetes 准入控制(OPA Gatekeeper + Cosign):

# 安装 Gatekeeperkubectlapply-fhttps://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.14.0/deploy/gatekeeper.yaml# 创建签名验证策略apiVersion:templates.gatekeeper.sh/v1kind:ConstraintTemplatemetadata:name:cosignsignedimagesspec:crd:spec:names:kind:CosignSignedImagestargets:-target:admission.k8s.gatekeeper.shrego:|        package cosignsignedimages        violation[{"msg": msg}] {          input.review.object.kind == "Pod"          image := input.review.object.spec.containers[_].image          not cosign_verify(image)          msg := sprintf("Image %v is not signed", [image])        }        cosign_verify(image) {          # 调用外部 Cosign 验证服务          response := http.send({            "method": "GET",            "url": sprintf("http://cosign-verifier.default.svc/verify?image=%v", [image])          })          response.status_code == 200        }

部署验证服务(简化示例):

# 使用 Policy Controller(Sigstore 官方)kubectl apply -f https://github.com/sigstore/policy-controller/releases/download/v0.8.0/release.yaml# 配置全局签名验证策略kubectl apply -f - <<EOFapiVersion: policy.sigstore.dev/v1beta1kind: ClusterImagePolicymetadata:  name: require-signed-imagesspec:  images:  - glob: "registry.example.com/**"  authorities:  - key:      data: |        $(cat cosign.pub)EOF

验证阻断效果:

# 尝试部署未签名镜像kubectl run test --image=nginx:latest# 预期错误:# Error: admission webhook denied the request:# no matching signatures found for image nginx:latest

Step 5:Kubernetes SecurityContext 配置

Pod 安全上下文模板:

apiVersion:v1kind:Podmetadata:name:secure-appspec:securityContext:runAsNonRoot:truerunAsUser:10000fsGroup:10000seccompProfile:type:RuntimeDefaultcontainers:-name:appimage:registry.example.com/myapp:v1.0securityContext:allowPrivilegeEscalation:falsereadOnlyRootFilesystem:truecapabilities:drop:-ALLadd:-NET_BIND_SERVICE# 仅允许绑定 1024 以下端口runAsNonRoot:truevolumeMounts:-name:tmpmountPath:/tmp-name:cachemountPath:/app/cachevolumes:-name:tmpemptyDir: {}-name:cacheemptyDir: {}

关键参数解释:

  • readOnlyRootFilesystem: true:防止运行时篡改文件系统
  • capabilities.drop: ALL:移除所有 Linux Capabilities
  • seccompProfile: RuntimeDefault:启用 seccomp 系统调用过滤

验证安全配置:

# 检查 Pod 运行用户kubectl exec secure-app -- id# 预期输出:uid=10000 gid=10000# 尝试写入根目录(应失败)kubectl exec secure-app -- touch /test.txt# 预期错误:touch: /test.txt: Read-only file system# 检查 Capabilitieskubectl exec secure-app -- capsh --print# 预期输出:Current: cap_net_bind_service=ep

Step 6:Network Policy 最小化暴露

默认拒绝策略:

apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:default-deny-allnamespace:productionspec:podSelector: {}policyTypes:-Ingress-Egress

精细化白名单策略:

apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:allow-app-egressnamespace:productionspec:podSelector:matchLabels:app:myapppolicyTypes:-Egressegress:# 允许访问内部数据库-to:-podSelector:matchLabels:app:mysqlports:-protocol:TCPport:3306# 允许 DNS 查询-to:-namespaceSelector:matchLabels:name:kube-system-podSelector:matchLabels:k8s-app:kube-dnsports:-protocol:UDPport:53# 允许访问外部 API(CIDR 限定)-to:-ipBlock:cidr:203.0.113.0/24ports:-protocol:TCPport:443

验证网络隔离:

# 测试内部连接(应成功)kubectl exec -n production myapp-pod -- nc -zv mysql-service 3306# 测试未授权连接(应超时)kubectl exec -n production myapp-pod -- nc -zv redis-service 6379# 预期输出:Connection timed out# 检查策略生效kubectl get netpol -n productionkubectl describe netpol allow-app-egress -n production

Step 7:Pod Security Admission(替代 PSP)

启用 PSA(K8s 1.25+ 默认启用):

# 查看当前策略kubectl get ns production -o yaml | grep pod-security# 配置 Namespace 级别策略kubectl label namespace production \  pod-security.kubernetes.io/enforce=restricted \  pod-security.kubernetes.io/audit=restricted \  pod-security.kubernetes.io/warn=restricted

Restricted 策略限制内容:

  • • 禁止特权容器(privileged: true
  • • 禁止主机网络/IPC/PID 命名空间
  • • 禁止主机端口映射
  • • 强制非 root 用户
  • • 强制只读根文件系统或明确的卷挂载
  • • 禁止 ALL Capabilities(除已批准列表)

测试策略阻断:

# 尝试部署特权容器cat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata:  name: privileged-test  namespace: productionspec:  containers:  - name: test    image: nginx    securityContext:      privileged: trueEOF# 预期错误:# Error: pods "privileged-test" is forbidden:# violates PodSecurity "restricted:latest": privileged

豁免特定工作负载(谨慎使用):

apiVersion:v1kind:Namespacemetadata:name:monitoringlabels:pod-security.kubernetes.io/enforce:baselinepod-security.kubernetes.io/audit:restrictedpod-security.kubernetes.io/warn:restricted# 豁免特定用户/ServiceAccountpod-security.kubernetes.io/exempt:prometheus-sa

Step 8:运行时防护(Falco 规则)

安装 Falco(Helm):

# 添加 Helm 仓库helm repo add falcosecurity https://falcosecurity.github.io/chartshelm repo update# 安装 Falco(eBPF 模式)helm install falco falcosecurity/falco \  --namespace falco --create-namespace \  --set driver.kind=ebpf \  --set falcosidekick.enabled=true \  --set falcosidekick.webui.enabled=true# 验证 DaemonSet 运行kubectl get pods -n falco

自定义安全规则(/etc/falco/rules.d/custom.yaml ):

-rule:UnauthorizedProcessinContainerdesc:Detectshellorpackagemanagerexecutioninproductioncontainerscondition:>    spawned_process and    container and    (proc.name in (sh, bash, ash, z

文章来源: https://www.freebuf.com/articles/web/453287.html
如有侵权请联系:admin#unsafe.sh