Lesson 0021:DevSecOps——安全不是上线前检查,是每一步都在检查

DevOps 不做安全 = 你搭了一条高速公路,但没有护栏。这节课帮你把安全自动化嵌入已有的 CI/CD 流水线(0006)和 K8s 部署(0014-0016)。

传统安全:开发完了 → 安全团队审查 → 修 → 再审查 → 上线(周级别)
DevSecOps:每次 git push → 自动扫描 → 有问题直接挡在流水线里(秒级别)
                    ↑
                   Shift Left —— 安全问题越早发现,修复成本越低

DevSecOps 四道防线

防线在哪一步工具防什么
1. 代码扫描(SAST)CI:commit → PRSemgrep, SonarQubeSQL 注入、XSS、硬编码密钥
2. 密钥检测CI:commit → PR + pre-commitgitleaks, truffleHog误提交 API Key / Token / 密码
3. 镜像扫描CI:build → pushTrivy, Snyk基础镜像 CVE、依赖漏洞
4. 运行时安全K8s:deploy → runOPA/Gatekeeper, Falco不合规 Pod、异常行为

第一道防线:代码扫描(SAST)

SAST(Static Application Security Testing)= 不运行代码,直接读源码找安全漏洞。

# 安装 Semgrep(开源 SAST 工具,DevOps 标配)
pip install semgrep

# 在你的项目里跑(以之前 0005 的 Node.js 项目为例)
cd /tmp/docker-demo
semgrep --config=auto .
# 输出示例:
#   server.js
#     javascript.lang.security.audit.path-traversal.path-join-resolve-traversal
#       Found 'path.join' with user-controlled input — possible path traversal

# 加到 GitHub Actions(结合 0006)
cat >> .github/workflows/ci.yml << 'EOF'
  security-sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Semgrep scan
        run: |
          pip install semgrep
          semgrep --config=auto --error .
          # --error = 发现漏洞就把 CI 标红(不让合并)
EOF

Semgrep 的 --config=auto 自动检测项目语言并加载对应规则集。它不像传统 SAST 需要配置几百条规则——零配置就能跑。

第二道防线:密钥检测——救命的那道防线

所有 DevOps 工程师最害怕的凌晨 3 点电话:「你把 AWS Secret Key 提交到公开仓库了」。

# gitleaks:扫描 Git 历史,找泄露的密钥
# 安装
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz | sudo tar -xz -C /usr/local/bin gitleaks

# 在你的仓库里检测
cd ~/code/devops
gitleaks detect --source . --verbose
# 如果有漏洞:输出文件名 + 行号 + 匹配到的密钥类型

# pre-commit hook:在提交前拦截(最靠左的防御)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 每次 git commit 之前自动跑 gitleaks
gitleaks protect --staged --verbose
if [ $? -ne 0 ]; then
  echo "密钥泄露!commit 已被阻止。"
  exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
如果已经泄露了怎么办?第一步:立刻在云平台 revoke 那个 Key(AWS IAM / GitHub Settings)。第二步:清理 Git 历史(git filter-branch 或 BFG Repo-Cleaner)。第三步:git push --force。不要只删文件 + 新 commit——Git 历史里还有。

第三道防线:容器镜像扫描

你 0005 写的 Dockerfile FROM node:22-alpine——这个基础镜像有多少已知漏洞?Trivy 告诉你。

# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh

# 扫描本地镜像
docker build -t my-app:latest .
trivy image my-app:latest
# 输出:按严重级别(CRITICAL/HIGH/MEDIUM/LOW)列出 CVE

# 只关注 CRITICAL 和 HIGH(严重和高级别漏洞)
trivy image --severity CRITICAL,HIGH my-app:latest

# 加到 GitHub Actions——镜像有严重漏洞就不让 push
cat >> .github/workflows/ci.yml << 'EOF'
  security-image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t my-app:${{ github.sha }} .
      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: my-app:${{ github.sha }}
          format: table
          exit-code: 1              # 有 CRITICAL 就失败
          severity: CRITICAL,HIGH
EOF

写更安全的 Dockerfile(生产规范):

# 不安全
FROM node:22-alpine
USER root
COPY . .
CMD ["node", "server.js"]

# 安全
FROM node:22-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --production               # 不加 devDependencies
COPY --chown=appuser:appgroup . .
USER appuser                            # 不用 root 运行!
EXPOSE 3000
CMD ["node", "server.js"]

# docker run 时再加一层防护
docker run --read-only --tmpfs /tmp \
  --cap-drop=ALL --cap-add=NET_BIND_SERVICE \
  my-app:latest
# --read-only: 文件系统只读
# --cap-drop=ALL: 去掉所有 Linux capabilities
# --cap-add=NET_BIND_SERVICE: 只加回绑定端口的权限
实践为什么
USER 非 root容器逃逸漏洞利用 root 权限提权到宿主机
--read-only阻止攻击者写文件(webshell、挖矿程序)
npm ci --productiondevDependencies 可能包含有漏洞的工具包
固定 base image digestnode:22-alpine@sha256:abc... 而不是 node:22-alpine,防止上游投毒

第四道防线:K8s 策略管控(OPA/Gatekeeper)

你 0016 学过 K8s RBAC——控制谁能做什么。但 RBAC 控制不了做什么是合规的

比如:谁都能 kubectl apply -f evil-pod.yaml,只要他有权限。你怎么拦住?

# 用 OPA Gatekeeper 写一条策略:禁止使用 latest tag
# 这是面试常考题——写出一个 Rego 策略

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8sdisallowedtags
spec:
  crd:
    spec:
      names:
        kind: K8sDisallowedTags
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8sdisallowedtags
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          endswith(container.image, ":latest")
          msg := sprintf("禁止使用 latest tag: %v", [container.image])
        }
        violation[{"msg": msg}] {
          container := input.review.object.spec.containers[_]
          not contains(container.image, ":")
          msg := sprintf("镜像必须指定 tag(不能隐式用 latest): %v", [container.image])
        }

生产环境至少要有这几条策略:

动手练习:给你的 CI/CD 加上三道安全防线

任务:在你 0005 的 Node.js 项目或任何已有项目里,依次完成以下三步。

# 1. SAST 扫描
cd your-project
semgrep --config=auto .
# 看输出,修掉至少一个 HIGH/WARNING

# 2. 密钥检测
gitleaks detect --source .
# 如果检出密钥(即使只是 demo key),观察输出的格式

# 3. 镜像扫描
docker build -t your-app:test .
trivy image --severity CRITICAL,HIGH your-app:test
# 看有多少 CRITICAL——这就是为什么基础镜像要选 Alpine + 定期更新

# 4. 把三道防线串进 GitHub Actions(选做)
# 在 .github/workflows/ci.yml 里加三个 job:
#   security-sast (semgrep)
#   security-secrets (gitleaks)
#   security-image (trivy)
# 每个 job 有漏洞就把 CI 标红
你的任务:跑通上面三道扫描后,试着在代码里故意写一个硬编码的「假」AWS Key(比如 AKIAIOSFODNN7EXAMPLE),看 gitleaks 能不能抓出来。然后删掉它。
加分题:写一个 pre-commit hook,在每次 git commit 前自动跑 Semgrep + gitleaks,有漏洞就拒绝提交。

面试速记:DevSecOps 核心回答模板

  1. 「你怎么保证 CI/CD 流水线的安全?」→ 四道防线:SAST 扫代码 + gitleaks 扫密钥 + Trivy 扫镜像 + OPA 管 K8s。每一道都在 pipeline 里自动化,发现问题就 block。
  2. 「密钥泄露了怎么办?」→ 第一秒 revoke 密钥(IAM/GitHub Settings)→ 清理 Git 历史 → force push → 排查是否已被恶意使用
  3. 「Docker 镜像怎么做安全加固?」→ 非 root 用户 + read-only 文件系统 + distroless 基础镜像 + Trivy 扫描 CVE + 固定 digest

小测验

Q1:DevSecOps「Shift Left」的核心含义是什么?
Q2:不小心提交了 AWS Secret Key 到公开仓库,第一步做什么?
Q3:Docker 安全加固中,为什么必须用非 root 用户运行容器?
Q4:Trivy 在 CI/CD 流水线里的作用是什么?

推荐资源

下一步

下一课学 Lesson 0022:数据库运维基础——备份策略、连接池排错、迁移管理。DevOps 最常见的半夜 on-call 原因。

安全不是「加上就完了」。今天加的 Semgrep/Trivy/gitleaks 规则一个月后可能过时——CVE 每天都在新增,密钥模式也在变化。养成每月更新扫描规则的习惯。
← Lesson 0020 · 术语表 · Mission