secubox-deb/docs/wiki/Eye-Remote-Bootstrap-ZH.md
CyberMind-FR 27e60770cd docs(eye-remote): add FR and ZH translations for Bootstrap wiki
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-23 11:47:11 +02:00

26 KiB
Raw Permalink Blame History

Eye Remote Bootstrap 启动媒体管理

版本: 2.1.0 最后更新: 2026-04-23 状态: 生产环境 作者: CyberMind — Gerald Kerma


概述

Eye Remote Bootstrap 系统扩展了 Pi Zero W USB OTG 小工具,为 ESPRESSObin 板提供托管启动媒体通道。通过单个 USB OTG 电缆Eye Remote 同时提供:

  1. 指标传输 (ECM) — 10.55.0.0/30 上的 USB 以太网网络
  2. 串行控制台 (ACM) — /dev/ttyACM0主机/ /dev/ttyGS0小工具上的调试控制台
  3. 启动媒体 (Mass Storage) — 提供内核、DTB、initrd 和 rootfs 镜像的 USB LUN

这使得恢复工作流程无需物理干预:从 Eye Remote Web 仪表板刷写新内核,在目标板上测试它,然后使用原子交换语义将其提升到活动槽。

架构图

┌─────────────────────────────────────────────────────────────────────┐
│                        Eye Remote Pi Zero W                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────┐    ┌──────────────────┐    ┌───────────────┐  │
│  │ FastAPI Router  │───▶│ core/boot_media  │───▶│ gadget-setup  │  │
│  │ /boot-media/*   │    │ (Python)         │    │ (Bash)        │  │
│  └────────┬────────┘    └────────┬─────────┘    └───────┬───────┘  │
│           │                      │                      │          │
│           ▼                      ▼                      ▼          │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │              /var/lib/secubox/eye-remote/boot-media/        │   │
│  │  ┌─────────┐  ┌─────────┐  ┌──────────────────────────────┐  │   │
│  │  │ active  │  │ shadow  │  │ images/<sha256>.img          │  │   │
│  │  │ (link)  │  │ (link)  │  │ images/<sha256>.img.tmp (UP) │  │   │
│  │  └────┬────┘  └────┬────┘  └──────────────────────────────┘  │   │
│  │       │            │                                         │   │
│  │       ▼            ▼                                         │   │
│  │  ┌─────────────────────────────┐    ┌────────────────────┐  │   │
│  │  │ LUN 0 (mass_storage.usb0)   │    │ tftp/ (symlinks)   │  │   │
│  │  │ points to active slot       │    │ serves shadow slot │  │   │
│  │  └─────────────────────────────┘    └────────────────────┘  │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                    libcomposite configfs                       │ │
│  │  ┌──────────┐  ┌──────────┐  ┌────────────────────┐           │ │
│  │  │ ecm.usb0 │  │ acm.usb0 │  │ mass_storage.usb0  │           │ │
│  │  │ 10.55.0.2│  │ ttyGS0   │  │ LUN 0 (removable)  │           │ │
│  │  └──────────┘  └──────────┘  └────────────────────┘           │ │
│  └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              │ USB OTG cable
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                     ESPRESSObin U-Boot                              │
│  Option 1: usb start → fatload usb 0 Image                          │
│  Option 2: dhcp → tftpboot $kernel_addr_r Image (shadow channel)    │
└─────────────────────────────────────────────────────────────────────┘

启动媒体目录结构

Eye Remote 维护双缓冲 4R 存储布局:

/var/lib/secubox/eye-remote/boot-media/
├── state.json                    ← 启动媒体元数据和状态
├── active                        ← 符号链接 → images/<sha256>.img
├── shadow                        ← 符号链接 → images/<sha256>.img或 NULL
├── images/
│   ├── a1b2c3d4e5f6.img         ← FAT32 或 ext4 镜像(只读,去重)
│   ├── f0e1d2c3b4a5.img.tmp     ← 上传进行中(临时)
│   ├── rollback-r1/             ← 先前活动4R #1
│   │   └── a1b2c3d4e5f6.img
│   ├── rollback-r2/             ← 先前活动4R #2
│   ├── rollback-r3/             ← 先前活动4R #3
│   └── rollback-r4/             ← 先前活动4R #4
└── tftp/                        ← TFTP 服务根目录(指向 shadow 的符号链接)
    ├── Image → ../images/f0e1d2c3b4a5.img
    ├── device-tree.dtb
    └── initrd.img

状态机

初始状态:空(无 active无 shadow
                    │
                    ▼
    ┌─────────────────────────────────┐
    │   UPLOAD SHADOW                 │
    │ (通过 /api/v1/eye-remote/       │
    │  boot-media/upload)             │
    └────────────┬────────────────────┘
                 │
                 ▼
    ┌─────────────────────────────────┐
    │   SHADOW READY                  │
    │ (镜像有效,可提取)               │
    │                                 │
    │ [分支 A] 通过 TFTP 测试 ──┐    │
    │            (可选)          │    │
    │                             ▼   │
    │                      测试中...  │
    │                             │   │
    │ [分支 B] ◄───────────────┘     │
    │ 将 Shadow 提升为 Active         │
    │ (通过 /api/v1/eye-remote/       │
    │  boot-media/swap)               │
    └────────────┬────────────────────┘
                 │
                 ▼
    ┌─────────────────────────────────┐
    │   ACTIVE ONLY                   │
    │ (shadow 已清除active 已设置)  │
    │ (LUN 已弹出并重新附加)          │
    └────────────┬────────────────────┘
                 │
    [可选]       │ 上传新 shadow
                 ▼
    ┌─────────────────────────────────┐
    │   READY TO SWAP                 │
    │ (active + shadow 均已设置)      │
    │ 可以测试 shadow 或回滚          │
    └────────────┬────────────────────┘
                 │
    ┌────────────┴───────────┐
    │                        │
    │ Swap提升 shadow    │  Rollback恢复 R1
    │                        │
    └────────────┬───────────┘
                 │
                 ▼
    ┌─────────────────────────────────┐
    │   SWAPPED                       │
    │ (shadow → active, active → R1)  │
    └─────────────────────────────────┘

功能特性

1. USB Mass Storage LUN

  • 功能: 通过 libcomposite configfs 的 mass_storage.usb0
  • LUN 0: 指向 active 启动槽
  • 可移动媒体: 是(允许弹出而无需卸载)
  • 大小: 16 MiB4 GiB文件系统无关
  • 支持格式: FAT16、FAT32、ext2、ext3、ext4
  • 访问: 读+写(刷写 U-Boot 环境、日志等)

2. 双缓冲与 4R 回滚

Eye Remote 维护 4 个回滚快照4R

  • Active: 当前通过 USB LUN 向 ESPRESSObin 提供服务
  • Shadow: 待验证(已上传但未提升)
  • R1R4: 先前的活动状态,可用于回滚

每次状态更改(交换、回滚)都会原子链接并记录。

3. TFTP Shadow 通道

与 USB LUN 并行Eye Remote 在 10.55.0.2 端口 69 上运行 dnsmasq TFTP

  • 根目录: /var/lib/secubox/eye-remote/boot-media/tftp/
  • 内容: 指向 shadow 槽的符号链接(Imagedevice-tree.dtbinitrd.img
  • 使用场景: 测试新内核而不交换活动槽
  • 启动命令ESPRESSObin U-Boot:
    => setenv serverip 10.55.0.2
    => setenv ipaddr 10.55.0.1
    => tftpboot $kernel_addr_r Image
    => booti $kernel_addr_r - $fdt_addr_r
    

4. 防崩溃原子交换

提升 shadow 到 active 时:

  1. 从小工具弹出 LUN(强制断开连接)
  2. 原子交换符号链接(重命名,而不是取消链接然后链接)
  3. 更新元数据state.json
  4. 重新附加 LUN 到小工具
  5. 验证 LUN 文件与预期路径匹配

所有操作都受 文件锁 + 进程锁PARAMETERS 模块样式)保护。

5. API 管理

基本路径: /api/v1/eye-remote/boot-media/

所有端点都需要 JWT 身份验证POST 需要 boot:write 范围GET 需要 boot:read 范围。


API 端点

方法 路径 身份验证 描述
GET /state boot:read 获取当前启动媒体状态(槽、元数据)
POST /upload boot:write 流式传输镜像到 shadow 槽(分块 multipart
POST /swap boot:write 将 shadow 提升为 activeactive 轮换到 R1
POST /rollback boot:write 从 R1R4 恢复先前的 active
GET /tftp/status boot:read TFTP 服务状态和 shadow 内容
GET /images boot:read 列出可用镜像及元数据

端点详细规范

GET /api/v1/eye-remote/boot-media/state

请求:

curl -H "Authorization: Bearer $JWT" \
     http://10.55.0.1:8000/api/v1/eye-remote/boot-media/state

响应 (200 OK):

{
  "active": {
    "path": "images/a1b2c3d4e5f6.img",
    "sha256": "a1b2c3d4e5f6...",
    "size_bytes": 268435456,
    "created_at": "2026-04-23T10:30:00Z",
    "label": "debian-bookworm-arm64-espressobin"
  },
  "shadow": {
    "path": "images/f0e1d2c3b4a5.img",
    "sha256": "f0e1d2c3b4a5...",
    "size_bytes": 268435456,
    "created_at": "2026-04-23T11:45:00Z",
    "label": "debian-bookworm-arm64-espressobin-rc1"
  },
  "lun_attached": true,
  "last_swap_at": "2026-04-23T10:00:00Z",
  "tftp_armed": true,
  "rollback_available": ["r1", "r2", "r3"]
}

POST /api/v1/eye-remote/boot-media/upload

请求 (multipart/form-data):

curl -X POST \
  -H "Authorization: Bearer $JWT" \
  -F "image=@debian-bookworm-arm64.img" \
  -F "label=debian-bookworm-arm64-espressobin-rc1" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/upload

参数:

  • image文件必需启动镜像FAT32/ext4
  • label(字符串,可选):人类可读标签

处理:

  1. 流式传输到带 .tmp 后缀的临时文件
  2. 在流式传输期间计算 SHA256
  3. 验证文件系统魔数和大小16 MiB4 GiB
  4. 提取启动文件到 tftp/如果可提取Image、dtb、initrd
  5. 原子重命名为 images/<sha256>.img
  6. 更新 shadow 符号链接

响应 (201 Created):

{
  "path": "images/f0e1d2c3b4a5.img",
  "sha256": "f0e1d2c3b4a5...",
  "size_bytes": 268435456,
  "created_at": "2026-04-23T11:45:00Z",
  "label": "debian-bookworm-arm64-espressobin-rc1",
  "tftp_ready": true
}

响应 (400 Bad Request) — 镜像无效:

{
  "error": "Invalid filesystem",
  "detail": "Image size must be 16 MiB4 GiB"
}

POST /api/v1/eye-remote/boot-media/swap

请求:

curl -X POST \
  -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/swap

可选参数:

  • verify=true(默认):验证 LUN 重新附加成功

处理:

  1. 检查 shadow 存在且有效
  2. 从小工具弹出 LUN
  3. 交换符号链接:activeshadowr1 ← 旧 active
  4. 移动回滚链:r2r1r3r2r4r3
  5. 清除 shadow 槽
  6. 重新附加 LUN
  7. 更新 state.json

响应 (200 OK):

{
  "success": true,
  "message": "Boot slot swapped successfully",
  "active": {
    "path": "images/f0e1d2c3b4a5.img",
    "sha256": "f0e1d2c3b4a5...",
    "size_bytes": 268435456,
    "created_at": "2026-04-23T11:45:00Z"
  },
  "rollback_available": ["r1", "r2", "r3", "r4"]
}

响应 (409 Conflict) — Shadow 未准备好:

{
  "error": "No shadow to swap",
  "detail": "Upload an image to shadow before promoting"
}

POST /api/v1/eye-remote/boot-media/rollback

请求:

curl -X POST \
  -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/rollback?target=r1

参数:

  • target(字符串):要恢复的回滚槽(r1r2r3r4

处理:

  1. 检查目标存在
  2. 弹出 LUN
  3. 将目标提升为 active轮换链
  4. 重新附加 LUN

响应 (200 OK):

{
  "success": true,
  "message": "Restored from r1",
  "active": {
    "path": "images/a1b2c3d4e5f6.img",
    "sha256": "a1b2c3d4e5f6...",
    "size_bytes": 268435456,
    "created_at": "2026-04-23T10:30:00Z"
  }
}

GET /api/v1/eye-remote/boot-media/tftp/status

请求:

curl -H "Authorization: Bearer $JWT" \
     http://10.55.0.1:8000/api/v1/eye-remote/boot-media/tftp/status

响应 (200 OK):

{
  "enabled": true,
  "dnsmasq_running": true,
  "port": 69,
  "root": "/var/lib/secubox/eye-remote/boot-media/tftp",
  "shadow": {
    "path": "images/f0e1d2c3b4a5.img",
    "label": "debian-bookworm-arm64-espressobin-rc1"
  },
  "files": [
    {
      "name": "Image",
      "size": 12582912,
      "type": "kernel"
    },
    {
      "name": "device-tree.dtb",
      "size": 65536,
      "type": "devicetree"
    },
    {
      "name": "initrd.img",
      "size": 8388608,
      "type": "initramfs"
    }
  ]
}

工作流程示例

工作流程 1上传新镜像

#!/bin/bash

# 1. 生成 JWT token以 boot:write 用户身份登录)
JWT=$(curl -s -X POST http://10.55.0.1:8000/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"username":"boot-admin","password":"secubox-bootstrap"}' | jq -r .access_token)

# 2. 上传新镜像到 shadow
curl -X POST \
  -H "Authorization: Bearer $JWT" \
  -F "image=@debian-bookworm-arm64-espressobin-rc1.img" \
  -F "label=RC1 Build $(date +%Y%m%d)" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/upload

# 3. 检查当前状态
curl -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/state | jq .

# 输出:
# {
#   "active": { ... 旧镜像 ... },
#   "shadow": { ... 刚上传的新镜像 ... },
#   "tftp_armed": true,
#   ...
# }

工作流程 2通过 TFTP 测试(可选)

无需 API 调用shadow 立即通过 TFTP 可用。

# 在 ESPRESSObin U-Boot 控制台上:
=> setenv serverip 10.55.0.2
=> setenv ipaddr 10.55.0.1
=> tftpboot $kernel_addr_r Image
=> booti $kernel_addr_r - $fdt_addr_r

# 启动日志通过 Eye Remote 显示在串行控制台上

如果测试内核崩溃或失败只需重新启动U-Boot 将从 USB LUN 加载 active 槽(未更改)。

工作流程 3将 Shadow 提升为 Active

一旦 shadow 经过测试且稳定:

# 1. 获取 JWT已从上传获得
JWT=$(...)

# 2. 将 shadow 提升为 active
curl -X POST \
  -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/swap

# 响应显示 active 现在包含 RC1 镜像,
# 旧 active 保存在 r1 中shadow 已清除。

# 3. 重启 ESPRESSObin或电源循环
# U-Boot 现在将从 LUN 加载新内核

工作流程 4从 LUN 启动

在 ESPRESSObin U-Boot 控制台上:

=> usb start
=> usb tree

# 输出:
# USB device tree:
#   1  Hub (480 Mb/s, 0mA)
#   |  ├─ 1.1 Mass Storage (active boot media)
#   └─ ...

=> fatload usb 0 $kernel_addr_r Image
=> fatload usb 0 $fdt_addr_r device-tree.dtb
=> fatload usb 0 $initrd_addr_r initrd.img
=> booti $kernel_addr_r $initrd_addr_r:$initrd_size $fdt_addr_r

工作流程 5回滚到先前版本

如果活动镜像损坏或不稳定:

# 1. 检查可用的回滚点
curl -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/state | \
  jq .rollback_available

# 输出: ["r1", "r2", "r3", "r4"]

# 2. 回滚到 r1最近的先前版本
curl -X POST \
  -H "Authorization: Bearer $JWT" \
  http://10.55.0.1:8000/api/v1/eye-remote/boot-media/rollback?target=r1

# 响应确认 active 现已从 r1 恢复
# r1 内容移至 r2r2→r3r3→r4r4 清除

# 3. 重启 ESPRESSObin — 再次启动旧内核

镜像要求

格式

  • 支持: FAT16、FAT32、ext2、ext3、ext4
  • 推荐: FAT32最大 U-Boot 兼容性)

大小

  • 最小: 16 MiB允许内核 + DTB + initrd 的空间)
  • 最大: 4 GiBUSB 大容量存储实际限制)
  • 典型: 256 MiB1 GiB

内容

必需(用于 USB LUN 启动):

  • 内核镜像arm64 为 Imagearm32 为 zImage
  • 设备树二进制文件(device-tree.dtb<board>.dtb

可选:

  • 初始 ramdiskinitrd.img
  • U-Boot 环境变量
  • 启动脚本

FAT32 结构示例:

/Image                   ← 内核(必需)
/device-tree.dtb        ← 设备树(必需)
/initrd.img             ← Initramfs可选
/uEnv.txt               ← U-Boot 环境(可选)
/boot.scr               ← 启动脚本(可选)

验证

Eye Remote 在上传时验证镜像:

  1. 文件系统魔数: 检查 FAT 或 ext 的魔数字节
  2. 大小检查: 强制执行 16 MiB4 GiB 边界
  3. 可提取性: 对于 TFTP尝试提取 Image、dtb、initrd
  4. SHA256 摘要: 计算并存储以进行完整性跟踪

如果验证失败,上传将以 400 Bad Request 拒绝。


配置

secubox.conf

Eye Remote bootstrap 遵循 /etc/secubox/secubox.conf 中的以下设置:

[eye_remote]
enabled = true
bootstrap_enabled = true
bootstrap_root = "/var/lib/secubox/eye-remote/boot-media"
max_image_size_gb = 4
min_image_size_mb = 16

[eye_remote.tftp]
enabled = true
dnsmasq_config = "/etc/dnsmasq.d/secubox-eye-remote-tftp.conf"
port = 69

[eye_remote.gadget]
ecm_enabled = true
acm_enabled = true
mass_storage_enabled = true

TFTP DHCP 配置 (dnsmasq)

文件: /etc/dnsmasq.d/secubox-eye-remote-tftp.conf

# TFTP service for Eye Remote bootstrap
enable-tftp
tftp-root=/var/lib/secubox/eye-remote/boot-media/tftp
tftp-port=69
listen-address=10.55.0.2
# Allow read from tftp root only (security)
tftp-secure
# Increase timeout for large initrd
tftp-max-block-size=1024

上电顺序

注意: Eye Remote 小工具在启动时立即附加 LUN。ESPRESSObin U-Boot 负责检测 LUN 并启动 usb start

推荐序列:

  1. 给 ESPRESSObin 上电U-Boot 启动,等待用户输入)
  2. 将 USB OTG 电缆插入 Eye Remote
  3. 等待 2 秒进行 USB 枚举
  4. 在 U-Boot 上按 Enter 中断自动启动
  5. 执行 usb start 命令
  6. 执行 fatload usb 0 ... 加载内核

故障排除

问题:"ESPRESSObin 上 LUN 不可见"

症状:

  • usb start 不显示大容量存储设备
  • usb tree 仅列出集线器,无 LUN

诊断:

# 在 Eye Remote主机上:
ssh pi@eye-remote.local
systemctl status secubox-eye-remote-gadget

# 检查小工具树是否存在:
ls -la /sys/kernel/config/usb_gadget/secubox/functions/mass_storage.usb0/

解决方案:

  1. 重启小工具:

    systemctl restart secubox-eye-remote-gadget
    
  2. 检查 active 符号链接存在:

    ls -la /var/lib/secubox/eye-remote/boot-media/active
    # 应指向真实镜像文件
    
  3. 验证文件可读:

    ls -lah /var/lib/secubox/eye-remote/boot-media/images/
    # 文件应具有读取权限
    
  4. 物理检查 USB 连接:

    • 使用 DATA 端口(中间),而不是 PWR 端口
    • 尝试不同的 USB 电缆或端口
    • 验证 Eye Remote 和 ESPRESSObin 之间没有 USB 集线器

问题:"TFTP 超时 / 未找到镜像"

症状:

  • tftpboot 挂起或报告 "not found"
  • TFTP 根路径不正确

诊断:

# 检查 TFTP 服务:
curl http://10.55.0.1:8000/api/v1/eye-remote/boot-media/tftp/status | jq .

# 检查 shadow 符号链接:
ls -la /var/lib/secubox/eye-remote/boot-media/tftp/

# 验证 dnsmasq TFTP 正在运行:
ps aux | grep dnsmasq
netstat -tlnup | grep :69

解决方案:

  1. 首先上传镜像到 shadow:

    curl -X POST \
      -H "Authorization: Bearer $JWT" \
      -F "image=@debian-bookworm.img" \
      http://10.55.0.1:8000/api/v1/eye-remote/boot-media/upload
    
  2. 验证网络连接:

    # 在 ESPRESSObin U-Boot 上:
    => ping 10.55.0.2
    # 应使用主机 IP 响应
    
  3. 检查文件提取是否成功:

    • TFTP 状态应显示非空 files 数组
    • 如果镜像是原始的(无文件系统),提取必须优雅失败
    • 改为使用 LUN 启动

问题:"交换失败 / LUN 弹出超时"

症状:

  • POST /swap 返回 500 错误
  • LUN 在小工具中卡住

诊断:

# 检查小工具锁:
lsof | grep /var/lib/secubox/eye-remote/boot-media/

# 检查 gadget-setup.sh 日志:
journalctl -u secubox-eye-remote-gadget -n 50

# 验证文件锁未被持有:
ps aux | grep eye-remote

解决方案:

  1. 通过 shell 强制弹出(小心!):

    sudo /usr/sbin/gadget-setup.sh swap-lun ""
    sleep 0.5
    sudo /usr/sbin/gadget-setup.sh swap-lun \
      "/var/lib/secubox/eye-remote/boot-media/active"
    
  2. 重启小工具服务:

    systemctl stop secubox-eye-remote-gadget
    sleep 2
    systemctl start secubox-eye-remote-gadget
    
  3. 检查过时进程:

    systemctl status secubox-eye-remote-api
    # 如果 API 进程持有锁,重启它
    systemctl restart secubox-eye-remote-api
    

问题:"上传时文件系统无效"

症状:

  • POST /upload 返回 400 Bad Request
  • 错误:"Invalid filesystem" 或 "Size out of range"

解决方案:

  1. 验证镜像格式:

    file debian-bookworm.img
    # 应输出: FAT boot sector, x86 or x64 boot loader binary
    # 或: Linux rev 1.0 ext4 filesystem
    
  2. 检查镜像大小:

    ls -lh debian-bookworm.img
    # 应在 16 MiB 到 4 GiB 之间
    
  3. 如果需要,创建有效的 FAT32 镜像:

    # 创建 256 MiB FAT32 镜像
    fallocate -l 256M debian-bookworm.img
    mkfs.vfat -F32 debian-bookworm.img
    
    # 挂载并复制内核文件
    sudo mount debian-bookworm.img /mnt/boot
    sudo cp Image /mnt/boot/
    sudo cp device-tree.dtb /mnt/boot/
    sudo umount /mnt/boot
    

另请参阅


CyberMind · SecuBox-Deb · Eye Remote Bootstrap v2.1.0

最后审查: 2026-04-23 · 维护者: Gerald Kerma gandalf@cybermind.fr