Skip to content

IAM + LLM Guard 详细架构与模块流程

本文档是 iam_auth_architecture.md详细篇,聚焦 启动、模块内部状态机、关键场景时序、故障演练、demo 部署。前置概念 (3 模块职责、token 4 层 TTL、Claims、key hierarchy、Lease 模型)见主页。

目录

  • §A 启动序列
  • §B 模块内部状态机
  • §C 关键场景时序图
  • §D 故障场景演练
  • §E demo 部署架构
  • §F 修订记录

§A 启动序列

3 个域控制器(AD/CD/VD)冷启动到可对外提供 Guard 调用,需要完成 8 个阶段。下图以 AD 域为例展示,其他 2 域与之并行但彼此独立(各域有自己的 Trust Anchor 与 KMSS daemon)。

§A.1 冷启动时序(cold start)

mermaid
sequenceDiagram
    autonumber
    participant BOOT as Bootloader (GRUB/UEFI)
    participant KMSD as KMSS Daemon
    participant KMSS as libkmss.so (TEE)
    participant SB as Sidecar (iam-guard-sidecar)
    participant SDK as Agent SDK (libiamguard.so)
    participant AG as Agent Process

    BOOT->>KMSD: 启动 km systemd unit
    BOOT->>SB: 启动 sidecar systemd unit
    BOOT->>AG: 启动 agent systemd unit (依赖 KMSD+SB)

    Note over KMSD,KMSS: 阶段 1 - TEE 自检
    KMSD->>KMSS: km_init()
    KMSS-->>KMSD: TEE_OK / TEE_FAULT
    alt TEE 故障
        KMSD->>SB: HEALTH=tde_fault (gRPC stream)
        SB-->>SDK: Auth disabled (refuse all)
    end

    Note over KMSD: 阶段 2 - 加载 L0 Root
    KMSD->>KMSS: km_load_root(efuse_pub)
    KMSS-->>KMSD: root_pub, root_serial

    Note over KMSD: 阶段 3 - 加载/派生 L1 Domain Cert
    alt 本地有 L1 私钥
        KMSD->>KMSS: km_load_l1(key_id)
        KMSS-->>KMSD: l1_priv_ref
    else 本地无 L1 (首次启动 / OTA 后)
        KMSD->>KMSS: km_derive_l1(csr_template)
        KMSS-->>KMSD: l1_priv_ref, l1_cert (pending)
        KMSD->>BOOT: 持久化 l1_priv_ref 到 secure storage
    end

    Note over KMSD: 阶段 4 - 创建 L2 Workload Key
    KMSD->>KMSS: km_generate_workload(spiffe_id)
    KMSS-->>KMSD: workload_priv_ref (TEE 内)
    KMSD->>KMSD: 自签 workload SVID (TTL=1h)

    Note over SB: 阶段 5 - 加载 Trust Bundle
    SB->>SB: 读取 /etc/agent/bundles/ad.pb
    alt 文件不存在
        SB->>KMSD: bundle_get_via_ota_bootstrap()
        KMSD-->>SB: bundle_pb (via OEM backend, 仅 setup 时)
    end
    SB->>SB: 验证 root == bundle.root
    SB->>SB: 解析 CRL, 标记 revoked serial

    Note over SB: 阶段 6 - 注册到 KMSS
    SB->>KMSD: km_discovery_register(workload_id, bundle_version)
    KMSD-->>SB: discovery_ok
    SB->>SB: Watch bundle 版本变化 (inotify)

    Note over SDK,AG: 阶段 7 - Agent SDK 初始化
    AG->>SDK: iam_init(sidecar_socket="/run/agent-iam/ad.sock")
    SDK->>SB: HELLO agent_id=ad-llm-agent, asil=D
    SB->>KMSD: get_workload_svid()
    KMSD-->>SB: svid_jwt (TTL=1h)
    SB-->>SDK: HELLO_OK, svid_jwt

    Note over SDK: 阶段 8 - 预热 Session
    SDK->>SB: session_open(task="none", scope=baseline)
    SB-->>SDK: session_jwt (TTL=15min, silent_renew_on)

    Note over AG: 阶段 9 - Agent ready
    AG->>SDK: iam_ready()
    SDK-->>AG: READY

§A.2 热启动(warm start,AG restart within 1h)

mermaid
sequenceDiagram
    autonumber
    participant KMSD as KMSS Daemon
    participant SB as Sidecar
    participant SDK as Agent SDK
    participant AG as Agent Process

    Note over KMSD,SB: KMSS/Sidecar 已运行 (uptime > 0)

    AG->>SDK: iam_init(sidecar_socket)
    SDK->>SB: HELLO agent_id
    SB->>KMSD: workload_svid 是否仍有效
    alt TTL 剩余 > 50%
        SB-->>SDK: HELLO_OK, svid_jwt (复用, 不重签)
    else TTL 剩余 <= 50%
        SB->>KMSD: rotate_workload_svid()
        KMSD-->>SB: new_svid_jwt
        SB-->>SDK: HELLO_OK, svid_jwt
    end
    SDK->>SB: session_open(task="none")
    SB-->>SDK: session_jwt (silent_renew_on)
    SDK-->>AG: READY (典型 < 200ms)

§A.3 启动并发与依赖

mermaid
gantt
    title 冷启动并行时间线 (典型 3 域 AD 域)
    dateFormat ss
    axisFormat %S
    section Kernel
    TEE init           :a1, 00, 800ms
    Bootloader->KMS    :a2, after a1, 100ms
    section KMSS Daemon
    km_init            :b1, after a2, 300ms
    load L0+L1         :b2, after b1, 200ms
    create L2          :b3, after b2, 100ms
    section Sidecar
    load bundle        :c1, after b3, 150ms
    register KMS       :c2, after c1, 50ms
    section Agent SDK
    HELLO              :d1, after c2, 100ms
    session_open       :d2, after d1, 50ms
    section Agent ready
    first Guard call   :e1, after d2, 200ms

关键依赖:

  • KMSD 必须在 Sidecar 启动前 ready(Sidecar 需要 KMSS 派生 SVID)。
  • Sidecar 必须在 Agent SDK ready 前 ready(SDK HELLO 调用 Sidecar)。
  • AG 可以与 Sidecar/KMSD 并行启动,依赖 systemd After= 控制。
  • 3 域彼此独立:AD 启动失败不应阻塞 CD/VD。

§A.4 启动失败处理

阶段失败现象恢复
1 TEE 自检TEE 物理故障KMSD 进入 degradedSOC 安全灯 + OTA 重连
2 加载 L0efuse 未烧录KMSD 启动失败OEM 工厂返修
3 派生 L1KMSS 内部错误重试 3 次后失败重新生成 CSR + OEM 签字
5 加载 Bundle文件损坏Sidecar HEALTH=bundle_invalidOTA 重试 + 默认拒绝
6 KMS 注册KMSS 拒绝Sidecar crash-loop检查 SPIFFE ID 一致性
7 HELLOSidecar 未启动SDK retry 3 次后 failsystemd 依赖不对
8 session_openKMSS 限流session_open 503SDK 退避重试

§B 模块内部状态机

§B.1 Identity 模块状态机

Identity 负责解析和签发 SPIFFE ID 形态的 workload 身份。

mermaid
stateDiagram-v2
    [*] --> Uninitialized
    Uninitialized --> LoadingBundle: sidecar start
    LoadingBundle --> BundleInvalid: 文件损坏/签名错
    LoadingBundle --> BundleLoaded: 文件 OK
    BundleInvalid --> LoadingBundle: OTA push 新 bundle
    BundleLoaded --> WorkloadKeyCreating: km_generate_workload
    WorkloadKeyCreating --> WorkloadKeyError: KMSS 失败
    WorkloadKeyCreating --> WorkloadSigning: 私钥已派生
    WorkloadKeyError --> WorkloadKeyCreating: 退避重试
    WorkloadSigning --> SVIDActive: 自签 + 缓存 JWT
    SVIDActive --> SVIDActive: TTL 剩余 > 50% (复用)
    SVIDActive --> SVidRotating: TTL 剩余 <= 50%
    SVidRotating --> SVIDActive: 派生新私钥 + 签发新 JWT
    SVIDActive --> RotatingUnderCert: L1 中间证书轮换
    RotatingUnderCert --> SVIDActive: 完成 30 天 overlap
    BundleLoaded --> TrustBundleStale: 收到 bundle.new 信号
    TrustBundleStale --> BundleLoaded: 校验新 bundle 通过
    TrustBundleStale --> BundleInvalid: 校验失败, 回滚
    SVIDActive --> [*]: sidecar stop

核心不变量:

  • SVIDActive唯一稳定态,其他都是过渡态。
  • BundleLoaded 必须始终比 SVIDActive 新,否则签发的 SVID 无法被对端验证。
  • RotatingUnderCert 期间新旧 L1 都签发过 SVID,对端需支持两个 trust anchor。

§B.2 Authentication 模块状态机

Authentication 负责 token 派生、刷新、撤销。

mermaid
stateDiagram-v2
    [*] --> NoSession
    NoSession --> SessionOpening: session_open()
    SessionOpening --> SessionActive: 成功 (TTL=15min)
    SessionOpening --> SessionError: KMSS 拒绝 (scope 超限等)
    SessionError --> SessionOpening: 重试 (调整 scope)
    SessionActive --> SessionRenewing: TTL 剩余 25% (silent_renew)
    SessionRenewing --> SessionActive: 新 session_jwt (TTL=15min)
    SessionRenewing --> SessionRenewFailed: KMSS 拒绝 (race)
    SessionRenewFailed --> SessionActive: 用旧 token until expire
    SessionRenewFailed --> NoSession: 重试 2 次仍失败

    SessionActive --> TaskTokenIssuing: task_token()
    TaskTokenIssuing --> TaskTokenIssued: 成功 (TTL=30s-5min)
    TaskTokenIssuing --> TaskTokenError: parent 已过期 / scope 冲突
    TaskTokenIssued --> Consumed: SDK 调 Guard(secret_x) 使用
    TaskTokenIssued --> Expired: TTL 到期未用
    TaskTokenIssued --> Revoked: 紧急撤销 (CVE-2026-XXX)
    Consumed --> TaskTokenIssued: 同 task 重新申请 (新 jti)
    TaskTokenIssued --> TaskTokenIssued: 同一 task 多次 renew (renew_count<3)

    SessionActive --> LeaseOpening: lease_open()
    LeaseOpening --> LeaseActive: KMSS 同意 (TTL=task_duration)
    LeaseOpening --> LeaseRejected: ASIL 拒绝 / scope 冲突
    LeaseActive --> LeaseRenewing: 心跳 (30s)
    LeaseRenewing --> LeaseActive: KMSS 同意续租 (scope 不变)
    LeaseRenewing --> LeaseScopeTightened: scope 自动收紧 (近失效)
    LeaseScopeTightened --> LeaseActive: scope 收紧成功
    LeaseRenewing --> LeaseEnded: KMSS 拒绝
    LeaseActive --> LeaseEnded: natural close / task 结束
    LeaseEnded --> NoSession: 释放, 但 session 仍活动

核心不变量:

  • SessionActiveTaskTokenIssued / LeaseActive正交的: session 是"我能调用 Guard",task_token/lease 是"我这次具体能做什么"。
  • TaskTokenIssued 单次使用语义:Guard.Check 成功后 token 立即 burned。
  • LeaseScopeTightened不可逆:scope 缩小后不能再次扩大。

§B.3 Credential 模块状态机

Credential 负责 4 层密钥生命周期管理。

mermaid
stateDiagram-v2
    [*] --> L0Active
    L0Active --> L0Active: eFuse 状态正常 (出厂后不变)
    L0Active --> L0Replaced: OEM 工厂返修 (极少)
    L0Replaced --> L0Active: 新 root 烧入 + fleet OTA bundle

    L0Active --> L1Generating: 首次启动 / OTA 触发
    L1Generating --> L1Pending: CSR 生成, 待 OEM 签字
    L1Pending --> L1Active: OEM 签字回传 + 安装
    L1Pending --> L1Generating: OEM 拒绝 (rare)

    L1Active --> L1Active: 正常 (TTL=1-3y)
    L1Active --> L1Rotating: 剩余 30 天 / 紧急 CVE
    L1Rotating --> L1Active: 新私钥派生, 30 天 overlap 期
    L1Active --> L1Revoked: OEM 紧急撤销 (rare)

    L1Active --> L2Creating: workload 启动
    L2Creating --> L2Active: 私钥 TEE 内派生
    L2Creating --> L2Error: KMSS 故障
    L2Error --> L2Creating: 退避重试

    L2Active --> L2Active: SVID 在有效期内
    L2Active --> L2Rotating: TTL <= 50%
    L2Rotating --> L2Active: 新私钥 + 新 SVID

    L2Active --> L3Creating: session 建立
    L3Creating --> L3Active: ephemeral key (per session)
    L3Active --> L3Destroyed: session 结束 / TTL 到

    L1Active --> CRLPublished: 紧急 revocation
    CRLPublished --> CRLPropagated: CRL 推送到 3 域
    CRLPropagated --> L1Revoked: 所有 bundle 收到

核心不变量:

  • L0 只在工厂变更,整个 fleet 同步更新。
  • L1 overlap 期两个 L1 都信任,不允许中间状态不接受验证。
  • L2 私钥永不离开 TEE,External World 只看 handle。
  • L3 完全 ephemeral,session 关闭即焚毁。

§B.4 状态机通用语义

  • 稳定态 vs 过渡态:只有 SVIDActiveSessionActiveLeaseActiveL0ActiveL1ActiveL2ActiveL3Active 是稳定态,可被外部观察为"长期存在"。
  • 不可逆边:所有 Revoked / ScopeTightened / Destroyed 边都不可逆(除非显式 OTA 重建)。
  • 观测点:每个稳定态都向 Sidecar 的 /healthzaudit_log 发送事件。

§C 关键场景时序图

§C.1 同域 Guard.Check(最快路径)

mermaid
sequenceDiagram
    autonumber
    participant U as User Prompt
    participant AG as Agent
    participant SDK as Agent SDK
    participant SB as Sidecar (ad-guard-sidecar)
    participant KMSD as KMSS Daemon
    participant TEE as libkmss (TEE)

    U->>AG: "执行 rm -rf /"
    AG->>SDK: guard_check(action=exec, args=...)
    SDK->>SDK: 检查本地 L1 task_token 缓存
    alt 本地无 task_token
        SDK->>SB: task_token(parent=session, scope={exec:{allow=false}})
        SB->>KMSD: km_issue_task(parent_jti, scope)
        KMSD->>TEE: 用 session 私钥签 task_jwt
        TEE-->>KMSD: task_jwt (TTL=30s)
        KMSD-->>SB: task_jwt
        SB-->>SDK: task_jwt
    end
    SDK->>SB: Guard.Check(req, task_jwt)
    SB->>SB: 校验 JWT: 签名 / TTL / scope
    SB->>SB: 解析 req, 匹配策略 (Deny by default)
    SB->>SB: 命中策略 rule[id=exec-system-critical] allow=false
    SB-->>SDK: CheckResponse(allow=false, reason="rule:exec-system-critical")
    SDK->>SDK: Token marked consumed (single-use)
    SDK-->>AG: 拒绝执行, 返回安全回复
    AG-->>U: "无法执行危险命令"

典型延迟:< 5ms(task_token 缓存命中时)。

§C.2 跨域 delegation(含完整证书链)

mermaid
sequenceDiagram
    autonumber
    participant AG_CD as Agent (CD域)
    participant SDK_CD as SDK (CD)
    participant SB_CD as Sidecar (CD)
    participant KMSD_CD as KMSS (CD)
    participant SB_AD as Sidecar (AD)
    participant KMSD_AD as KMSS (AD)
    participant AG_AD as Agent (AD)

    Note over AG_CD,KMSD_AD: CD域 Agent 想调用 AD域 工具 (read_only CAN db)

    AG_CD->>SDK_CD: invoke_remote(tool=can_dump, target=AD)
    SDK_CD->>SB_CD: deleg_token(parent=session_cd, target_domain=AD, scope={can_dump})
    SB_CD->>KMSD_CD: km_issue_deleg(cross_domain=true, scope)
    KMSD_CD->>KMSD_CD: 校验 target_domain 信任 (cross_domain_trust_list)
    KMSD_CD-->>SB_CD: deleg_jwt (aud=ad-domain, TTL=2min)

    SB_CD->>SB_AD: RPC: Invoker.Invoke(req={can_dump}, token=deleg_jwt)
    Note right of SB_CD: gRPC over TCP+mTLS<br/>TCP port 7000<br/>mTLS 用双方 trust bundle

    SB_AD->>KMSD_AD: verify_deleg(deleg_jwt, expected_aud=AD)
    KMSD_AD->>KMSD_AD: 验证 CD 域签名 (cross trust list)
    alt 签名通过 + TTL ok + scope 包含 can_dump
        KMSD_AD-->>SB_AD: deleg_valid=true
        SB_AD->>AG_AD: 本地路由 can_dump 到 AD Agent
        AG_AD-->>SB_AD: can_dump result
        SB_AD-->>SB_CD: InvokeResponse(result)
    else 验证失败
        KMSD_AD-->>SB_AD: deleg_valid=false
        SB_AD-->>SB_CD: PermissionDenied (UNAUTHENTICATED)
        SB_CD-->>SDK_CD: 错误转发
        SDK_CD-->>AG_CD: 拒绝调用
    end

关键点

  • deleg_jwt 在 CD 域用 CD 的 workload 私钥签发,AD 域通过跨域 trust list验证签名。
  • AD 域不需要 CD 的 bundle,反之亦然;只需要 OEM root + 已知 intermediates。
  • 单次 deleg 是一次性凭证,不保留状态(如果需要持续调用,需申请 session 级跨域凭证)。

§C.3 Lease scope 收紧(紧急)

mermaid
sequenceDiagram
    autonumber
    participant T as Task Scheduler
    participant SDK as Agent SDK
    participant SB as Sidecar
    participant KMSD as KMSS Daemon
    participant TEE as libkmss

    Note over T,KMSD: 场景: lease 已发放 30min, 此时 task 即将结束

    T->>SDK: notify_task_near_end(task_id=T123, remaining=2min)
    SDK->>SB: lease_request_event(task_id=T123, type=near_end)
    SB->>KMSD: lease_notify(T123, event=near_end)
    KMSD->>TEE: 查 lease T123 当前 scope
    TEE-->>KMSD: scope_current = {can_dump, can_write_speed}
    KMSD->>KMSD: 自动 scope_tighten<br/>保留: can_dump<br/>撤销: can_write_speed
    KMSD->>TEE: 更新 lease 内部 scope 表
    KMSD-->>SB: scope_tightened=true, new_scope_summary
    SB-->>SDK: scope_tightened (含 reason + removed_scopes)
    SDK->>SDK: 本地 lease_cache 更新, 标记 can_write_speed 不可用

    Note over SDK: 后续 Guard 调用:
    SDK->>SB: Guard.Check(action=can_write_speed, lease=...)
    SB->>SB: 校验 lease.scope ⊇ action -> false
    SB-->>SDK: CheckResponse(allow=false, reason="lease_scope_tightened")

关键点

  • scope 收紧是单向:收紧后不能再次扩大,必须等 lease 自然结束重新申请。
  • 收紧由 KMSS 主动发起(基于 task scheduler event),不是 SDK 主动。
  • SDK 通过 lease_tightened 回调收到通知,可以主动放弃执行中的危险操作。

§C.4 紧急 revocation 全网传播

mermaid
sequenceDiagram
    autonumber
    participant OEM as OEM Backend (云端 / TBOX)
    participant SB_AD as Sidecar (AD)
    participant KMSD_AD as KMSS (AD)
    participant SB_CD as Sidecar (CD)
    participant SB_VD as Sidecar (VD)
    participant AG as Agent (任一域)

    Note over OEM: 场景: L1 cert serial=0xCAFEBABE 泄露, 紧急撤销

    OEM->>SB_AD: 触发 CRL push (gRPC stream / bundle update)
    OEM->>SB_CD: 并行触发 CRL push
    OEM->>SB_VD: 并行触发 CRL push

    par 三域并行
        SB_AD->>KMSD_AD: crl_update(serial=0xCAFEBABE, reason=key_compromise)
        KMSD_AD->>KMSD_AD: 写入 local CRL
        SB_CD->>KMSD_CD: crl_update(...)
        KMSD_CD->>KMSD_CD: 写入 local CRL
        SB_VD->>KMSD_VD: crl_update(...)
        KMSD_VD->>KMSD_VD: 写入 local CRL
    end

    Note over SB_AD,SB_VD: T+0 完成本地 CRL 更新
    Note over SB_AD,SB_VD: SLA: 1 秒内 3 域全部收到

    SB_AD->>AG: 通知现有 lease/svid 受影响
    Note right of SB_AD: 通过 lease_event 告知<br/>SDK 收到 scope_tightened 或<br/>直接 revoke (紧急)

    AG->>SDK: 后续 guard_check
    SDK->>SB: Guard.Check(..., svid)
    SB->>SB: 验证 svid 签名 + 查 CRL
    SB->>SB: 命中 revoked_serial -> reject
    SB-->>SDK: CheckResponse(allow=false, reason="svid_revoked")
    SDK-->>AG: 拒绝

关键点

  • revocation 通过两条路径同时传播:CRL delta + bundle 紧急更新
  • 任一收到即可生效,因为 SVID 验证时先看本地 CRL再看签名。
  • 已签发的 session/lease 立即失效(KMSS 收到 revocation token 时主动通知 SDK)。

§C.5 OTA Trust Bundle 更新

mermaid
sequenceDiagram
    autonumber
    participant OEM as OEM OTA Server
    participant SB as Sidecar
    participant FS as /etc/agent/bundles/
    participant KMSD as KMSS Daemon
    participant SDK as Agent SDK

    OEM->>SB: ota_push_bundle(version=vN+1, signed_pb)
    SB->>FS: 写到 .tmp 文件
    SB->>SB: 验证签名: sign(ota_key) == expect
    alt 签名失败
        SB->>FS: 删除 .tmp
        SB->>OEM: OTA reject (签名无效)
        SB->>SDK: bundle_push_failed (日志)
    else 签名通过
        SB->>FS: rename(.tmp, vN+1.pb)
        SB->>FS: 更新 active link (latest -> vN+1)
        SB->>KMSD: bundle_updated(version=vN+1, l1_count=N+1)
        KMSD->>KMSD: 重载 trust root (新增 L1)
        KMSD->>SDK: bundle_updated event (推送 sdk)
        SDK->>SDK: 后续 svid 验证使用新 L1
        SDK->>SDK: 现有 svid 缓存标记 stale, 自然到期后切换
    end

关键点

  • Bundle 文件原子替换rename 系统调用保证),不会有半写入状态。
  • 签名用 ota_key(独立于 L1 链),失效时也不影响 trust anchor。
  • 新旧 L1 共处 30 天 overlap,是 key rotation 的关键期。

§C.6 KMSS daemon failover(demo 用 SoftHSM2)

mermaid
sequenceDiagram
    autonumber
    participant SDK as Agent SDK
    participant SB as Sidecar
    participant KMSD as KMSS Daemon (主)
    participant FS as persistent storage
    participant KMSS_B as KMSS Daemon (systemd restart)

    SDK->>SB: task_token()
    SB->>KMSD: km_issue_task()
    KMSD->>KMSD: 处理中突然 crash
    KMSD--xSB: (无响应 / conn closed)

    SB->>SB: 重试 +1, +2, +3 (50ms 退避)
    SB->>KMSD: 重连尝试
    alt 重连失败
        SB->>SB: 标记 kmss_status=down
        SB->>SDK: 返回 UNAVAILABLE, sdk 进入降级
        SDK->>SDK: 缓存 task_token 失败, 降级到 last-known-good
    end

    Note over KMSS_B: systemd 重启 KMSS (Restart=always)
    KMSS_B->>FS: 读 L1 持久化句柄
    KMSS_B->>FS: 读 L2 workload key (派生)
    KMSS_B->>SB: HEALTH=ready (gRPC)
    SB->>KMSD_B: 重试 task_token
    KMSD_B-->>SB: new task_token
    SB-->>SDK: 恢复 + cached tokens 重新校验

    Note over SB,SDK: 故障恢复时间目标 RTO < 5s (demo)

关键点

  • L1 中间证书私钥必须持久化(encrypted at rest,使用 TEE 内 sealed key 加密)。
  • 重启后 load 即可,无需重新派生(避免 OEM 重新签字)。
  • 短期 task_token 不可恢复(stateful),但 session_jwt 可用,因为只签不存。

§C.7 L1 Key Rotation Overlap 期间验签

mermaid
sequenceDiagram
    autonumber
    participant T as 第三方工具 (另一域或 OEM 后端)
    participant SB_AD as Sidecar (AD)
    participant FS_AD as Bundle (AD)
    participant KMSD_AD as KMSS (AD)

    Note over T,KMSD_AD: 场景: L1 cert serial=A 即将过期, serial=B 已签发, 共存 30 天

    T->>SB_AD: RPC 带 cert_A 签名的请求
    SB_AD->>KMSD_AD: verify_chain(cert_A)
    KMSD_AD->>FS_AD: 查 bundle.root + intermediates
    FS_AD-->>KMSD_AD: [root, cert_A, cert_B]
    KMSD_AD->>KMSD_AD: 尝试用 cert_A 验签
    alt cert_A 在 bundle 内
        KMSD_AD->>KMSD_AD: 验签通过
        KMSD_AD-->>SB_AD: valid=true
    else cert_A 已不在 bundle (过期被清理)
        KMSD_AD-->>SB_AD: valid=false (cert_unknown)
    end

    Note over T,SB_AD: 此时如果是 cert_B 签的请求, 同理通过

    T->>SB_AD: RPC 带 cert_B 签名的请求
    SB_AD->>KMSD_AD: verify_chain(cert_B)
    KMSD_AD-->>SB_AD: valid=true

关键点

  • overlap 期必须两个 cert 都能验签,否则 rotation 期间会出现"部分请求不可用"窗口。
  • Bundle 文件保留所有仍有效的 intermediates(每个带过期时间)。
  • 第 31 天起,旧 cert 自动从 bundle 移除(无需手动操作)。

§D 故障场景演练

§D.1 KMSS 崩溃 + lease 中处理

前置:AD 域 Agent 持 lease L (scope={can_dump, can_write_speed}, TTL=10min),已运行 6min。

故障:T+6min 时 KMSS daemon crash。

预期流程

  1. SDK 收到 Guard.Check 调用,调用 Sidecar。
  2. Sidecar 检测 KMSS 连接超时(默认 500ms)。
  3. Sidecar 重试 3 次(指数退避),仍失败。
  4. Sidecar 返回 UNAVAILABLE: kmss_down 给 SDK。
  5. SDK 缓存 lease 仍存在,不立即撤销,但标记 kmss_unavailable=true
  6. SDK 业务侧收到 UNAVAILABLE,进入降级模式:拒绝所有 mutating call,仅允许缓存可证明是只读的(例如 hash 不变的配置读取)。
  7. KMSS systemd 重启完成(≤ 5s),Sidecar 重连成功。
  8. Sidecar 主动通知 SDK:kmss_recovered=true
  9. SDK 重新校验 lease:通过 L1 签名验证仍有效(TTL 剩余 4min),继续使用。
  10. audit_log 写入 event=kmss_failover, lease=L, downtime_ms=2300

反例(不应发生)

  • SDK 不等待直接撤销 lease,导致任务提前失败。
  • Sidecar 崩溃重启后丢失 KMS connection pool,重新建立耗时长。

§D.2 跨域网络分区(CD-AD 中断 30s)

前置:CD 域 SDK 已成功获得 deleg_jwt 准备调用 AD。

故障:CAN 网段分区,CD 看不到 AD 30s。

预期流程

  1. CD SDK 调用 Sidecar AD 入口,TCP SYN 超时(> 2s)。
  2. Sidecar CD 重试 2 次(gRPC 默认 retry policy),仍失败。
  3. Sidecar 返回 UNAVAILABLE: cross_domain_unreachable
  4. SDK 进入退避:第一次重试 5s 后,第二次 10s 后。
  5. 30s 后网络恢复。
  6. 第 11 次重试成功调用 AD,deleg_jwt 仍有效(TTL=2min,过期前 30s 才过期)。
  7. audit_log 记录 event=cross_domain_partition, downtime_s=30

反例

  • deleg_jwt 持续重试,导致过期后报错(应轮换新 deleg_jwt)。
  • 网络恢复后未刷新 token 缓存,重复使用已撤销的 deleg_jwt。

§D.3 TEE 物理故障

前置:正常运行中 TEE 突然硬件故障(demo 极少,prod 罕见但关键)。

预期流程

  1. KMSS daemon 调用 km_init() 重试机制持续失败。
  2. KMSS 进入 degraded 状态(所有 load/generate 操作返回 TEE_FAULT)。
  3. Sidecar 通过 KMSS health stream 收到 health=tde_fault
  4. Sidecar:
    • 拒绝所有新 task_token 申请。
    • 已签发的 token 全部撤销(因为无法保证 L0/L1 私钥未泄露)。
    • 发送紧急 audit_log: event=tde_fault, action=disable_all_tokens
  5. SOC 收到告警(车机+OEM 后端双通道)。
  6. 整车进入 fail-safe 模式:所有 Agent 调用 LLM 被阻断,只允许预设的安全回复模板。
  7. OTA 推送 TEE 修复(如果可行)或要求 4S 店维修。

反例

  • KMSS 装作正常但 L0 私钥失窃(TEE 不暴露此状态,防 side channel)。
  • 没有 fail-safe,导致车机功能完全不可用(影响驾驶体验)。

§D.4 Trust Bundle 文件损坏

前置:/etc/agent/bundles/ad.pb 正常加载。

故障:OTA 过程中文件半写入或下载损坏。

预期流程

  1. Sidecar 启动时/OTA 时对 bundle 验证签名。
  2. 签名失败 → 不加载该 bundle,保留上一个有效 bundle。
  3. 如果是首次启动(无 fallback),Sidecar 启动失败并 crash-loop。
  4. systemd 触发 Restart=always,每次启动都是 startup_fail。
  5. watchdog 在 5 次失败后触发系统降级(只显示安全灯,开机画面)。
  6. OTA 后台重试:先尝试 OEM 仓库下载,如仍失败则 fallback 到出厂 bundle(只含 L0 root)。

反例

  • 加载损坏 bundle,导致后续 SVID 验证失败但车机仍运行(应立即拒绝加载)。
  • OTA 重试无限循环,应有最大次数(5 次)后停止。

§D.5 时钟漂移(Clock Skew)

前置:3 域时钟轻微漂移(最大 2s)。

预期流程

  1. Guard.Check 收到请求,时间戳在 token TTL 边界上。
  2. KMSS 接受最大 ±30s skew(token_leeway 可配置)。
  3. 例如 token TTL=30s 实际过期时间 30s+30s=60s(容忍)。
  4. 如果时钟漂移 > 30s(异常),返回 TOKEN_EXPIRED
  5. SDK 通过 peer_clock_get() 定期同步(每 5min NTP 校时)。
  6. RTC 硬件故障:通过 GPS 时间兜底(车机通常有 GPS 模块)。

反例

  • 不允许任何 skew,导致正常 token 被误拒。
  • 允许太多 skew,导致撤销的 token 仍可用。

§D.6 OTA Bundle 损坏 + 回滚

前置:OTA 推 vN+1 bundle,签名错误或内容损坏。

预期流程

  1. Sidecar 接收 OTA bundle → 写入 .tmp → 验签失败。
  2. Sidecar 删除 .tmp,保留 vN bundle。
  3. Sidecar 推送 bundle_push_failed 事件到 audit_log。
  4. KMS 保持使用 vN bundle(不需重启)。
  5. OTA 服务器收到失败 ACK,触发自动重试(不超过 3 次)。
  6. 3 次失败后,OEM 进入人工排查模式,4S 升级处理。

反例

  • OTA 强制覆盖 bundle(vN+1 即使损坏也加载)。
  • OTA 重试无限循环(应有 max_retries=3)。

§E demo 部署架构

§E.1 docker-compose 总览

mermaid
graph TB
    subgraph CD[CD 域 (QM)]
        CD_AG[cd-agent<br/>port: grpc 7000]
        CD_SB[cd-sidecar<br/>port: 7001→7000]
        CD_KM[cd-kmss-daemon<br/>port: 7002→7001]
        CD_HSM[cd-softhsm2<br/>PKCS#11 socket]
    end

    subgraph AD[AD 域 (ASIL-D)]
        AD_AG[ad-agent<br/>port: grpc 7000]
        AD_SB[ad-sidecar<br/>port: 7001→7000]
        AD_KM[ad-kmss-daemon<br/>port: 7002→7001]
        AD_HSM[ad-softhsm2]
    end

    subgraph VD[VD 域 (ASIL-D)]
        VD_AG[vd-agent<br/>port: grpc 7000]
        VD_SB[vd-sidecar<br/>port: 7001→7000]
        VD_KM[vd-kmss-daemon<br/>port: 7002→7001]
        VD_HSM[vd-softhsm2]
    end

    subgraph OEM[OEM 模拟 (本地容器)]
        OEM_API[oem-stub<br/>:8080]
        OEM_OTA[oem-ota-server<br/>:8090]
        OEM_OBS[oem-observability<br/>:9090]
    end

    CD_SB <-->|TCP+mTLS<br/>:7000| AD_SB
    AD_SB <-->|TCP+mTLS<br/>:7000| VD_SB
    CD_SB <-->|TCP+mTLS<br/>:7000| VD_SB

    CD_KM <-->|UDS<br/>/var/run/kmss/cd.sock| CD_SB
    AD_KM <-->|UDS| AD_SB
    VD_KM <-->|UDS| VD_SB

    CD_KM <--> CD_HSM
    AD_KM <--> AD_HSM
    VD_KM <--> VD_HSM

    OEM_API -.->|首次设置| CD_KM
    OEM_API -.->|首次设置| AD_KM
    OEM_API -.->|首次设置| VD_KM

    OEM_OTA -.->|bundle push| CD_SB
    OEM_OTA -.->|bundle push| AD_SB
    OEM_OTA -.->|bundle push| VD_SB

    CD_AG --> CD_SB
    AD_AG --> AD_SB
    VD_AG --> VD_SB

§E.2 docker-compose.yml 骨架

yaml
# 关键 fragment, 完整版见 github.com/sphinx/llm-guard-iam-demo
version: "3.9"

networks:
  intra-ad:
    driver: bridge
    internal: true   # 仅允许 ad 域内通信
  intra-cd:
    driver: bridge
    internal: true
  intra-vd:
    driver: bridge
    internal: true
  inter-domain:    # 跨域, 模拟 CAN 网段
    driver: bridge
  oem-net:
    driver: bridge

volumes:
  ad_km_state:
  cd_km_state:
  vd_km_state:
  ad_bundle:
  cd_bundle:
  vd_bundle:

services:
  # AD 域 (ASIL-D)
  ad-softhsm2:
    image: softhsm2:latest
    volumes:
      - ad_km_state:/softhsm-state
    networks: [intra-ad]
    command: softhsm2-util --init-token --free --label ad-tok

  ad-kmss-daemon:
    image: llmguard/kmss:latest
    depends_on: [ad-softhsm2]
    volumes:
      - ad_km_state:/kmss-data
      - ad_bundle:/etc/agent/bundles:ro
    networks: [intra-ad, inter-domain]
    command: kmsd --domain ad --bind /var/run/kmss/ad.sock
    healthcheck:
      test: ["CMD", "kmsctl", "ping"]
      interval: 5s

  ad-sidecar:
    image: llmguard/iam-guard-sidecar:latest
    depends_on:
      ad-kmss-daemon:
        condition: service_healthy
    volumes:
      - ad_bundle:/etc/agent/bundles:ro
    networks: [intra-ad, inter-domain]
    command: sidecar --domain ad --kmss-uds /var/run/kmss/ad.sock --listen 0.0.0.0:7000
    healthcheck:
      test: ["CMD", "iam-ctl", "health"]
      interval: 5s

  ad-agent:
    image: llmguard/agent-sample:latest
    depends_on:
      ad-sidecar:
        condition: service_healthy
    networks: [intra-ad]
    command: agent --sidecar-uds /run/agent-iam/ad.sock --log-level debug
    environment:
      IAM_DOMAIN: ad
      IAM_AGENT_ID: ad-llm-agent

  # CD 和 VD 同构, 略...
  # oem-stub, oem-ota-server, oem-observability 用于初始化 + 监控

§E.3 网络隔离矩阵

Source → Dest协议端口mTLSASIL 要求
Agent → SidecargRPC over UDS/run/agent-iam/*.sockn/a (UDS)同域
Sidecar → KMSSgRPC over UDS/var/run/kmss/*.sockn/a (UDS)同域
Sidecar → Sidecar (跨域)gRPC over TCP+mTLS7000双向双方 bundle
Sidecar → OEM OTAHTTPS8090单向 OEM certdemo 用
Sidecar → OEM StubHTTPS8080双向 (初始化)demo 用

§E.4 状态持久化

mermaid
graph LR
    subgraph Persistent
        A[ad_km_state/<br/>- L1 priv (sealed)<br/>- L2 workload ref<br/>- audit log]
        B[ad_bundle/<br/>- bundle.pb<br/>- crl.pb]
    end

    subgraph Ephemeral
        C[L2 workload priv<br/>in TEE]
        D[L3 session key<br/>per process]
    end

    A -.持久化.-> C
    B -.只读加载.-> C
    D -. 不持久化 .-> [*]
类型位置重启后demo 说明
L0 root 公钥efuse (demo: bundle 内)保留公钥
L1 私钥KMSS 持久化目录 (encrypted)保留SoftHSM2 CKO_PRIVATE_KEY
L2 workload refKMSS 运行时 (TEE)丢失,需重新派生不持久化为安全设计
L3 session key进程内 (TEE)丢失必然 ephemeral
Trust Bundle/etc/agent/bundles/保留 (volume mount)OTA 可更新
CRL/etc/agent/bundles/保留包含在 bundle
audit logKMSS 日志目录保留demo 用 stdout + file

§E.5 端口总览

mermaid
graph TB
    subgraph AD[AD 域]
        AD_AG_PORT[Agent: gRPC-UDS]
        AD_SB_PORT[Sidecar: gRPC-TCP 7000<br/>+ UDS /run/agent-iam/ad.sock]
        AD_KM_PORT[KMSS: gRPC-UDS /var/run/kmss/ad.sock]
    end

    AD_AG_PORT --> AD_SB_PORT
    AD_SB_PORT --> AD_KM_PORT
    AD_SB_PORT -.跨域.-> CD_SB_PORT[CD Sidecar :7000]
    AD_SB_PORT -.跨域.-> VD_SB_PORT[VD Sidecar :7000]

§E.6 mock KMSS 行为说明

demo 中 SoftHSM2 模拟 TEE,不是真正的硬件 TEE。差异:

真 TEESoftHSM2 demo
私钥内存隔离硬件进程隔离 (Token)
防 side channel硬件仅防 API 调用
防 physical attack
持久化sealed storagefile-based encrypted
性能~10ms per sign~1ms per sign

警告:SoftHSM2 仅适合功能 demo接口验证不可用于安全敏感场景。 生产必须用硬件 TEE(高通 SEE、ARM TrustZone 等)。


§F 修订记录

版本日期变更
v0.12026-07-15初稿:补齐启动序列 / 模块状态机 / 关键时序 / 故障演练 / demo 部署
v0.22026-07-16启动序列独立章节(§A),补充并行时间线(§A.3);模块状态机 3 个(§B.1-§B.3)
v0.32026-07-16关键场景 7 个时序图(§C.1-§C.7);故障演练 6 个(§D.1-§D.6);demo 部署(§E.1-§E.6)

关联文档

基于 VitePress 构建