Pod 和镜像安全

Pod 和镜像安全

Pod 和容器镜像是应用安全的基础,本节介绍如何加固 Pod 和镜像安全。

Security Context

Security Context 定义 Pod 和容器的权限和访问控制设置。

Pod 级别 Security Context

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true        # 必须以非 root 用户运行
    runAsUser: 1000           # 指定用户 ID
    runAsGroup: 3000          # 指定组 ID
    fsGroup: 2000             # 文件系统组 ID
    fsGroupChangePolicy: "OnRootMismatch"  # 仅在不匹配时更改
    seccompProfile:
      type: RuntimeDefault    # 使用默认 seccomp 配置
  
  containers:
  - name: app
    image: myapp:1.0
    securityContext:
      allowPrivilegeEscalation: false  # 禁止权限提升
      readOnlyRootFilesystem: true     # 只读根文件系统
      capabilities:
        drop:
        - ALL                  # 删除所有 capabilities
        add:
        - NET_BIND_SERVICE     # 只添加必需的

Capabilities 管理

# ❌ 危险:给予过多权限
securityContext:
  capabilities:
    add:
    - SYS_ADMIN    # 几乎等同于 root
    - NET_ADMIN

# ✅ 推荐:最小权限
securityContext:
  capabilities:
    drop:
    - ALL
    add:
    - NET_BIND_SERVICE  # 绑定 1024 以下端口
    - CHOWN             # 更改文件所有权(如果需要)

常用 Capabilities

  • NET_BIND_SERVICE: 绑定特权端口(< 1024)
  • CHOWN: 更改文件所有权
  • DAC_OVERRIDE: 绕过文件读写权限检查
  • SETUID: 设置进程 UID
  • SETGID: 设置进程 GID

只读根文件系统

apiVersion: v1
kind: Pod
metadata:
  name: readonly-root
spec:
  containers:
  - name: app
    image: myapp:1.0
    securityContext:
      readOnlyRootFilesystem: true
    
    # 挂载可写目录
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: cache
      mountPath: /var/cache
  
  volumes:
  - name: tmp
    emptyDir: {}
  - name: cache
    emptyDir: {}

Pod Security Standards

Kubernetes 1.25+ 推荐使用 Pod Security Standards 替代 PSP。

三个级别

  1. Privileged: 无限制(不推荐)
  2. Baseline: 最低限制(基本安全)
  3. Restricted: 严格限制(推荐生产环境)

使用 Pod Security Admission

# 在 Namespace 上设置
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Restricted 级别要求

apiVersion: v1
kind: Pod
metadata:
  name: restricted-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    seccompProfile:
      type: RuntimeDefault
  
  containers:
  - name: app
    image: myapp:1.0
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
      runAsNonRoot: true
      seccompProfile:
        type: RuntimeDefault

镜像安全

镜像扫描 - Trivy

# 安装 Trivy
brew install aquasecurity/trivy/trivy

# 扫描镜像
trivy image myapp:latest

# 输出示例:
# myapp:latest (alpine 3.18.4)
# Total: 5 (CRITICAL: 1, HIGH: 2, MEDIUM: 2, LOW: 0)
#
# ┌────────────┬──────────────┬──────────┬──────────────┬───────────────┐
# │  Library   │ Vulnerability│ Severity │ Installed    │ Fixed Version │
# ├────────────┼──────────────┼──────────┼──────────────┼───────────────┤
# │ openssl    │ CVE-2023-xxx │ CRITICAL │ 3.1.1        │ 3.1.2         │
# └────────────┴──────────────┴──────────┴──────────────┴───────────────┘

# 只显示高危及以上
trivy image --severity HIGH,CRITICAL myapp:latest

# 扫描并失败(CI/CD 中使用)
trivy image --exit-code 1 --severity CRITICAL myapp:latest

在 CI/CD 中集成 Trivy

# GitHub Actions
name: Scan Image
on: [push]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
    - name: Build image
      run: docker build -t myapp:${{ github.sha }} .
    
    - name: Run Trivy
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'myapp:${{ github.sha }}'
        format: 'sarif'
        output: 'trivy-results.sarif'
        severity: 'CRITICAL,HIGH'
    
    - name: Upload results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

镜像签名 - Cosign

# 安装 Cosign
brew install cosign

# 生成密钥对
cosign generate-key-pair

# 签名镜像
cosign sign --key cosign.key myregistry/myapp:v1.0.0

# 验证签名
cosign verify --key cosign.pub myregistry/myapp:v1.0.0

使用签名验证准入控制器

# 使用 Kyverno 验证镜像签名
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: enforce
  rules:
  - name: verify-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "myregistry/*"
      attestors:
      - entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
              -----END PUBLIC KEY-----

私有镜像仓库

# 创建 Docker Registry Secret
kubectl create secret docker-registry regcred \
  --docker-server=https://registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=myemail@example.com \
  -n production

# 在 Pod 中使用
apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: registry.example.com/myapp:v1.0.0

Dockerfile 最佳实践

安全的 Dockerfile

# ❌ 不安全的写法
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl
COPY app /app
CMD ["/app"]

# ✅ 安全的写法
# 1. 使用特定版本标签
FROM node:18.19-alpine3.19

# 2. 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 3. 设置工作目录
WORKDIR /app

# 4. 复制依赖文件并安装
COPY --chown=nodejs:nodejs package*.json ./
RUN npm ci --only=production && \
    npm cache clean --force

# 5. 复制应用代码
COPY --chown=nodejs:nodejs . .

# 6. 切换到非 root 用户
USER nodejs

# 7. 暴露端口
EXPOSE 3000

# 8. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
  CMD node healthcheck.js

# 9. 启动应用
CMD ["node", "server.js"]

多阶段构建

# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 生产阶段
FROM node:18-alpine AS production
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs package*.json ./
USER nodejs
EXPOSE 3000
CMD ["node", "dist/main.js"]

镜像优化

# 1. 使用 .dockerignore
# .dockerignore 文件:
node_modules
npm-debug.log
.git
.env
*.md
tests/

# 2. 合并 RUN 命令减少层数
RUN apt-get update && \
    apt-get install -y curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 3. 使用构建缓存
COPY package*.json ./
RUN npm ci
COPY . .

# 4. 清理不必要的文件
RUN npm ci --only=production && \
    npm cache clean --force && \
    rm -rf /tmp/*

PodDisruptionBudget

确保应用在节点维护时的可用性:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp-pdb
  namespace: production
spec:
  minAvailable: 2  # 至少保持 2 个 Pod 运行
  selector:
    matchLabels:
      app: myapp
# 或使用百分比
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp-pdb
  namespace: production
spec:
  maxUnavailable: 1  # 最多 1 个 Pod 不可用
  selector:
    matchLabels:
      app: myapp

运行时安全 - Falco

Falco 是云原生运行时安全工具,监测异常行为。

安装 Falco

# 使用 Helm 安装
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace

Falco 规则示例

# 检测在容器中执行的 shell
- rule: Terminal shell in container
  desc: A shell was spawned in a container
  condition: >
    spawned_process and container and 
    shell_procs and proc.tty != 0
  output: >
    Shell spawned in container 
    (user=%user.name container_id=%container.id 
    image=%container.image.repository)
  priority: WARNING

# 检测容器中的特权升级
- rule: Privilege Escalation Detected
  desc: Detect privilege escalation attempt
  condition: >
    spawned_process and container and
    (proc.name in (sudo, su)) and
    not user_known_privilege_escalation_activities
  output: >
    Privilege escalation detected
    (user=%user.name command=%proc.cmdline 
    container=%container.id)
  priority: CRITICAL

# 检测容器写入敏感目录
- rule: Write to sensitive directories
  desc: Detect writes to sensitive directories
  condition: >
    open_write and container and
    fd.name startswith /etc
  output: >
    File write to sensitive directory
    (user=%user.name file=%fd.name 
    container=%container.id)
  priority: WARNING

AppArmor 配置

AppArmor 提供强制访问控制(MAC)。

创建 AppArmor Profile

# /etc/apparmor.d/docker-nginx
#include <tunables/global>

profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  # 允许网络访问
  network inet tcp,
  network inet udp,

  # 允许读取必要文件
  /usr/sbin/nginx mr,
  /etc/nginx/** r,
  /var/log/nginx/** w,

  # 拒绝其他访问
  deny /etc/shadow r,
  deny /etc/passwd w,
  deny /proc/sys/** w,
}

在 Pod 中使用 AppArmor

apiVersion: v1
kind: Pod
metadata:
  name: nginx-apparmor
  annotations:
    container.apparmor.security.beta.kubernetes.io/nginx: localhost/docker-nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.25-alpine

安全检查清单

# ✅ 使用以下配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      # 1. ServiceAccount
      serviceAccountName: app-sa
      automountServiceAccountToken: false  # 如果不需要
      
      # 2. Pod Security Context
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 2000
        seccompProfile:
          type: RuntimeDefault
      
      containers:
      - name: app
        # 3. 镜像
        image: myapp:v1.2.3  # 使用特定版本,不用 latest
        imagePullPolicy: Always
        
        # 4. Container Security Context
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          capabilities:
            drop:
            - ALL
        
        # 5. 资源限制
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
        
        # 6. 健康检查
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
        
        # 7. 可写卷
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      
      volumes:
      - name: tmp
        emptyDir: {}

小结

本节介绍了 Pod 和镜像安全:

Security Context:runAsNonRoot、capabilities、只读文件系统
Pod Security Standards:Restricted 级别配置
镜像扫描:Trivy 漏洞扫描
镜像签名:Cosign 签名验证
Dockerfile:最佳实践和多阶段构建
PDB:保证应用可用性
运行时安全:Falco、AppArmor
安全清单:生产级配置模板

下一节:Secret 管理和审计。