容器安全Skill container-security

这个技能提供了容器化应用程序的安全最佳实践,包括Docker镜像加固、Kubernetes安全配置、镜像漏洞扫描和运行时保护。关键词:容器安全、Docker、Kubernetes、镜像扫描、Pod安全、网络策略、RBAC、漏洞扫描、运行时安全、云安全。

云安全 0 次安装 0 次浏览 更新于 3/11/2026

名称:容器安全 描述:容器和Kubernetes安全模式,包括Docker加固、镜像扫描、Pod安全标准、网络策略、RBAC、秘密管理和运行时保护。用于保护容器化应用、构建安全镜像或配置Kubernetes安全控制。

容器安全

概述

这个技能涵盖了容器化应用的安全最佳实践,包括Docker镜像加固、Kubernetes安全配置、镜像漏洞扫描和运行时保护。

关键词: 容器安全、Docker、Kubernetes、镜像扫描、Dockerfile、Pod安全、网络策略、RBAC、容器运行时、Trivy、Falco、gVisor、seccomp、AppArmor、distroless、无根容器

何时使用此技能

  • 构建安全的Docker镜像
  • 配置Kubernetes Pod安全
  • 设置容器漏洞扫描
  • 实现Kubernetes RBAC
  • 配置网络策略
  • 管理Kubernetes中的秘密
  • 设置运行时安全监控

容器安全层

控制 工具
镜像 最小化基础、漏洞扫描、签名 Trivy、Cosign、Grype
构建 多阶段构建、非root、无秘密 Docker、Buildah、Kaniko
注册表 扫描、签名验证、访问控制 Harbor、ECR、ACR
运行时 Seccomp、AppArmor、只读root gVisor、Kata、Falco
编排 Pod安全、RBAC、网络策略 Kubernetes、OPA/Gatekeeper
秘密 静态加密、外部提供者 Vault、Sealed Secrets、ESO

安全的Dockerfile模式

最小化安全的Dockerfile

# 使用特定版本,而非:latest
FROM node:20.10-alpine3.19 AS builder

# 尽早创建非root用户
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

WORKDIR /app

# 先复制依赖文件(层缓存)
COPY package*.json ./

# 使用安全标志安装依赖
RUN npm ci --only=production --ignore-scripts && \
    npm cache clean --force

# 复制应用代码
COPY --chown=appuser:appgroup . .

# 如果需要则构建
RUN npm run build

# --- 生产阶段 ---
FROM node:20.10-alpine3.19 AS production

# 安全:不运行作为root
RUN addgroup -g 1001 -S appgroup && \
    adduser -u 1001 -S appuser -G appgroup

# 安全:移除不必要的包
RUN apk --no-cache add dumb-init && \
    rm -rf /var/cache/apk/*

WORKDIR /app

# 仅复制生产工件
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./

# 安全:支持只读文件系统
RUN mkdir -p /app/tmp && chown appuser:appgroup /app/tmp

# 切换到非root用户
USER appuser

# 安全:使用dumb-init处理信号
ENTRYPOINT ["dumb-init", "--"]

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD node healthcheck.js || exit 1

# 暴露端口(非特权)
EXPOSE 3000

CMD ["node", "dist/server.js"]

无发行版生产镜像

# 构建阶段使用完整工具链
FROM golang:1.22-alpine AS builder

RUN apk add --no-cache git ca-certificates

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# 构建静态二进制文件
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags='-w -s -extldflags "-static"' \
    -o /app/server ./cmd/server

# --- 无发行版生产镜像 ---
FROM gcr.io/distroless/static-debian12:nonroot

# 复制二进制文件和CA证书
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server

# 作为非root运行(65532是无发行版中的非root用户)
USER 65532:65532

EXPOSE 8080

ENTRYPOINT ["/server"]

镜像扫描

Trivy扫描

# 扫描镜像查找漏洞
trivy image --severity CRITICAL,HIGH myapp:latest

# 扫描并生成SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# 扫描文件系统(用于构建前的CI)
trivy fs --security-checks vuln,secret,config .

# 扫描并设置退出码用于CI
trivy image --exit-code 1 --severity CRITICAL myapp:latest

# 忽略未修复的漏洞
trivy image --ignore-unfixed myapp:latest

CI流水线镜像扫描

# .github/workflows/container-security.yml
名称:容器安全

触发:
  推送:
    分支:[main]
  拉取请求:
    分支:[main]

作业:
  扫描:
    运行环境:ubuntu-latest
    步骤:
      - uses: actions/checkout@v5

      - name: 构建镜像
        run: docker build -t myapp:${{ github.sha }} .

      - name: 运行Trivy漏洞扫描器
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: '1'

      - name: 上传Trivy扫描结果
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

      - name: 运行Dockle代码检查器
        uses: erzz/dockle-action@v1
        with:
          image: myapp:${{ github.sha }}
          failure-threshold: high

      - name: 使用Cosign签名镜像
        if: github.ref == 'refs/heads/main'
        env:
          COSIGN_KEY: ${{ secrets.COSIGN_PRIVATE_KEY }}
        run: |
          cosign sign --key env://COSIGN_KEY myapp:${{ github.sha }}

Kubernetes Pod安全

Pod安全标准(PSS)

# 在命名空间级别强制执行Pod安全标准
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    # 强制执行限制策略
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    # 对基线违规发出警告
    pod-security.kubernetes.io/warn: baseline
    pod-security.kubernetes.io/warn-version: latest
    # 审计所有违规
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

安全的Pod规范

apiVersion: v1
kind: Pod
metadata:
  name: secure-app
  labels:
    app: secure-app
spec:
  # 防止容器间权限提升
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault

  # 具有最小权限的服务账户
  serviceAccountName: app-minimal-sa
  automountServiceAccountToken: false

  containers:
    - name: app
      image: myapp:v1.0.0@sha256:abc123...
      imagePullPolicy: Always

      # 容器级安全上下文
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        runAsUser: 1000
        capabilities:
          drop:
            - ALL
        seccompProfile:
          type: RuntimeDefault

      # 资源限制(防止DoS)
      resources:
        limits:
          cpu: "500m"
          memory: "256Mi"
          ephemeral-storage: "100Mi"
        requests:
          cpu: "100m"
          memory: "128Mi"

      # 通过emptyDir可写目录
      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /app/cache

      # 健康检查
      livenessProbe:
        httpGet:
          path: /health
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5

  volumes:
    - name: tmp
      emptyDir:
        sizeLimit: 10Mi
    - name: cache
      emptyDir:
        sizeLimit: 50Mi

  # DNS策略用于安全
  dnsPolicy: ClusterFirst

  # 主机设置(全部禁用以安全)
  hostNetwork: false
  hostPID: false
  hostIPC: false

网络策略

默认拒绝所有

# 默认拒绝所有入站和出站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

应用特定策略

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-network-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
    - Egress

  ingress:
    # 仅允许来自入口控制器
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

    # 允许来自特定服务
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

  egress:
    # 允许DNS
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

    # 允许数据库访问
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432

    # 允许外部HTTPS
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - protocol: TCP
          port: 443

Kubernetes RBAC

最小服务账户

# 服务账户,无自动挂载令牌
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-minimal-sa
  namespace: production
automountServiceAccountToken: false
---
# 具有最小权限的角色
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: production
rules:
  # 仅允许读取configmaps
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["app-config"]
    verbs: ["get"]
  # 仅允许读取特定秘密
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["app-credentials"]
    verbs: ["get"]
---
# 将角色绑定到服务账户
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-role-binding
  namespace: production
subjects:
  - kind: ServiceAccount
    name: app-minimal-sa
    namespace: production
roleRef:
  kind: Role
  name: app-role
  apiGroup: rbac.authorization.k8s.io

审计RBAC权限

# 列出所有集群管理员绑定(高风险)
kubectl get clusterrolebindings -o json | jq '.items[] |
  select(.roleRef.name == "cluster-admin") |
  {name: .metadata.name, subjects: .subjects}'

# 检查服务账户权限
kubectl auth can-i --list --as=system:serviceaccount:production:app-minimal-sa

# 查找过于宽松的角色(使用通配符)
kubectl get roles,clusterroles -A -o json | jq '.items[] |
  select(.rules[]?.resources[]? == "*" or .rules[]?.verbs[]? == "*") |
  {name: .metadata.name, namespace: .metadata.namespace}'

秘密管理

外部秘密操作器

# 指向HashiCorp Vault的SecretStore
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: production
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "production-role"
          serviceAccountRef:
            name: "vault-auth-sa"
---
# 从Vault同步的外部秘密
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-secrets
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        DATABASE_URL: "{{ .database_url }}"
        API_KEY: "{{ .api_key }}"
  data:
    - secretKey: database_url
      remoteRef:
        key: production/app
        property: database_url
    - secretKey: api_key
      remoteRef:
        key: production/app
        property: api_key

密封秘密

# 安装sealed-secrets控制器
helm install sealed-secrets sealed-secrets/sealed-secrets \
  --namespace kube-system

# 密封一个秘密
kubectl create secret generic my-secret \
  --from-literal=password=supersecret \
  --dry-run=client -o yaml | \
  kubeseal --format yaml > sealed-secret.yaml

# 密封的秘密可以安全地提交到git
cat sealed-secret.yaml

运行时安全

Falco规则

# 自定义Falco规则
- rule: 容器中未授权进程
  desc: 检测容器中运行的未授权进程
  condition: >
    spawned_process and
    container and
    not proc.name in (allowed_processes) and
    not container.image.repository in (trusted_images)
  output: >
    未授权进程启动(用户=%user.name 命令=%proc.cmdline
    容器=%container.name 镜像=%container.image.repository)
  priority: WARNING
  tags: [container, process]

- rule: 写入敏感目录
  desc: 检测容器中写入敏感目录
  condition: >
    open_write and
    container and
    (fd.name startswith /etc/ or
     fd.name startswith /bin/ or
     fd.name startswith /sbin/ or
     fd.name startswith /usr/bin/)
  output: >
    写入敏感目录(文件=%fd.name 用户=%user.name
    容器=%container.name 镜像=%container.image.repository)
  priority: ERROR
  tags: [container, filesystem]

- rule: 容器中生成shell
  desc: 检测容器中生成的shell
  condition: >
    spawned_process and
    container and
    proc.name in (shell_binaries) and
    not proc.pname in (allowed_shell_parents)
  output: >
    容器中生成shell(用户=%user.name shell=%proc.name
    父进程=%proc.pname 容器=%container.name)
  priority: WARNING
  tags: [container, shell]

- list: shell_binaries
  items: [bash, sh, zsh, ash, dash, ksh, tcsh, csh]

- list: allowed_shell_parents
  items: [crond, sshd, sudo]

快速参考

Dockerfile安全检查清单

检查 命令/模式
无latest标签 FROM image:specific-version
非root用户 USER 1000
镜像中无秘密 trivy fs --security-checks secret .
多阶段构建 分离构建和生产阶段
只读文件系统 --read-onlyreadOnlyRootFilesystem: true
最小化基础镜像 Alpine、distroless或scratch
签名镜像 cosign sign / cosign verify

Kubernetes安全检查清单

检查 设置
非root runAsNonRoot: true
无权限提升 allowPrivilegeEscalation: false
丢弃能力 capabilities: {drop: [ALL]}
只读root readOnlyRootFilesystem: true
资源限制 resources.limits 已定义
网络策略 默认拒绝 + 显式允许
Seccomp配置文件 seccompProfile: {type: RuntimeDefault}
无主机命名空间 hostNetwork/PID/IPC: false

参考

  • Dockerfile加固:参见 references/dockerfile-security.md 获取详细模式
  • Kubernetes安全:参见 references/kubernetes-security.md 获取全面K8s指南
  • 容器扫描:参见 references/container-scanning.md 获取扫描器配置

最后更新: 2025-12-26