引言 #
GitOps 将基础设施即代码 (IaC) 的理念扩展到整个部署流程,但随之而来的是新的安全挑战:GitHub Token 泄露、Pipeline 注入、供应链攻击。本文将演示如何构建安全的 GitOps 体系。
GitOps 安全模型 #
┌────────────────────────────────────────────────────┐
│ GitOps 信任边界 │
├────────────────────────────────────────────────────┤
│ │
│ [可信] Git Repository (签名提交) │
│ └─ [可信] CI/CD Pipeline (受控 Runner) │
│ └─ [可信] Artifact Registry (签名镜像) │
│ └─ [可信] Kubernetes Cluster (准入控制) │
│ │
│ [不可信] 外部输入 │
│ └─ Pull Request (未审计代码) │
│ └─ Pipeline Logs (可能泄露 Secrets) │
│ └─ Dependencies (可能包含恶意包) │
│ │
└────────────────────────────────────────────────────┘Secret 管理 #
SOPS + Age 加密 #
# 安装 SOPS
go install go.mozilla.org/sops/v3@latest
# 生成 Age 密钥
age-keygen -o .age.key
# 加密 Kubernetes Secret
cat secret.yaml | sops --encrypt --age $(cat .age.key | grep -o "age1.*") > secret.enc.yaml.sops.yaml 配置:
creation_rules:
- path_regex: k8s/.*\.yaml$
age: >-
age1qljz4djpo64nnn82k3aoz2e0hyl6mjwt0xp0m740jzn2tn32mvesj4mzp0External Secrets Operator #
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: secret/data/production/db
property: connection_stringPipeline 安全防护 #
GitHub Actions 安全配置 #
# .github/workflows/deploy.yml
name: Secure Deploy Pipeline
on:
pull_request:
branches: [main]
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 依赖安全扫描
- name: Audit Dependencies
run: npm audit --production
# 代码安全扫描 (SAST)
- name: Semgrep Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
# 容器镜像扫描
- name: Scan Image
uses: anchore/scan-action@v3
with:
image: myapp:latest
fail-build: true
# 使用 OIDC 代替长期 Token
- name: Authenticate to GCP
uses: google-github-actions/auth@v1
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: deploy@project.iam.gserviceaccount.com
# 禁止 Secrets 泄露到日志
- name: Deploy
run: ./deploy.sh
env:
# 使用 GITHUB_TOKEN (短期) 而非 PAT (长期)
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}供应链安全 #
SBOM 生成 #
# 在 Pipeline 中生成 SBOM
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: myapp:latest
format: cyclonedx-json
output-file: sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v3
with:
name: sbom
path: sbom.jsonSigstore 镜像签名 #
# 使用 cosign 签名镜像
cosign sign --key env://COSIGN_KEY myapp:latest
# 验证签名
cosign verify --key cosign.pub myapp:latestOPA/Gatekeeper 准入控制 #
# 禁止使用 latest 标签
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: blocklatesttag
spec:
crd:
spec:
names:
kind: BlockLatestTag
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package blocklatesttag
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
endswith(container.image, ":latest")
msg := sprintf("Container %v uses :latest tag", [container.name])
}真实攻击场景 #
GitHub Token 泄露 #
场景: 开发者在 PR 评论中粘贴了完整的 CI 日志
日志包含:
- AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
- GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
修复:
1. 立即轮换泄露的凭据
2. 配置 git-secrets / gitleaks 在 commit 前扫描
3. 使用短效 Token (OIDC)gitleaks 配置 #
# 安装 gitleaks
go install github.com/gitleaks/gitleaks@latest
# Pre-commit hook
gitleaks detect --source . --report-path leak_report.json
# 添加到 .git/hooks/pre-commit
#!/bin/bash
gitleaks detect --source . --fail总结 #
- 密钥永不落盘——使用 SOPS/ESO 管理 Secret
- 短效凭据优先——OIDC 替代长期 Token
- 流水线零信任——每个 Step 最小权限
- 供应链全链路——SBOM + 签名 + 准入控制
参考资源
- SOPS: https://github.com/mozilla/sops
- External Secrets Operator: https://external-secrets.io
- Sigstore: https://sigstore.dev
- OPA/Gatekeeper: https://openpolicyagent.org