mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
131 lines
5.1 KiB
Markdown
131 lines
5.1 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。
|
||
|
||
## 精确命令
|
||
|
||
注册 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、自由文本或无界导出。
|
||
|
||
## 执行
|
||
|
||
生产默认要求 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
|
||
```
|
||
|
||
也可以直接调用同镜像内的 `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。
|
||
|
||
## 当前边界
|
||
|
||
本入口没有远程 API/UI、双人复核或 break-glass、pepper rotation、audit retention/export/alert,也没有默认安装的 Kubernetes Job。生产部署应把它放在受控工作站或自行审查的一次性 Job 中,并确保 admin database credential 不进入常驻 Cluster Control。完整安全决策见 [ADR-0500](../adr/ADR-0500-short-lived-cluster-security-administration-command.md)。
|