名称:容器安全 描述:容器和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-only 或 readOnlyRootFilesystem: 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