路线图

29-DevSecOps

星辉 2026-07-02 阅读 3 min 559 字 路线图
29-DevSecOps 封面

传统研发流程里,安全测试往往排在最后——等功能开发完毕才交给安全团队做渗透测试,结果要么上线前发现一堆高危漏洞,要么"先上线再说",安全变成摆设。DevSecOps 的核心思想:安全左移,把安全检查前置到每一个研发阶段。本章覆盖安全左移理念、SAST/SCA/DAST 工具链、镜像签名、K8s 安全加固。

安全左移#

"左移"来自研发生命周期的时间轴:代码编写在左,生产部署在右。安全左移意味着在时间轴上尽量靠左发现问题——在 IDE 里、在 commit 钩子里、在 CI 流水线里,而不是等到生产环境。

成本与阶段相关:生产环境修复一个漏洞的成本是开发阶段的 30 倍以上。越晚发现,需要回滚的代码越多,影响面越广,协调成本越高。

安全即代码#

安全规则本身也应该版本化管理——OPA 策略、Semgrep 规则、Vault 的 Secret 路径定义都应该存在 Git 里,跟代码一起 Review、测试、部署。这样才能避免"安全配置漂移"。

每个阶段的安全门禁#

text
代码提交 → SAST 扫描 → 依赖漏洞扫描(SCA) → 构建镜像 → 镜像漏洞扫描
       → 镜像签名 → 部署 Staging → 动态扫描(DAST) → 合规检查 → 生产部署

每个阶段都有"门禁"——发现高危问题则阻断流水线。门禁严格程度分级:HIGH 阻断,MEDIUM 警告,LOW 仅记录。

代码阶段:SAST#

静态应用安全测试(SAST)分析源代码,不需要运行程序。能发现 SQL 注入、XSS、路径遍历、硬编码密钥、不安全加密等。

Semgrep:轻量级可定制#

yaml
# 自定义规则——检测硬编码 AWS 密钥
rules:
  - id: hardcoded-aws-key
    patterns:
      - pattern: |
          $KEY = "AKIA..."
    message: "检测到硬编码的 AWS Access Key,请使用环境变量或 Vault"
    languages: [python, go, javascript]
    severity: ERROR
bash
# 使用官方规则集扫描
semgrep --config=p/security-audit --config=p/owasp-top-ten .

Pre-commit 钩子#

最早的安全左移是在开发者本地——提交前自动运行扫描:

yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    hooks:
      - id: gitleaks
        name: 检测敏感信息泄露
  - repo: https://github.com/returntocorp/semgrep
    hooks:
      - id: semgrep
        args: ['--config=p/secrets', '--error']

依赖阶段:SCA#

现代应用 70-90% 的代码来自开源依赖。Log4Shell(CVE-2021-44228)就是最典型的例子——Java 应用广泛使用的 log4j 存在 RCE 漏洞,几乎影响了所有 Java 生态。SCA(软件成分分析)解决这类问题。

Trivy:最主流的扫描工具#

bash
# 扫描镜像
trivy image nginx:1.25

# 只报告 HIGH 和 CRITICAL
trivy image --severity HIGH,CRITICAL --ignore-unfixed nginx:1.25

# 扫描文件系统(依赖文件)
trivy fs --scanners vuln,secret .   # 旧参数 --security-checks 自 v0.37 起弃用

CI 中集成——两次扫描设计:LOW/MEDIUM 只打印不阻断,HIGH/CRITICAL 直接失败:

yaml
trivy-scan:
  script:
    - trivy image --exit-code 0 --severity LOW,MEDIUM ${IMAGE}
    - trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed ${IMAGE}

--ignore-unfixed 过滤掉暂无修复版本的漏洞,减少误报噪音。

最小化基础镜像#

选择合适的基础镜像从源头减少攻击面:

dockerfile
# 避免:完整的 Ubuntu/Debian
FROM ubuntu:22.04

# 推荐:distroless(只包含运行时)
FROM gcr.io/distroless/static-debian12

# Go 应用最佳实践:多阶段构建 + distroless
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .

FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
USER nonroot:nonroot
ENTRYPOINT ["/server"]

使用 distroless 镜像后,Trivy 扫描结果通常从几十个 CVE 降到个位数甚至零。

镜像签名:Cosign#

供应链攻击(SolarWinds 事件)让行业重视软件供应链安全。Cosign 是 Sigstore 项目的核心工具,对容器镜像签名和验证。

bash
# 生成密钥对
cosign generate-key-pair
# cosign.key(私钥)存 CI 密钥管理系统,cosign.pub(公钥)公开

# 在 CI 中签名
cosign sign --key env://COSIGN_PRIVATE_KEY ${IMAGE}

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

Kyverno 强制验证#

Kyverno 是 K8s 原生策略引擎,在 Admission 阶段拦截未签名镜像:

yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds: [Pod]
              namespaces: [production, staging]
      verifyImages:
        - imageReferences: ["myregistry.com/myapp/*"]
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      ...
                      -----END PUBLIC KEY-----

强制要求所有部署到 production/staging 的 Pod,镜像必须有 Cosign 签名,否则拒绝创建。

K8s 安全加固#

Pod Security Admission#

K8s 1.25 用 Pod Security Admission(PSA)替代了 PodSecurityPolicy。三个策略级别:

  • privileged:无限制(只给基础设施命名空间)
  • baseline:禁止已知高危配置(hostPID、hostIPC、privileged 容器)
  • restricted:最严格(只读文件系统、非 root 运行、禁止特权提升)

通过命名空间标签启用:

yaml
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

SecurityContext 最佳实践#

yaml
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
    runAsGroup: 10001
    fsGroup: 10001
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: [ALL]
      volumeMounts:
        - name: tmp          # 需要写临时文件时挂载 emptyDir
          mountPath: /tmp
  volumes:
    - name: tmp
      emptyDir: {}

Network Policy:微分段#

默认 K8s 所有 Pod 互通。Network Policy 实现微分段,最小化爆炸半径:

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
spec:
  podSelector:
    matchLabels: { app: backend }
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector:
            matchLabels: { app: frontend }    # 只允许 frontend 访问
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels: { app: postgres }     # 只允许访问数据库
      ports:
        - protocol: TCP
          port: 5432

完整 DevSecOps 流水线#

yaml
stages:
  - security-sast       # Semgrep + SonarQube
  - security-sca        # Snyk 依赖扫描
  - build
  - security-image      # Trivy 镜像扫描
  - sign                # Cosign 签名
  - deploy-staging
  - security-dast       # ZAP 动态扫描
  - deploy-production   # 人工审批

门禁策略#

阶段工具阻断条件
代码扫描SemgrepERROR 级别规则
代码质量SonarQubeQuality Gate 不通过
依赖扫描Snyk/TrivyCVSS ≥ 7.0
镜像扫描TrivyHIGH/CRITICAL 且有修复版本
运行时Kyverno未签名镜像
合规kube-benchCIS 检查项 FAIL 超阈值

落地注意事项#

渐进式引入,不要一刀切。第一周先跑扫描但不阻断,收集现有代码库的漏洞基线。第二周对新增代码启用阻断。第三周再逐步要求修复存量漏洞。

维护误报白名单。每个工具都有误报。建立白名单流程:安全团队 Review 后,将特定问题加入白名单并记录理由和到期时间。

安全指标可视化。把扫描结果推到 Grafana——每周新增漏洞数、修复平均时间(MTTR)、各严重级别趋势。数据可见,才能驱动改进。

小结#

DevSecOps 的核心是安全左移——把安全检查从末尾挪到每个研发阶段。SAST 扫代码(Semgrep)、SCA 扫依赖(Trivy/Snyk)、镜像扫描(Trivy)、镜像签名(Cosign)+ Kyverno 强制验证,构成从代码到生产的全链路安全门禁。K8s 层面用 Pod Security Admission(restricted 级别)+ SecurityContext + Network Policy 加固。落地要渐进——先跑不阻断收集基线,再逐步启用门禁。安全不是一次性项目,而是持续运营的能力建设。