Skip to content

feat(deploy): KMS+DVT 二合一 imx93 部署方案(实测基线 + 生产安全 + 可复制工具)#151

Merged
jhfnetboy merged 6 commits into
mainfrom
feat/kms-dvt-imx93-colocation
Jul 7, 2026
Merged

feat(deploy): KMS+DVT 二合一 imx93 部署方案(实测基线 + 生产安全 + 可复制工具)#151
jhfnetboy merged 6 commits into
mainfrom
feat/kms-dvt-imx93-colocation

Conversation

@jhfnetboy

Copy link
Copy Markdown
Member

概述

单块 NXP FRDM-IMX93(2×A55/2GB)同跑 KMS(AirAccount)+ DVT(aNode v1.9.0),为小社区提供 TEE 私钥 + BLS 二签。纯新增 kms/deploy/ 工具 + kms/docs/ 文档,不改 KMS 运行时代码(低风险)。全程实机验证(Cooperation-Center CC-22 三方对齐)。

实测结论

  • 性能:暖签名 server-side p50 1–2ms,吞吐 ~80 sign/s(单 TA worker 串行),20 并发 p99 <40ms,零内存泄漏。KMS RSS 7.5MB / DVT ~110MB / 共存 available 1.45G → 2GB 服务 20 并发绰绰有余,DVT 不必为内存 Rust 重写
  • BLS 真能签:节点独立 BLS12-381 密钥签 userOpHash(DST=_POP_)+ 服务 /signature/verify valid=true;全链路 owner-gated /signature/sign → HTTP 201 + BLS 签名(账户 0x92EA8b02 + ownerAuth 0x01‖personal_sign)。

部署 = bare-node(不用 Docker)

imx93 Docker 三处残缺(legacy builder ext4 xattr / 无 buildx / 内核无 iptables raw)→ 官方 glibc arm64 Node20 + 源码 npm ci && npm run build + systemd。DVT 已认可为受限板官方姿势(PR #176)。

生产安全(实测验证含 fail-closed)

  • BLS 私钥 EIP-2335 加密 keystore(pbkdf2,A55 建议);NODE_KEY_PASSPHRASE 手动输入 → 只进 tmpfs,绝不落盘
  • 崩溃 systemd 从 tmpfs 自启;断电 dvt-unlock.sh 人工重输;清掉密码 → 服务起不来(硬盘离线拿不到私钥)
  • systemd 加固 + 只绑 127.0.0.1;合约地址测/生产双组 config 切换;KMS rpId 走 env 运行期可配。

交付物

文件 作用
kms/deploy/deploy-dvt.sh 安全部署脚本(node/build/keystore/密码/加固/注册)
kms/deploy/dvt-unlock.sh 断电重启恢复(输密码 → tmpfs → 启动)
kms/deploy/community.toml.example KMS+DVT 共享配置(社区复制)
kms/docs/kms-dvt-imx93-baseline.md 性能基线
kms/docs/kms-dvt-imx93-deploy.md 部署 + BLS 验证 runbook
kms/docs/kms-dvt-production-init.md 生产初始化 + 社区部署 + 灾备

待生产板

  • 完整链上 validate=0 E2E:POST /node/register 注册 → realnode-e2e(合约地址走 config)。
  • 主网 e2e_account/validator 待 SP/contract 主网部署后回填 contracts_mainnet

跨仓协调见 Cooperation-Center CC-22(repo:dvt PR #176 + repo:airaccount-contract 交付 e2e_account,均已确认)。

https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5

jhfnetboy added 4 commits July 6, 2026 18:02
单板(NXP FRDM-IMX93, 2×A55/2GB)同跑 KMS(AirAccount v0.27.4) +
DVT(aNode v1.9.0), 实测跑通:

- kms/docs/kms-dvt-imx93-baseline.md: 性能基线(暖签名 p50 1-2ms,
  吞吐 ~80 sign/s, 20 并发 p99<40ms, KMS RSS 7.5MB, 共存 available 1.45G)
- kms/deploy/deploy-dvt.sh: bare-node 部署脚本(板子 Docker 三处残缺 →
  官方 glibc Node20 + 源码 npm build + systemd, node_state 独立密钥,
  --exclude 仓库 committed 的测试 fixture 键)
- kms/deploy/community.toml.example: KMS+DVT 共享配置(社区复制)
- kms/docs/kms-dvt-imx93-deploy.md: 部署 runbook + BLS 验证 + 跨仓阻塞

BLS 实测: 节点独立密钥签 userOpHash(DST=_POP_) + 服务 /signature/verify
valid=true. 全链路 owner-gated sign 卡在跨仓账户接口漂移(DVT 调 0xa0cf00cf,
测试账户只有标准 ERC-1271), 已在 Cooperation-Center @DVT @contract.

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
实测验证的安全模型(imx93 板上跑通):
- BLS 私钥 EIP-2335 加密 keystore(scrypt), 盘上只密文
- NODE_KEY_PASSPHRASE 手动输入 → 只进 tmpfs(/run/dvt/pass, 内存, 绝不落盘)
- 崩溃: systemd Restart 从 tmpfs 自动重启; 断电: dvt-unlock.sh 人工重输
- 实测 fail-closed: 清掉 tmpfs 密码 → 服务起不来(硬盘离线拿不到私钥)
- systemd 加固: ProtectSystem=strict/NoNewPrivileges/PrivateTmp/RestrictAddressFamilies
- 禁开机自启(断电须人工 unlock), 只绑 127.0.0.1

配置化:
- community.toml 合约测试/生产双组 + contracts.active 一键切换
- KMS rpId 走 KMS_RP_ID/KMS_ORIGIN env(运行期, 不重编)
- 秘密全不入库([secrets] 段仅说明, 手动 provision)

链上注册用 DVT 现成 POST /node/register(不新增功能), E2E 前置。

新增: kms/deploy/dvt-unlock.sh(断电重启恢复工具)

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
CC-22 三方确认后定稿:
- validator (testnet) = 0x539B9681aFd5BFbCaa655Fe4c6BdcFe1fa7864bC (repo:dvt)
- e2e_account = 0x92EA8b02D34A4D5d10f0Db9Ea894e8bC72e292e8 (repo:airaccount-contract,
  AAStarAirAccountV7 v0.27.0, owner=dvt1 operator key)
- KDF scrypt → pbkdf2 (DVT 建议, A55 上 scrypt 偏重)
- ownerAuth 格式注释: k1 owner = 0x01 ‖ personal_sign(userOpHash)

实机验证: 全链路 owner-gated /signature/sign → HTTP 201 + BLS 签名(之前 403 是
ownerAuth 格式用错 + 账户过时, 非接口漂移)。跨仓阻塞闭合。

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
- 新板工作流:你(刷机/联网/输密码) vs 我(远程编译/部署/配置/测试)
- 社区初始化:弹性可配字段表(AAstar 默认 vs 他人覆盖)+ 三步部署
- 秘密管理:清单/provision/轮换
- 灾备:DVT node 身份可迁移(keystore+密码异地备份);KMS TEE 身份不可迁移
  (换板=换TEE,用户资产靠合约层 social recovery)
- NTP 时间同步(TA challenge/attestation TTL 依赖)
- 监控告警 + 生产验收清单

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Security Audit Report

Date: Mon Jul 6 15:09:30 UTC 2026
Commit: c1ecd5a

Cargo Audit Results

�[1m�[33mwarning�[0m: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   /home/runner/work/AirAccount/AirAccount/kms/host/Cargo.toml
workspace: /home/runner/work/AirAccount/AirAccount/Cargo.toml
�[1m�[92m    Updating�[0m crates.io index
�[1m�[92m     Locking�[0m 1 package to latest compatible version
�[1m�[92m    Updating�[0m kms v0.27.2 (/home/runner/work/AirAccount/AirAccount/kms/host) -> v0.27.4
�[1m�[92mnote�[0m: pass `--verbose` to see 40 unchanged dependencies behind latest
�[0m�[0m�[1m�[32m    Fetching�[0m advisory database from `https://github.com/RustSec/advisory-db.git`
�[0m�[0m�[1m�[32m      Loaded�[0m 1157 security advisories (from /home/runner/.cargo/advisory-db)
�[0m�[0m�[1m�[32m    Updating�[0m crates.io index
�[0m�[0m�[1m�[31merror:�[0m not found: Couldn't load Cargo.lock
Caused by:
  -

... [查看完整报告](artifacts)

非技术社区买板 → 只填配置就跑起 KMS+DVT(DVT 门限 ≥3 节点)。
核心:编译是唯一壁垒 → AAStar 预构建发产物,社区只配置。

定案(2026-07-07):
- MVP = 预刷板 + SSH 跑一次 aastar-node-setup TUI 向导
- 交付 = AAStar 预刷板邮寄
- 注册 = SuperPaymaster gasless registerWithProof (SDK v0.38.0)
- 子域名 <community>.aastar.io 仅 web UI;rpId=社区自己域名(独立)
- Web UI 向导 = 未来(同后端,浏览器填表 + 状态仪表盘)
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Security Audit Report

Date: Mon Jul 6 18:15:44 UTC 2026
Commit: 1e5d622

Cargo Audit Results

�[1m�[33mwarning�[0m: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   /home/runner/work/AirAccount/AirAccount/kms/host/Cargo.toml
workspace: /home/runner/work/AirAccount/AirAccount/Cargo.toml
�[1m�[92m     Locking�[0m 1 package to latest compatible version
�[1m�[92m    Updating�[0m kms v0.27.2 (/home/runner/work/AirAccount/AirAccount/kms/host) -> v0.27.4
�[1m�[92mnote�[0m: pass `--verbose` to see 40 unchanged dependencies behind latest
�[0m�[0m�[1m�[32m    Fetching�[0m advisory database from `https://github.com/RustSec/advisory-db.git`
�[0m�[0m�[1m�[32m      Loaded�[0m 1157 security advisories (from /home/runner/.cargo/advisory-db)
�[0m�[0m�[1m�[32m    Updating�[0m crates.io index
�[0m�[0m�[1m�[31merror:�[0m not found: Couldn't load Cargo.lock
Caused by:
  -> I/O operation failed: I/O operation fail

... [查看完整报告](artifacts)

@clestons clestons left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PK Review — AirAccount#151 (KMS+DVT imx93 部署工具)

Verdict: REQUEST CHANGES — 2 medium security findings in the deployment scripts. The overall security architecture (EIP-2335 encrypted keystore, tmpfs-only passphrase, systemd hardening, disabled autostart) is well-designed. The two issues below should be addressed before running this against a production security board.


[Confirmed] Medium — BLS private key written to plaintext flash before encryption

File: kms/deploy/deploy-dvt.sh:574–582

The inline Node.js generates node_state.json with the raw privateKey field in clear JSON, writing it to the board's flash storage:

writeFileSync("node_state.json", JSON.stringify({
  ..., privateKey: "0x" + Buffer.from(sk).toString("hex"), ...
}))

A subsequent step calls encrypt-node-key.mjs to produce the EIP-2335 keystore and shreds only node_state.json.bak. But the ORIGINAL plaintext node_state.json was already written to flash. On NOR/NAND flash with wear leveling, shred of the backup file does NOT guarantee erasure of the sector holding the original plaintext — the controller may have already remapped that sector.

Fix: Never write the plaintext key to persistent storage. Generate and encrypt in a single in-memory step, or generate the EIP-2335 keystore directly using DVT's scripts/gen-node-state.mjs (if it supports in-memory generation), writing only the ciphertext to disk. Alternatively, generate on tmpfs (/run) and move the ciphertext to the DVT dir.


[Confirmed] Medium — Node.js binary installed with no checksum verification

File: kms/deploy/deploy-dvt.sh:136

$SSH "curl -fsSL -o /tmp/node.tar.xz https://nodejs.org/dist/$NODE_VER/node-$NODE_VER-linux-arm64.tar.xz \
  && tar xJf /tmp/node.tar.xz -C $NODE_DIR --strip-components=1"

The downloaded tarball is extracted directly with no SHA-256/SHA-512 verification against the official Node.js SHASUMS256.txt. On a production KMS+DVT security board, a compromised CDN, DNS hijack, or opportunistic MITM could substitute a malicious node binary. The binary then runs npm ci, BLS key generation, and the DVT server itself.

Fix: Verify the download before extracting:

curl -fsSL -o /tmp/node.tar.xz "https://nodejs.org/dist/$NODE_VER/node-$NODE_VER-linux-arm64.tar.xz"
curl -fsSL -o /tmp/node.shasums "https://nodejs.org/dist/$NODE_VER/SHASUMS256.txt"
# Verify on operator machine before SCP:
grep "node-$NODE_VER-linux-arm64.tar.xz" /tmp/node.shasums | sha256sum -c -

Or pin the expected SHA in community.toml under [runtime] and verify on the board.


[Confirmed] Low — dvt-unlock.sh hardcodes port 8080

File: kms/deploy/dvt-unlock.sh:689

if curl -sf -m5 http://127.0.0.1:8080/health >/dev/null 2>&1; then

The port is configurable in community.toml ([dvt].port) and written to $DVT_DIR/dvt.env. If a community uses a non-default port, the unlock script will report failure even when the service started successfully — misleading an operator into thinking unlock failed when it succeeded.

Fix: Read the port from the co-located dvt.env:

DVT_DIR="${DVT_DIR:-/opt/dvt-build}"
PORT=$(grep '^PORT=' "$DVT_DIR/dvt.env" 2>/dev/null | cut -d= -f2)
PORT="${PORT:-8080}"
if curl -sf -m5 "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then

PK Summary

Finding PK Verdict
F1 — Node binary no checksum Confirmed (independent diff read)
F2 — BLS plaintext flash write [CONFIRM] Codex
F3 — dvt-unlock.sh hardcoded port Confirmed (independent diff read)

Codex was unable to fetch the diff for F1/F3 due to network access; both were independently verified directly from the PR diff.

Self-assessment: The security model (tmpfs passphrase, encrypted keystore, disabled autostart, systemd hardening) represents solid threat modeling for an embedded KMS node. The two medium findings are pre-deployment blockers for a board that handles real BLS signing keys.

1. BLS 明文私钥只在 tmpfs(RAM)生成+加密,绝不落 flash
   —— flash(eMMC/SD)wear-leveling 使 shred/覆盖前的明文扇区残留,芯片级可恢复。
   改为 /run/dvt/gen(tmpfs)生成明文 → 加密 → 只 cp 密文到 flash → 抹 tmpfs。实测过。
2. Node.js 下载校验 sha256(默认版本 pin 死 hash,MITM 改不了本脚本;非默认回退官方
   SHASUMS + 告警)—— 否则被投毒的 runtime 能偷 BLS 私钥。
3. dvt-unlock.sh 从 dvt.env 读 PORT,不硬编码 8080(与 community.toml 配置一致)。

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5

@clestons clestons left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PK Review (incremental) — AirAccount#151 v2

Verdict: APPROVE — All 3 findings from the previous review are addressed. No new issues found in the incremental diff.

Fixed findings

  • [Fixed] Medium — Node.js binary checksum: SHA-256 now verified on the board before extraction (sha256sum -c). v20.20.2 hash is pinned in the script; non-default versions fall back to the official SHASUMS endpoint with an explicit warning. MITM injection path closed. ✓

  • [Fixed] Medium — BLS plaintext flash write: Key generation now runs in /run/dvt/gen/ (tmpfs, RAM-only). Only the EIP-2335-encrypted ciphertext is cp'd to the DVT dir on flash. Plaintext and .bak are shred -u'd from tmpfs. Flash never sees the cleartext private key. ✓

  • [Fixed] Low — dvt-unlock.sh hardcoded port: Port now read from dvt.env (grep -E '^PORT='), with 8080 as default. ✓

New additions — no issues

kms/docs/community-node-kit-design.md and kms-dvt-production-init.md are design/runbook docs. Security model described (tmpfs password, BLS key never leaves board, TEE non-migratable warning) is accurate and aligns with the implementation.

One observation for the runbook (kms-dvt-production-init.md:554): ETH_PRIVATE_KEY listed as going into dvt.env (persistent on flash, 600). For an operator key used only for chain registration, this is acceptable practice, but operators should be aware it persists on disk (unlike the keystore passphrase which is tmpfs-only).

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Security Audit Report

Date: Tue Jul 7 04:31:49 UTC 2026
Commit: 99d5293

Cargo Audit Results

�[1m�[33mwarning�[0m: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   /home/runner/work/AirAccount/AirAccount/kms/host/Cargo.toml
workspace: /home/runner/work/AirAccount/AirAccount/Cargo.toml
�[1m�[92m     Locking�[0m 1 package to latest compatible version
�[1m�[92m    Updating�[0m kms v0.27.2 (/home/runner/work/AirAccount/AirAccount/kms/host) -> v0.27.4
�[1m�[92mnote�[0m: pass `--verbose` to see 41 unchanged dependencies behind latest
�[0m�[0m�[1m�[32m    Fetching�[0m advisory database from `https://github.com/RustSec/advisory-db.git`
�[0m�[0m�[1m�[32m      Loaded�[0m 1158 security advisories (from /home/runner/.cargo/advisory-db)
�[0m�[0m�[1m�[32m    Updating�[0m crates.io index
�[0m�[0m�[1m�[31merror:�[0m not found: Couldn't load Cargo.lock
Caused by:
  -> I/O operation failed: I/O operation fail

... [查看完整报告](artifacts)

@jhfnetboy jhfnetboy merged commit f3ae929 into main Jul 7, 2026
6 checks passed
@jhfnetboy jhfnetboy deleted the feat/kms-dvt-imx93-colocation branch July 7, 2026 12:44
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants