10 KiB
Fischer AgentKit — 密钥轮换 Runbook
本文档给出全部 11 个 secret slot(KTD-4 / R2a)的轮换操作流程。 所有轮换遵循「双密钥并行 → 切换 → 旧密钥退役」原则,避免服务中断。
通用轮换原则
- 零停机:新密钥生效后再淘汰旧密钥,避免单点切换失败。
- 审计先行:轮换前记录当前密钥指纹(hash8),轮换后校验变更。
- 回滚预案:保留旧密钥值至少一个轮换周期,便于快速回滚。
- 权限最小化:轮换操作仅限归属角色,操作全程审计日志。
- 后端无关:以下步骤适用于 Sealed Secrets 与 External Secrets 两种后端, 差异处单独标注。
Slot 1:JWT 签名密钥(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 2:API Key(180 天)
影响:使用旧 API Key 的客户端调用失败。
步骤:
# 1) 生成新 API Key
NEW_KEY=$(openssl rand -hex 32)
# 2) 更新密钥后端(同 Slot 1 步骤 2,key=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 3:DB 凭据 / DATABASE_URL(90 天)
影响:数据库连接断开,需与 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 4:IDP 签名证书(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 providers(90 天)
影响:对应 provider 的 LLM 调用失败。JSON map 结构,单 provider 轮换不影响其他。
步骤:
# 1) 在 provider 控制台生成新 key(DashScope / 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 6:TLS 服务器证书(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 7:mTLS 客户端证书(180 天)
影响:与下游服务(KMS/HSM、内部 API)mTLS 握手失败。
步骤:
# 1) 从内部 PKI 签发新客户端证书 + 私钥 + CA 证书
# 2) 更新密钥后端(key=mtls-client-cert,PEM 文本)
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 8:KMS/HSM PKCS#11 PIN(365 天)
影响: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 轮换需物理 / 管理员协同,建议提前预约维护窗口。
回滚:恢复旧 PIN(HSM 管理工具支持回滚 PIN)。
Slot 9:OTel exporter token(90 天)
影响:遥测数据上报失败(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 10:Redis 密码(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 11:PostgreSQL 密码(90 天)
影响:PostgreSQL 连接断开(用户/会话/审计)。需 DBA 协同。
DEPLOY-4 注意:应用通过 DATABASE_URL(Slot 3)内嵌密码连接 PG,不读 POSTGRES_PASSWORD env。
POSTGRES_PASSWORD 仅用于 PG StatefulSet 初始化。轮换 PG 密码时须同步更新 Slot 3(database-url),
否则应用连接会失败。
步骤:
# 1) PostgreSQL 轮换密码(DBA)
psql -U postgres -c "ALTER USER agentkit WITH PASSWORD 'new-pg-pwd';"
# 2) 更新密钥后端 — 两个 key 须同步:
# a) postgres-password(PG 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 旧密码 + 恢复两个 Secret(postgres-password + database-url)。
紧急轮换(泄露响应)
发生密钥泄露时,立即按以下流程处置:
- 隔离:撤销泄露密钥(provider 控制台 / HSM / CA)
- 轮换:按上述对应 slot 步骤紧急轮换
- 审计:检索泄露密钥的使用日志,确认无异常调用
- 通报:通知安全团队 + 受影响用户
- 复盘:记录泄露原因,加固访问控制
紧急轮换不遵循「双密钥并行」原则,直接切换到新密钥并废弃旧密钥。