DevOps 不做安全 = 你搭了一条高速公路,但没有护栏。这节课帮你把安全自动化嵌入已有的 CI/CD 流水线(0006)和 K8s 部署(0014-0016)。
传统安全:开发完了 → 安全团队审查 → 修 → 再审查 → 上线(周级别)
DevSecOps:每次 git push → 自动扫描 → 有问题直接挡在流水线里(秒级别)
↑
Shift Left —— 安全问题越早发现,修复成本越低
| 防线 | 在哪一步 | 工具 | 防什么 |
|---|---|---|---|
| 1. 代码扫描(SAST) | CI:commit → PR | Semgrep, SonarQube | SQL 注入、XSS、硬编码密钥 |
| 2. 密钥检测 | CI:commit → PR + pre-commit | gitleaks, truffleHog | 误提交 API Key / Token / 密码 |
| 3. 镜像扫描 | CI:build → push | Trivy, Snyk | 基础镜像 CVE、依赖漏洞 |
| 4. 运行时安全 | K8s:deploy → run | OPA/Gatekeeper, Falco | 不合规 Pod、异常行为 |
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
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 --production | devDependencies 可能包含有漏洞的工具包 |
| 固定 base image digest | node:22-alpine@sha256:abc... 而不是 node:22-alpine,防止上游投毒 |
你 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])
}
生产环境至少要有这几条策略:
:latest tag — 必须指定版本号hostPath volume — 防止容器访问宿主机文件系统resources.requests/limits — 防止一个 Pod 吃光节点资源privileged: true — 特权容器几乎等于 root on host任务:在你 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 标红
AKIAIOSFODNN7EXAMPLE),看 gitleaks 能不能抓出来。然后删掉它。下一课学 Lesson 0022:数据库运维基础——备份策略、连接池排错、迁移管理。DevOps 最常见的半夜 on-call 原因。