feat(deploy): KMS+DVT 二合一 imx93 部署方案(实测基线 + 生产安全 + 可复制工具)#151
Conversation
单板(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
Security Audit ReportDate: Mon Jul 6 15:09:30 UTC 2026 Cargo Audit Results |
非技术社区买板 → 只填配置就跑起 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 向导 = 未来(同后端,浏览器填表 + 状态仪表盘)
Security Audit ReportDate: Mon Jul 6 18:15:44 UTC 2026 Cargo Audit Results |
clestons
left a comment
There was a problem hiding this comment.
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; thenThe 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; thenPK 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
left a comment
There was a problem hiding this comment.
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 iscp'd to the DVT dir on flash. Plaintext and.bakareshred -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).
Security Audit ReportDate: Tue Jul 7 04:31:49 UTC 2026 Cargo Audit Results |
概述
单块 NXP FRDM-IMX93(2×A55/2GB)同跑 KMS(AirAccount)+ DVT(aNode v1.9.0),为小社区提供 TEE 私钥 + BLS 二签。纯新增
kms/deploy/工具 +kms/docs/文档,不改 KMS 运行时代码(低风险)。全程实机验证(Cooperation-Center CC-22 三方对齐)。实测结论
_POP_)+ 服务/signature/verifyvalid=true;全链路 owner-gated/signature/sign→ HTTP 201 + BLS 签名(账户0x92EA8b02+ ownerAuth0x01‖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)
NODE_KEY_PASSPHRASE手动输入 → 只进 tmpfs,绝不落盘。dvt-unlock.sh人工重输;清掉密码 → 服务起不来(硬盘离线拿不到私钥)。交付物
kms/deploy/deploy-dvt.shkms/deploy/dvt-unlock.shkms/deploy/community.toml.examplekms/docs/kms-dvt-imx93-baseline.mdkms/docs/kms-dvt-imx93-deploy.mdkms/docs/kms-dvt-production-init.md待生产板
validate=0E2E:POST /node/register注册 → realnode-e2e(合约地址走 config)。contracts_mainnet。跨仓协调见 Cooperation-Center CC-22(repo:dvt PR #176 + repo:airaccount-contract 交付 e2e_account,均已确认)。
https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5