11d8679f65
Created by Github action Co-authored-by: github-actions <github-actions@twenty.com>
62 lines
4.0 KiB
Plaintext
62 lines
4.0 KiB
Plaintext
---
|
||
title: 密钥轮换
|
||
icon: rotate
|
||
---
|
||
|
||
Twenty 具有两个相互独立的密钥族:
|
||
|
||
* **JWT 签名密钥** — 存储在 `core."signingKey"` 中、带有 `kid` 标签的非对称 ES256 密钥对,用于对访问令牌 / 刷新令牌进行签名和验证。
|
||
* **静态加密密钥** — `ENCRYPTION_KEY`,用于在 `enc:v2:` 封装中加密 OAuth 令牌、应用变量、签名密钥私钥、敏感配置值和 TOTP 密钥。
|
||
|
||
`APP_SECRET` 是为向后兼容而保留的旧版密钥:当未设置 `ENCRYPTION_KEY` 时,它充当静态加密 / 会话 Cookie 的后备密钥,并且仍会验证已有的 HS256 访问令牌。 它将被弃用。
|
||
|
||
## JWT 签名密钥
|
||
|
||
每个密钥都包含一个 `publicKey`(无限期保留,以便验证之前签发的令牌)、一个加密的 `privateKey`(仅在该密钥为当前密钥时使用)、一个 `isCurrent` 标志(同一时间只有一行为当前),以及一个可选的 `revokedAt`。
|
||
|
||
### 轮换当前密钥
|
||
|
||
* **手动** — 在当前行上执行 **Settings → Admin Panel → Signing keys → Revoke**。 吊销操作会清除其加密的私有材料并将其降级;下一次签名调用会自动生成一个新的 ES256 密钥对,作为新的当前密钥。 在任何其他(未被吊销的)`kid` 下签名的令牌会一直验证,直到过期。
|
||
* **Enterprise(自动)** — 每日 cron(`'15 3 * * *'` UTC)会在现有密钥已作为当前密钥使用 `SIGNING_KEY_ROTATION_DAYS`(默认 `90`)后签发一个新的当前密钥。 先前的密钥*不会*被吊销,因此在其下签名的令牌仍然可以通过验证。 使用 `yarn command:prod cron:register:all` 注册一次。
|
||
|
||
<Note>Enterprise 的 cron 和 `SIGNING_KEY_ROTATION_DAYS` 从 v2.6+ 版本开始提供。</Note>
|
||
|
||
### 吊销密钥(仅限泄露 / 紧急情况)
|
||
|
||
在非当前行上执行 **Settings → Admin Panel → Signing keys → Revoke**。 会清除加密的私有材料,设置 `revokedAt`,并拒绝在该 `kid` 下签名的所有现有令牌。
|
||
|
||
## 轮换 `ENCRYPTION_KEY`
|
||
|
||
<Note>下面描述的 `secret-encryption:rotate` 命令从 v2.6+ 版本开始提供。</Note>
|
||
|
||
每个加密值都封装为 `enc:v2:\<keyId>:\<payload>`,其中 `\<keyId>` 是从原始密钥派生出的 8 位十六进制前缀。 轮换是在线且可恢复的。
|
||
|
||
1. **生成新密钥**:`openssl rand -base64 32`。
|
||
|
||
2. **在 `.env` 中并排配置两个密钥**,然后重启:
|
||
```ini
|
||
ENCRYPTION_KEY=NEW_VALUE
|
||
FALLBACK_ENCRYPTION_KEY=OLD_VALUE
|
||
```
|
||
新的写入使用新密钥,现有行仍通过后备密钥解密。
|
||
|
||
3. **重新加密现有行**:
|
||
|
||
```bash
|
||
docker exec -it {server_container} yarn command:prod secret-encryption:rotate
|
||
```
|
||
|
||
该命令会遍历六个位置(`connected-account-tokens`、`application-variable`、`application-registration-variable`、`signing-key-private-keys`、`sensitive-config-storage`、`totp-secrets`)。 SQL 过滤器会跳过已使用新 `\<keyId>` 的行,因此该命令是幂等的:可根据需要中断并重新运行。 如果任意一行失败则以非零状态退出 — 重新运行以重试。
|
||
|
||
| 标志 | 描述 |
|
||
| ---------------------------------------- | ---------------------------- |
|
||
| `-s, --site \<site>` | 仅限单个位置。 |
|
||
| `-b, --batch-size \<n>` | 每批处理的行数(默认 `200`,最大 `5000`)。 |
|
||
| `-d, --dry-run` | 在内存中解密并重新加密,跳过 `UPDATE`。 |
|
||
|
||
4. 一旦 `--dry-run` 显示没有剩余行,就**删除后备密钥**:移除 `FALLBACK_ENCRYPTION_KEY` 并重启。
|
||
|
||
## 旧版 `APP_SECRET` 支持
|
||
|
||
从未设置 `ENCRYPTION_KEY` 的旧实例会使用 `APP_SECRET` 作为静态加密密钥(以及会话 Cookie 密钥,从中派生)。 此路径为向后兼容而保留,但已被**弃用** — 请设置专用的 `ENCRYPTION_KEY`,并按照上述轮换流程迁移离开它。 `APP_SECRET` 本身仍用于验证旧版 HS256 访问令牌。
|