fischer-agentkit/docs/deployment/key-rotation-runbook.md

10 KiB
Raw Blame History

Fischer AgentKit — 密钥轮换 Runbook

本文档给出全部 11 个 secret slotKTD-4 / R2a的轮换操作流程。 所有轮换遵循「双密钥并行 → 切换 → 旧密钥退役」原则,避免服务中断。

通用轮换原则

  1. 零停机:新密钥生效后再淘汰旧密钥,避免单点切换失败。
  2. 审计先行轮换前记录当前密钥指纹hash8轮换后校验变更。
  3. 回滚预案:保留旧密钥值至少一个轮换周期,便于快速回滚。
  4. 权限最小化:轮换操作仅限归属角色,操作全程审计日志。
  5. 后端无关:以下步骤适用于 Sealed Secrets 与 External Secrets 两种后端, 差异处单独标注。

Slot 1JWT 签名密钥90 天)

影响:所有签发的 JWT access/refresh token 失效,用户需重新登录。

步骤

# 1) 生成新密钥32 字节随机串base64
NEW_KEY=$(openssl rand -base64 32)

# 2) 更新密钥后端
#    Sealed Secrets
kubectl create secret generic agentkit-secrets-new --dry-run=client -o yaml \
  --from-literal=jwt-signing-key="${NEW_KEY}" | \
  kubeseal --controller-name=sealed-secrets --controller-namespace=kube-system \
  --format yaml --merge-into agentkit-sealedsecret.yaml
#    External Secrets在 Vault/SM 中更新 agentkit/jwt-signing-key

# 3) 应用并等待同步
kubectl apply -f agentkit-sealedsecret.yaml -n agentkit
# DEPLOY-1: 用 instance 标签定位 Deployment — 避免硬编码 fullname 拼错。
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 4) 验证
kubectl exec -n agentkit deploy/agentkit -- \
  python -c "import os; print('jwt key len:', len(os.environ['JWT_SIGNING_KEY']))"

# 5) 用户重新登录(旧 token 失效)

回滚:保留旧密钥值,若新密钥异常,恢复 SealedSecret/ExternalSecret 并 restart。


Slot 2API Key180 天)

影响:使用旧 API Key 的客户端调用失败。

步骤

# 1) 生成新 API Key
NEW_KEY=$(openssl rand -hex 32)

# 2) 更新密钥后端(同 Slot 1 步骤 2key=api-key

# 3) 通知所有使用 API Key 的客户端更新agentkit pair 重新生成)

# 4) 应用并 restart
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证:用新 key 调用 /api/v1/auth/me 或 SCIM 端点

回滚:恢复旧 key 值。


Slot 3DB 凭据 / DATABASE_URL90 天)

影响:数据库连接断开,需与 DBA 协同。

步骤

# 1) DBA 在 PostgreSQL 创建新密码(或新用户)
psql -U postgres -c "ALTER USER agentkit WITH PASSWORD 'new-pg-pwd';"

# 2) 更新 DATABASE_URL含新密码
NEW_URL="postgresql+asyncpg://agentkit:new-pg-pwd@postgres:5432/agentkit"

# 3) 更新密钥后端key=database-url

# 4) restart pod连接池重建
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证:触发一次 DB 操作(如 GET /api/v1/memory

# 6) DBA 回收旧密码(确认新连接正常后)

注意PostgreSQL ALTER USER 立即生效,旧连接保持直到连接池重建。 建议在低峰期操作。

回滚DBA 恢复旧密码 + 恢复 DATABASE_URL。


Slot 4IDP 签名证书365 天)

影响OIDC/SAML 登录校验失败。多 IDP 时仅影响轮换的 IdP。

步骤

# 1) 从 IdP 管理后台导出新签名证书 PEM

# 2) 更新 JSON map保留其他 IdP 证书不变)
#    例如轮换 feishu 证书:
NEW_CERTS='{"feishu":"-----BEGIN CERTIFICATE-----\n新证书\n-----END CERTIFICATE-----","azure_ad":"<原证书>"}'

# 3) 更新密钥后端key=idp-signing-certs

# 4) restart pod证书热轮换不重启但 Helm 部署需 restart 加载新 Secret
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证:通过该 IdP 发起一次 SSO 登录

回滚:恢复旧证书 JSON map。


Slot 5第三方 API Key / LLM providers90 天)

影响:对应 provider 的 LLM 调用失败。JSON map 结构,单 provider 轮换不影响其他。

步骤

# 1) 在 provider 控制台生成新 keyDashScope / DeepSeek / OpenAI / Anthropic

# 2) 更新 JSON map保留其他 provider 不变)
NEW_KEYS='{"dashscope":"sk-new1","deepseek":"sk-new2","openai":"sk-old"}'

# 3) 更新密钥后端key=third-party-api-keys

# 4) restart pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证:调用对应 provider 的模型(如 agentkit chat 发一条测试消息)

# 6) 在 provider 控制台撤销旧 key

回滚:恢复旧 key需 provider 控制台未撤销旧 key建议保留旧 key 一个周期)。


Slot 6TLS 服务器证书365 天)

影响Ingress TLS 终止证书过期,浏览器告警 / mTLS 客户端握手失败。

步骤

# 1) 签发新证书cert-manager 自动续期,或手动从 CA 申请)
#    cert-manager 模式:证书到期前自动续期,无需手动操作。

# 2) 若手动:更新 kubernetes.io/tls 类型 Secret
kubectl create secret tls agentkit-tls \
  --cert=new.crt --key=new.key --dry-run=client -o yaml | \
  kubectl apply -f - -n agentkit

# 3) Ingress controller 自动感知新证书(无需 restart

# 4) 验证
echo | openssl s_client -connect agentkit.your-domain.com:443 -servername agentkit.your-domain.com 2>/dev/null | openssl x509 -noout -dates

注意:本 slot 不在 SealedSecret/ExternalSecret 的 encryptedData 内统一管理 TLS Secret 类型为 kubernetes.io/tls,与普通 Secret 不同)。 SealedSecret 模板中登记仅用于清单追踪,实际由 cert-manager 或运维单独管理。

回滚:恢复旧证书 Secret。


Slot 7mTLS 客户端证书180 天)

影响与下游服务KMS/HSM、内部 APImTLS 握手失败。

步骤

# 1) 从内部 PKI 签发新客户端证书 + 私钥 + CA 证书

# 2) 更新密钥后端key=mtls-client-certPEM 文本)
NEW_PEM="$(cat client.crt client.key ca.crt)"

# 3) 更新密钥后端

# 4) restart pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证:触发一次 mTLS 调用(如 KMS 签名操作)

回滚:恢复旧 PEM。


Slot 8KMS/HSM PKCS#11 PIN365 天)

影响HSM 访问失败,密钥托管 / 签名 / 解密不可用。

步骤

# 1) HSM 管理员通过 HSM 管理工具轮换 PIN具体命令依 HSM 型号)

# 2) 更新密钥后端key=kms-hsm-pin
NEW_PIN='<new-pin>'

# 3) restart pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 4) 验证:触发一次 HSM 操作(如 PKCS#11 签名)

注意HSM PIN 轮换需物理 / 管理员协同,建议提前预约维护窗口。

回滚:恢复旧 PINHSM 管理工具支持回滚 PIN


Slot 9OTel exporter token90 天)

影响遥测数据上报失败trace/metrics 丢失)。服务本身不受影响。

步骤

# 1) 在 OTel collector / 可观测性平台生成新 token

# 2) 更新密钥后端key=otel-exporter-token
# DEPLOY-5: OTEL_EXPORTER_OTLP_HEADERS 期望 'key=value' 格式OTel SDK 规范),
# 不是裸 'Bearer <token>'。secret 值必须形如 'Authorization=Bearer <token>'。
NEW_TOKEN='Authorization=Bearer new-otel-token'

# 3) restart pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 4) 验证:在 collector 端确认收到新 trace/metrics

回滚:恢复旧 token。


Slot 10Redis 密码90 天)

影响Redis 连接断开(缓存/会话/总线)。需与基础设施层协同。

步骤

# 1) Redis 轮换密码
redis-cli -a '<old-redis-pwd>' CONFIG SET requirepass '<new-redis-pwd>'

# 2) 更新密钥后端key=redis-password

# 3) 更新 Redis 服务配置(若 Redis 由 docker-compose / StatefulSet 管理,
#    需同步更新 requirepass 并 restart Redis

# 4) restart agentkit pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证
kubectl exec -n agentkit deploy/agentkit -- \
  python -c "import redis,os; redis.from_url(os.environ['REDIS_URL'],password=os.environ['REDIS_PASSWORD']).ping()"

注意Redis CONFIG SET requirepass 立即生效,旧连接保持到断开。 建议低峰期操作,且 Redis 持久化配置redis.conf需同步更新避免重启后失效。

回滚:恢复 Redis 旧密码 + 恢复 Secret。


Slot 11PostgreSQL 密码90 天)

影响PostgreSQL 连接断开(用户/会话/审计)。需 DBA 协同。

DEPLOY-4 注意:应用通过 DATABASE_URLSlot 3内嵌密码连接 PG不读 POSTGRES_PASSWORD envPOSTGRES_PASSWORD 仅用于 PG StatefulSet 初始化。轮换 PG 密码时须同步更新 Slot 3database-url 否则应用连接会失败。

步骤

# 1) PostgreSQL 轮换密码DBA
psql -U postgres -c "ALTER USER agentkit WITH PASSWORD 'new-pg-pwd';"

# 2) 更新密钥后端 — 两个 key 须同步:
#    a) postgres-passwordPG StatefulSet 初始化用)
#    b) database-url应用连接用DSN 内嵌新密码)
#       格式postgresql+asyncpg://agentkit:<new-pg-pwd>@postgres:5432/agentkit

# 3) 若 PG 由 StatefulSet 管理,需删除 PG pod 触发重新初始化(仅首次部署后需要;
#    已有数据的 PG 不会重新读 POSTGRES_PASSWORD密码由 ALTER USER 持久化)

# 4) restart agentkit pod
kubectl rollout restart deployment -l app.kubernetes.io/instance=agentkit -n agentkit

# 5) 验证
kubectl exec -n agentkit deploy/agentkit -- \
  python -c "import os; from sqlalchemy.ext.asyncio import create_async_engine; \
import asyncio; asyncio.run(create_async_engine(os.environ['DATABASE_URL']).dispose()); print('ok')"

回滚:恢复 PG 旧密码 + 恢复两个 Secretpostgres-password + database-url


紧急轮换(泄露响应)

发生密钥泄露时,立即按以下流程处置:

  1. 隔离撤销泄露密钥provider 控制台 / HSM / CA
  2. 轮换:按上述对应 slot 步骤紧急轮换
  3. 审计:检索泄露密钥的使用日志,确认无异常调用
  4. 通报:通知安全团队 + 受影响用户
  5. 复盘:记录泄露原因,加固访问控制

紧急轮换不遵循「双密钥并行」原则,直接切换到新密钥并废弃旧密钥。