Files
qinglong/docs/operations/ql3-local-identity-credential.md

8.6 KiB
Raw Permalink Blame History

ql3-identity 本机 Identity/Credential 管理

ql3-identity 是一次命令、一次进程的 Owner-only 管理入口。它不启动管理服务,也不把 credential token 写入 argv、stdout、stderr、command JSON 或 SQLite。

前置条件

  • 已完成 Fresh Setup,当前 credential 属于 default Project 的 active Owner
  • deployment root、其子目录、SQLite、Owner credential presentation 与 command file 均由当前 real/effective UID 拥有;
  • 目录权限为 0700,文件权限为 0600
  • Owner pepper keyring 中存在与 SQLite active pepper 完全匹配的材料;
  • managed credential delivery 使用独立、规范化、非 symlink 的 0700 目录。

实例 Authority Project

Identity 与 API credential 是实例级对象,不属于任意单个业务 Project。 ql3-identity 只接受实例 authority Project 的当前 Owner

  • 新部署以全库最早成功消费的 Owner bootstrap challenge 所属 Project 为 authority
  • 没有任何 consumed bootstrap challenge 的旧库兼容回退到迁移内建的 default
  • 后续给其他 Project 授予 Owner 不会转移该 authority
  • secondary Project Owner 使用自己的 projectId 调用时,返回统一 authorization error,并记录 instance_authority_project_required
  • 当前没有 authority Project 转移命令,不要通过直接编辑数据库模拟转移。

command 中的 projectId 必须填写该 authority Project。它会在服务授权和最终 SQLite 事务各验证一次;直接调用 repository 不能绕过。

所有示例中的 UUID、subject 和路径都必须替换。command file 创建完成后执行:

ql3-identity run --command-file /srv/qinglong3/commands/identity.json

查询当前版本

后续 mutation 所需的 expectedCurrentVersion 必须通过受支持的 inspect 命令取得, 不要直接查询 SQLite。Identity 精确查询示例:

{
  "schemaVersion": 1,
  "operation": "identity.inspect",
  "options": {
    "deploymentRoot": "/srv/qinglong3",
    "databasePath": "/srv/qinglong3/qinglong3.sqlite",
    "profile": "edge",
    "ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
    "credentialFilePath": "/srv/qinglong3/owner-credential.json"
  },
  "request": {
    "projectId": "default",
    "target": { "type": "agent", "id": "agent-planner" },
    "requestId": "identity-inspect-agent-planner",
    "auditEventId": "10111111-1111-4111-8111-111111111111"
  }
}

Credential 精确查询把 operation 改为 credential.inspectrequest 改为:

{
  "projectId": "default",
  "credentialId": "agent-planner-primary",
  "requestId": "credential-inspect-agent-planner",
  "auditEventId": "10222222-2222-4222-8222-222222222222"
}

成功命中只返回 subject、status/state、version 和时间窗;不存在返回:

{
  "schemaVersion": 1,
  "operation": "credential.inspect",
  "projectId": "default",
  "found": false
}

found:false 只会在 Owner authorization 与事务内 credential/Project/RoleBinding 围栏全部通过后返回。输出不会包含 secret digest、pepper key ID、token、数据库路径 或交付路径。每次人工查询使用新的 auditEventIdinspect 不是 mutation,也不使用 mutationIdfailureAuditEventIdexpectedCurrentVersion

注册 Identity

{
  "schemaVersion": 1,
  "operation": "identity.register",
  "options": {
    "deploymentRoot": "/srv/qinglong3",
    "databasePath": "/srv/qinglong3/qinglong3.sqlite",
    "profile": "edge",
    "ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
    "credentialFilePath": "/srv/qinglong3/owner-credential.json"
  },
  "request": {
    "projectId": "default",
    "target": { "type": "agent", "id": "agent-planner" },
    "expectedCurrentVersion": 0,
    "mutationId": "11111111-1111-4111-8111-111111111111",
    "requestId": "identity-register-agent-planner",
    "failureAuditEventId": "11111111-1111-4111-8111-111111111112"
  }
}

identity.enableidentity.disable 使用相同结构,并把 expectedCurrentVersion 设为当前 Identity version。仍有 active Owner binding 的 User Identity 不能被禁用。

签发或轮换 Credential

{
  "schemaVersion": 1,
  "operation": "credential.issue",
  "options": {
    "deploymentRoot": "/srv/qinglong3",
    "databasePath": "/srv/qinglong3/qinglong3.sqlite",
    "profile": "edge",
    "ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
    "credentialFilePath": "/srv/qinglong3/owner-credential.json",
    "credentialDeliveryDirectory": "/srv/qinglong3/managed-credentials"
  },
  "request": {
    "projectId": "default",
    "target": { "type": "agent", "id": "agent-planner" },
    "credentialId": "agent-planner-primary",
    "expectedCurrentVersion": 0,
    "lifetimeMs": 86400000,
    "mutationId": "22222222-2222-4222-8222-222222222221",
    "requestId": "credential-issue-agent-planner",
    "failureAuditEventId": "22222222-2222-4222-8222-222222222222"
  }
}

轮换时把 operation 改为 credential.rotate,并使用当前 credential version。 lifetimeMs 范围为 60 秒至两年。

成功输出示例:

{
  "schemaVersion": 1,
  "operation": "credential.issue",
  "status": "inserted",
  "projectId": "default",
  "target": { "type": "agent", "id": "agent-planner" },
  "credentialId": "agent-planner-primary",
  "version": 1,
  "state": "active",
  "expiresAtMs": 1780000000000,
  "delivery": {
    "fileName": "managed-credential-22222222-2222-4222-8222-222222222221.ready.json",
    "digest": "64-char-lowercase-sha256"
  }
}

输出没有绝对路径和 token。ready 文件位于 command 指定的 delivery directory 权限为 0600,其 JSON 可以直接作为后续 credentialFilePath。如果 CLI 在 commit 后中断,必须原样重放同一个 command;不要更换 mutation、request、target、version 或 lifetime。

确认交付

consumer 已复制、安装并真实验证 ready credential 后,提交确认:

{
  "schemaVersion": 1,
  "operation": "credential.delivery.acknowledge",
  "options": {
    "deploymentRoot": "/srv/qinglong3",
    "databasePath": "/srv/qinglong3/qinglong3.sqlite",
    "profile": "edge",
    "ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
    "credentialFilePath": "/srv/qinglong3/owner-credential.json",
    "credentialDeliveryDirectory": "/srv/qinglong3/managed-credentials"
  },
  "request": {
    "projectId": "default",
    "credentialMutationId": "22222222-2222-4222-8222-222222222221",
    "expectedDeliveryDigest": "replace-with-issue-output-digest",
    "mutationId": "33333333-3333-4333-8333-333333333331",
    "requestId": "credential-delivery-ack-agent-planner",
    "failureAuditEventId": "33333333-3333-4333-8333-333333333332"
  }
}

数据库 acknowledgement 先提交,随后 pending/ready 文件才删除。响应丢失时原样 重放;cleanup:"absent" 表示数据库已经确认且文件已在前一次执行中安全删除。

不要在 consumer 尚未验证前 acknowledge,也不要把唯一正在用于本命令认证的 credential presentation 提前删除。

撤销 Credential

credential.revoke 不需要 delivery directory 或 lifetime

{
  "schemaVersion": 1,
  "operation": "credential.revoke",
  "options": {
    "deploymentRoot": "/srv/qinglong3",
    "databasePath": "/srv/qinglong3/qinglong3.sqlite",
    "profile": "edge",
    "ownerPepperKeyringDirectory": "/srv/qinglong3/owner-keys",
    "credentialFilePath": "/srv/qinglong3/owner-credential.json"
  },
  "request": {
    "projectId": "default",
    "target": { "type": "agent", "id": "agent-planner" },
    "credentialId": "agent-planner-primary",
    "expectedCurrentVersion": 1,
    "mutationId": "44444444-4444-4444-8444-444444444441",
    "requestId": "credential-revoke-agent-planner",
    "failureAuditEventId": "44444444-4444-4444-8444-444444444442"
  }
}

撤销 Owner 当前使用的 credential 前,必须先签发并验证另一把有效 credential。 数据库会拒绝撤销 active Owner 的最后一把凭据。

运维规则

  • 先使用 exact inspect 取得当前 version,再创建 mutation command;不要把数据库表当作 产品 API;
  • command file 是不可变 mutation 载体;响应不确定时只允许原样重放;
  • 不编辑 pending/ready 文件,不按 mtime 自动清理;
  • mutation_conflict 表示相同 mutation ID 已用于不同语义,必须人工调查;
  • owner_continuity_required 表示操作会造成 Owner 无法接管;
  • credential_or_policy_fence_rejected 表示认证后 authority 已变化,应重新认证并使用 新 mutation;
  • managed delivery 上限是 64 个条目;长期未确认项需要人工核对,不得用通用目录 GC 删除。