mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
193 lines
9.7 KiB
Markdown
193 lines
9.7 KiB
Markdown
# QingLong 3.0 Cluster Security Administration
|
||
|
||
该命令是 Cluster Identity、API Credential 和 Security Audit 的短生命周期管理入口,不适用于 Edge/Standalone,也不会启动 HTTP listener。每次进程只执行一个命令,最多打开一个 admin PostgreSQL 连接,然后退出。
|
||
|
||
## 私有输入
|
||
|
||
准备一个仅当前操作者可访问的目录;command、assertion、keyset 和 pepper 必须使用规范绝对路径,私有文件不能向 group/world 开放。credential issue/rotate 还需要一个已存在的私有 delivery 目录,但目标文件必须不存在。
|
||
|
||
keyset 使用既有 generation/revocation 协议,例如:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"generation": 1,
|
||
"issuer": "https://identity.example.test/",
|
||
"audience": "qinglong3-security-administration",
|
||
"keys": [
|
||
{
|
||
"alg": "EdDSA",
|
||
"crv": "Ed25519",
|
||
"kid": "REPLACE_WITH_KEY_ID",
|
||
"kty": "OKP",
|
||
"use": "sig",
|
||
"x": "REPLACE_WITH_ED25519_PUBLIC_JWK_X"
|
||
}
|
||
],
|
||
"revokedKids": [],
|
||
"assuranceMappings": [
|
||
{
|
||
"acr": "urn:example:mfa",
|
||
"assurance": "multi_factor",
|
||
"requiredAmr": ["pwd", "otp"]
|
||
},
|
||
{
|
||
"acr": "urn:example:hardware",
|
||
"assurance": "hardware",
|
||
"requiredAmr": ["hwk"]
|
||
}
|
||
],
|
||
"constraints": {
|
||
"maxAssertionBytes": 8192,
|
||
"maxLifetimeMs": 300000,
|
||
"maxAuthenticationAgeMs": 300000,
|
||
"clockSkewMs": 5000
|
||
}
|
||
}
|
||
```
|
||
|
||
JWT header 必须使用 `typ=ql3-security-administration+jwt`;payload 的 issuer/audience 必须匹配 keyset,并包含 `ql3_purpose=security-administration`。只接受当前、未撤销的强认证 principal。其他管理面即使使用同一签名 key,也会因 type/purpose/audience 不同而被拒绝。
|
||
|
||
单 pepper 是现有 API credential digest authority 要求的 32-byte canonical base64url 值。它必须与已存 credential 的 pepper authority 一致,不要为了单次命令临时生成新值,也不要写入 command JSON。D-407 后也可以提供最多两代的私有 keyring:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"activePepperKeyId": "api-pepper-2026-08",
|
||
"keys": [
|
||
{ "pepperKeyId": "legacy-v1", "pepper": "REPLACE_WITH_32_BYTE_BASE64URL" },
|
||
{ "pepperKeyId": "api-pepper-2026-08", "pepper": "REPLACE_WITH_32_BYTE_BASE64URL" }
|
||
]
|
||
}
|
||
```
|
||
|
||
keyring 不超过 2 KiB,文件必须为 canonical、non-symlink、私有 regular file。`--pepper` 与 `--pepper-keyring` 必须且只能选择一个;单 pepper 明确映射到 `legacy-v1`,不会自动猜测历史 key。
|
||
|
||
## 精确命令
|
||
|
||
注册 Identity:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"operation": "identity.register",
|
||
"request": {
|
||
"mutationId": "123e4567-e89b-42d3-a456-426614174301",
|
||
"requestId": "security-identity-register-20260825-1",
|
||
"expectedCurrentVersion": 0,
|
||
"subject": { "type": "api_app", "id": "automation-client" }
|
||
}
|
||
}
|
||
```
|
||
|
||
`identity.enable` 和 `identity.disable` 使用相同 request shape,只需修改 operation、mutationId、requestId 和 expectedCurrentVersion。
|
||
|
||
签发 API Credential:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"operation": "credential.issue",
|
||
"request": {
|
||
"mutationId": "123e4567-e89b-42d3-a456-426614174302",
|
||
"requestId": "security-credential-issue-20260825-1",
|
||
"expectedCurrentVersion": 0,
|
||
"credentialId": "automation-primary",
|
||
"subject": { "type": "api_app", "id": "automation-client" },
|
||
"notBeforeAtMs": 1787596800000,
|
||
"expiresAtMs": 1787683200000
|
||
}
|
||
}
|
||
```
|
||
|
||
`credential.rotate` 使用同一完整 shape 和新的 mutationId;`credential.revoke` 必须删除 `notBeforeAtMs`、`expiresAtMs`,且不提供 `--delivery`。所有 mutation 都必须携带当前版本 fence。
|
||
|
||
有界查询 Security Audit:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"operation": "audit.list",
|
||
"request": {
|
||
"limit": 25,
|
||
"filter": { "outcome": "allowed" }
|
||
}
|
||
}
|
||
```
|
||
|
||
limit 范围为 1–200。可选 filter 只有 projectId、subject 和 outcome;翻页使用上一页的 exact `{occurredAtMs,eventId}` 作为 `before`,不支持 offset、自由文本或无界导出。
|
||
|
||
退休旧 pepper 前检查当前引用:
|
||
|
||
```json
|
||
{
|
||
"schemaVersion": 1,
|
||
"operation": "pepper.references",
|
||
"request": {
|
||
"pepperKeyId": "legacy-v1",
|
||
"limit": 64
|
||
}
|
||
}
|
||
```
|
||
|
||
结果只含数据库观察时间、请求的 key ID、有界 credential ID 列表和 `hasMore`。它不返回 token/digest/material,也不删除任何对象。必须继续分页和轮换/撤销旧 credential,直到在稳定部署下得到空引用结果,才能进入 material contraction。
|
||
|
||
## 执行
|
||
|
||
生产默认要求 TLS hostname verification:
|
||
|
||
```sh
|
||
export QL3_POSTGRES_ADMIN_URL='postgresql://ql3_admin:REDACTED@postgres.example.test:5432/qinglong'
|
||
export QL3_POSTGRES_ADMIN_TLS_SERVERNAME='postgres.example.test'
|
||
export QL3_POSTGRES_ADMIN_TLS_CA_FILE='/secure/qinglong3/postgres-ca.pem'
|
||
|
||
ql3-cluster-admin security \
|
||
--command=/secure/qinglong3/security-command.json \
|
||
--assertion=/secure/qinglong3/security-assertion.jwt \
|
||
--keyset=/secure/qinglong3/security-keyset.json \
|
||
--pepper=/secure/qinglong3/api-credential-pepper \
|
||
--delivery=/secure/qinglong3/delivery/new-api-credential.json
|
||
```
|
||
|
||
双代轮换时把 `--pepper` 替换为:
|
||
|
||
```sh
|
||
--pepper-keyring=/secure/qinglong3/api-credential-pepper-keyring.json
|
||
```
|
||
|
||
Cluster Control 使用 `QL3_API_CREDENTIAL_PEPPER_KEYRING_FILE`;它与旧 `QL3_API_CREDENTIAL_PEPPER` 同样二选一。认证严格使用 credential record 保存的 key ID,不尝试 active/legacy fallback,也不遍历 keyring。
|
||
|
||
也可以直接调用同镜像内的 `ql3-security-admin`。测试环境只有同时设置 `QL3_POSTGRES_ADMIN_TLS_MODE=disable` 与 `QL3_POSTGRES_ADMIN_ALLOW_INSECURE=true` 才能关闭 TLS;生产禁止这样部署。
|
||
|
||
成功签发或轮换时,stdout 只包含 delivery 文件名和 SHA-256。token 只存在于新建的 `0600` delivery 文件。目标已存在时命令失败且绝不覆盖。精确重放返回 `status=existing` 且不重新发布 token;如果首次响应丢失,先检查原 delivery 文件,确实丢失时使用新的 mutationId 执行 rotate,不能尝试恢复旧 token。
|
||
|
||
## Kubernetes 一次性 Job
|
||
|
||
仓库提供显式 opt-in 的部署模板,但不会随共享 Cluster operations 安装:
|
||
|
||
- `base`:外部 PostgreSQL;默认 NetworkPolicy 只有 DNS,必须用私有 overlay 增加数据库的精确 IP/Pod egress;
|
||
- `cloudnative-pg`:使用 `ql3-postgres-admin-auth`、`ql3-postgres-rw`、`ql3-postgres-ca`;
|
||
- `credential-delivery`:在 base 上增加调用方提供的 RWO PVC;
|
||
- `cloudnative-pg-credential-delivery`:CloudNativePG 与 PVC 交付的组合。
|
||
|
||
把 `input-secret.example.yaml` 复制到仓库外的私有目录,替换四个占位值,并保持 `immutable: true`。第四项必须是最多 old/new 两代、2 KiB 内的 canonical `pepper-keyring.json`;Kubernetes 不接受旧单 pepper 文件。示例不属于任何 Kustomization。非签发操作不要选择 delivery overlay;`credential.issue` / `credential.rotate` 必须先按 `delivery-pvc.example.yaml` 创建受加密、受访问控制的 PVC,并把 manifest 中的 `replace-with-unique-delivery.json` 改为本次唯一文件名。
|
||
|
||
以 CloudNativePG 的无 delivery audit query 为例:
|
||
|
||
```sh
|
||
kubectl create -f /secure/qinglong3/security-administration-input.yaml
|
||
kubectl kustomize \
|
||
deploy/kubernetes/ql3-cluster/operations/security-administration/cloudnative-pg \
|
||
| kubectl create -f -
|
||
kubectl wait --for=condition=complete --timeout=300s \
|
||
job/ql3-security-administration -n qinglong3-system
|
||
kubectl logs job/ql3-security-administration -n qinglong3-system \
|
||
-c administrator
|
||
```
|
||
|
||
当前固定资源名只允许串行执行。收集 content-free 结果和(仅 issue/rotate)PVC 中的 `0600` delivery 文件后,删除 Job 与本次 immutable input Secret;不得重用 assertion、把 token 复制到终端输出,或以 `kubectl apply` 修改旧 Job。ADR-0501 已由三节点 K3s、三实例 CloudNativePG/PostgreSQL、真实 kubelet Secret 投影和 RWO PVC 完成一次端到端 live 验收;它验证的是应用契约与权限边界,不替代生产 control-plane HA、跨主机 STONITH/DR、加密 CSI 和外部 IdP 验收。
|
||
|
||
## 当前边界
|
||
|
||
本入口没有远程管理 API/UI、双人复核或 break-glass、自动 pepper rotation/material GC、audit retention/export/alert。D-407 已提供 old/new 双代 keyring、active issuance、exact-key authentication 和退休前引用检查;active 切换仍由显式 Secret 更新加受控滚动重启完成。Kubernetes Job stager 与常驻 Cluster Control manifest 已收敛为 keyring-only;常驻 Pod 先由 hardened init container 把 kubelet symlink 投影物化成 `0400` Pod-private 普通文件,主容器不接触原始投影。overlap→activate→contract 已在远程 run `32893754795` 中通过三节点 K3s、三实例 CloudNativePG 与三次双副本反亲和 rollout 验收;五次真实 `/api/v3` probe 区分了“认证成功但未授权”的 403 与旧 credential 收缩后被拒绝的 401,content-free 报告 SHA-256 为 `d9e9fd1395adcef60f7f360959fcad27a9b2f0b132869bc1c75043dedd400ff6`。后续 source `f8934b401d724378fe5a6ea9dbe63e696b5480b9` 已通过远程 CI run `32898407637` 的 40/40 与独立 Kubernetes deployment run `32898407590`,包含 CloudNativePG failover 和 Plugin Package PostgreSQL OCI recovery。这里关闭的是应用合同与权限边界门,内部 HTTP probe 不替代外部 ingress TLS;可选 Job 仍不默认安装,也不证明生产基础设施 HA/DR 或存储加密,admin database credential 始终不得进入常驻 Cluster Control。命令决策见 [ADR-0500](../adr/ADR-0500-short-lived-cluster-security-administration-command.md),部署决策见 [ADR-0501](../adr/ADR-0501-opt-in-kubernetes-security-administration-job.md),双代 keyring 见 [ADR-0502](../adr/ADR-0502-bounded-cluster-api-credential-pepper-keyring.md)。
|