Tailscale+SSH 指南:修正板子型号为 CM5 Base Tablet + 三端 SSH 互通 + Android 编译深度踩坑

- 3.7 修正 -b orangepicm5 → orangepicm5-tablet,产物 Image-rk3588s_s/,对齐 CM5 Base Tablet 实际硬件
- 3.7.14 新增坑 11-18:aging.dtsi 注释、.git/HEAD 占位、kernel config 占位、
  WiFi/SDIO pin 冲突、VINTF 跳过、BoardConfig vs product.mk scope、Soong 缓存清理
- 第四部分:4.6 Mac→Win SSH、4.7 VM→Win 直推编译产物、4.8 三端拓扑 + 传输策略
- 3.7.15 完整流水线新增前置修补步骤

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rdzleo 2026-05-08 11:14:18 +08:00
parent 804dcab65b
commit c00ba2e6ae

View File

@ -397,15 +397,34 @@ sudo docker start -ai android-build
3. 开发板进入 Loader 模式(按住 Recovery 按键上电)
4. 在 RKDevTool 中加载各 img 文件烧录
## 3.7 OrangePi CM5 Android 13 编译环境(基于 Ubuntu 22.04 Docker
## 3.7 OrangePi CM5 Base Tablet Android 13 编译环境(基于 Ubuntu 22.04 Docker
OrangePi CM5 BaseRK3588S使用 Android 13 源码,与 Radxa CM5 的 Android 12 并行存在。
OrangePi CM5 核心板 + **CM5 Base Tablet** 底板RK3588S使用 Android 13 源码,
与 Radxa CM5 的 Android 12 并行存在。
为避免污染主机环境,使用独立的 Docker 镜像(基于 Ubuntu 22.04,对齐 OrangePi 官方推荐环境)。
### 3.7.0 板子型号选择(关键)⚠️
**本项目板子 = CM5 核心板 + CM5 Base Tablet 底板**(带板载 AP6256 WiFi/BT、M.2 SSD、DP over Type-C、MIPI 屏接口、电池充电等)。
**不是 CM5 Base 底板**Base 底板有 3 个 RJ45 网口但无 WiFi硬件差异巨大
`make.sh` 内的 board 映射必须**严格对应**底板型号,否则会烧出配置错误的固件:
| 实际底板 | `-b` 参数 | 产物目录 | kernel DTS |
|---|---|---|---|
| **CM5 Base Tablet本项目** | **`orangepicm5-tablet`** | **`rockdev/Image-rk3588s_s/`** | `rk3588s-orangepi-cm5-tablet.dts` |
| CM5 Base只有网口/无 WiFi | `orangepicm5` | `rockdev/Image-rk3588s_t/` | `rk3588s-orangepi-cm5.dts` |
| CM5 Base Tablet + MIPI LCD | `orangepicm5-tablet` + `export build_lcd_image=true` | `rockdev/Image-rk3588s_s/` | `rk3588s-orangepi-cm5-tablet-lcd.dts` |
**常见误判**
- 编 `orangepicm5` 产物目录是 `rk3588s_t`(注意是 `_t` 不是 Tablet 的意思)
- 编 `orangepicm5-tablet` 产物目录反而是 `rk3588s_s`
- 命名完全反直觉,**以 `make.sh` 里的 case 映射为准**
### 3.7.1 官方参考文档
- 《OrangePi_CM5_Base_RK3588S_用户手册_v1.3.pdf》第 7 章Android 13 源码的编译方法
- 《OrangePi_CM5_Base_Tablet_RK3588S_用户手册_v1.0.pdf》
- 《OrangePi_CM5_Base_Tablet_RK3588S_用户手册_v1.0.pdf》第 7 章Android 13 源码的编译方法
### 3.7.2 SDK 文件准备
@ -562,30 +581,38 @@ sudo docker run -it \
| `-F` | 编译 U-Boot + Kernel + Android三合一 |
| `-M` | 在 rockdev 目录生成分区镜像 |
| `-u` | 打包生成最终 update.img |
| `-b` | 指定开发板型号 |
| `--gapps` | 包含 Google Play 服务GApps |
| `-b` | 指定开发板型号(本项目必须用 `orangepicm5-tablet`,详见 3.7.0 |
| `--gapps` | 包含 Google Play 服务GApps**不需要就不加**(编译更快、镜像更干净) |
推荐全量编译命令:
推荐全量编译命令(本项目默认配置)
```bash
# 容器内或后台 docker run -d 执行
./make.sh -FMu -b orangepicm5 --gapps
./make.sh -FMu -b orangepicm5-tablet
# 如需 Google Play末尾加 --gapps
# 如接 MIPI LCD 屏:前面 export build_lcd_image=true
```
实测耗时:**6 小时 11 分钟**VMware 虚拟机 8 核 20GB RAM + 9GB Swap
实测耗时:
- **首次 Base 编译6 小时 11 分钟**(含全量下载 + 编译 + 打包)
- **切换到 Tablet 重编:预期 2~4 小时**Android 源码解析要重来kernel/uboot 部分可复用缓存)
推荐后台执行(避免 SSH 断开中断):
```bash
sudo docker run -d --name orangepi-build \
sudo docker run -d --name orangepi-tablet-build \
-v /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
bash -c 'echo 开始: $(date) > /tmp/build.log && \
./make.sh -FMu -b orangepicm5 --gapps >> /tmp/build.log 2>&1; \
echo 结束: $(date) exit=$? >> /tmp/build.log'
bash -c 'cd /workspace && \
./make.sh -FMu -b orangepicm5-tablet 2>&1 | tee /workspace/tablet-build.log'
# 查看进度
sudo docker exec orangepi-build tail -20 /tmp/build.log
sudo docker exec orangepi-build du -sh /workspace/out/
# 查看进度(每 3 秒刷新)
watch -n 3 "ssh linux-vm 'tail -5 ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/tablet-build.log'"
# 或实时跟随
ssh linux-vm 'tail -f ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/tablet-build.log'
# 监控磁盘/内存
ssh linux-vm 'docker exec orangepi-tablet-build du -sh /workspace/out/'
```
### 3.7.10 编译后的 U-Boot 修复(实测必需)
@ -635,17 +662,17 @@ sudo docker run --rm \
-v /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
./make.sh -Mu -b orangepicm5 --gapps
./make.sh -Mu -b orangepicm5-tablet
```
`-Mu` 只做"生成分区镜像 + 打包 update.img",不重新编译 Android耗时约 5-10 分钟。
### 3.7.11 验证编译结果
成功后在 `rockdev/Image-rk3588s_t/` 目录生成所有镜像:
成功后在 `rockdev/Image-rk3588s_s/` 目录生成所有镜像:
```bash
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_s/
```
预期产物(实测):
@ -678,7 +705,7 @@ ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_
1. 从虚拟机拷贝 `update.img`2.3GB)到 Windows 电脑:
```bash
# Mac 或虚拟机上执行(假设通过 Tailscale SSH
scp zhangwenqi@100.123.82.91:/home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/update.img ~/Desktop/
scp zhangwenqi@100.123.82.91:/home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_s/update.img ~/Desktop/
```
2. Windows 电脑下载并安装 **RKDevTool**(瑞芯微官方烧录工具)
@ -728,9 +755,9 @@ ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_
| JDK 版本 | OpenJDK 8 | **OpenJDK 11** |
| 交叉编译工具链 | 需手动 COPY 5 套 Linaro | 编译脚本自动下载 |
| SDK 分发方式 | 单个压缩包 | 9 个分卷(需先合并) |
| 编译脚本 | `./build.sh -AUCKup` | `./make.sh -FMu -b orangepicm5 --gapps` |
| 镜像输出目录 | `rockdev/Image-RadxaCM5/` | `rockdev/Image-rk3588s_t/` |
| GMS 支持 | 需注释(官方不含) | `--gapps` 参数直接支持 |
| 编译脚本 | `./build.sh -AUCKup` | `./make.sh -FMu -b orangepicm5-tablet` |
| 镜像输出目录 | `rockdev/Image-RadxaCM5/` | `rockdev/Image-rk3588s_s/` |
| GMS 支持 | 需注释(官方不含) | `--gapps` 参数可选(默认不启用) |
| Docker 镜像名 | android-builder:12.x | android-builder-orangepi:13.x |
### 3.7.13 容器管理
@ -758,7 +785,15 @@ sudo docker rm -f orangepi-build
| 4 | 主体编译 | 6 小时编译完成后打包失败:`u-boot/trust.img not fount` | `make.sh -F` 只跑 U-Boot 编译,没调用 Rockchip 合成脚本 | 手动 `cd u-boot && ./make.sh rk3588` |
| 5 | U-Boot 合成 | Rockchip make.sh 报 `ERROR: No python2` | Ubuntu 22.04 没 Python 2默认只有 python3 | Dockerfile 装 `python2 python2-dev python2.7` |
| 6 | 打包阶段 | mkimage.sh 找不到 `trust.img` | 新版 Rockchip 把 trust 合并到 uboot.img不单独生成 | `cp uboot.img trust.img`(副本伪装) |
| 7 | 打包阶段 | 手动 lunch 后执行 mkimage.sh 编译目标变成 `aosp_arm` | OrangePi make.sh 内部根据 `-b orangepicm5` 设置 lunch手动设不对 | 直接用 `./make.sh -Mu -b orangepicm5 --gapps` 打包 |
| 7 | 打包阶段 | 手动 lunch 后执行 mkimage.sh 编译目标变成 `aosp_arm` | OrangePi make.sh 内部根据 `-b orangepicm5-tablet` 设置 lunch手动设不对 | 直接用 `./make.sh -Mu -b orangepicm5-tablet` 打包 |
| 11 | Kernel 编译 | `fatal error: 'rk3588-aging.dtsi' file not found` 在 tablet.dts:1181 | OrangePi 公开 SDK 包未含 aging.dtsi老化测试专用Base DTS 已注释Tablet DTS 漏注释 | Tablet DTS 第 1181 行改为 `//#include "rk3588-aging.dtsi"`(见 commit `f641b4381` |
| 12 | Android Soong | `hardware/rockchip/libhwjpeg/Android.bp: module source path ".git/HEAD" does not exist` | SDK 打包时删了 `.git/` 目录,但 Android.bp 的 `genrule` 依赖 `.git/HEAD` 生成版本号 | 解压 SDK 后执行:`for m in libhwjpeg libmpimmz; do mkdir -p hardware/rockchip/$m/.git && echo "ref: refs/heads/main" > hardware/rockchip/$m/.git/HEAD; done`(脚本 genversion.sh 会容错失败,用空版本号继续) |
| 13 | Kernel Config | `kernel_config` 引用的 `sdio_bcmdhd.config` / `android-11.config` / `aw87xx.config` 不存在 | SDK 打包不完整,但 `rockchip_defconfig` 已默认启用 bcmdhd | 三个文件创建空占位:`cd kernel-5.10/arch/arm64/configs && touch sdio_bcmdhd.config android-11.config aw87xx.config`merge_config 合并空文件不影响) |
| 14 | 板子选择 | 烧录后 WiFi 扫不到、SDIO 报 `could not request pin 101 from group sdiom1-pins` | 编译用 `-b orangepicm5`Base但实际硬件是 Base TabletBase DTS 启用 gmac1RGMII bus2 pin 与 SDIO M1 复用SDIO 拿不到 pin | 改用 `-b orangepicm5-tablet`Tablet DTS 默认不启用 gmac1SDIO 正常 |
| 15 | Android 打包 98% | `check_vintf_compatible.log FAILED: For config CONFIG_DEVMEM, value = y but required n` | Android 13 FCM level 6 要求 kernel 禁用 /dev/mem但 Rockchip `rockchip_defconfig` 默认 `CONFIG_DEVMEM=y`(硬件调试依赖)。该失败导致 super.img 未打包update.img 只剩 91MB 没有 Android 主体 | 在 `device/rockchip/rk3588/rk3588s_s/BoardConfig.mk``BOARD_SKIP_VINTF_COMPATIBILITY_CHECK := true`(本开发板不走 OTA跳过检查。**切勿** 在 BoardConfig.mk 加 `PRODUCT_*` 变量,见坑 16 |
| 16 | Android lunch | 编译 1m49s 快速失败:`TARGET_PRODUCT=aosp_arm` + `kernel image not fount![/workspace/out/target/product/generic/kernel]` | BoardConfig.mk 里误放 `PRODUCT_OTA_ENFORCE_VINTF_KERNEL_REQUIREMENTS := false` → Android build 系统报 `cannot assign to readonly variable``dumpvars failed with: exit status 1` → lunch 失败 → TARGET_PRODUCT fallback 到默认 `aosp_arm` | AOSP 强制 scope`BOARD_*` 变量只能放 `BoardConfig.mk``PRODUCT_*` 变量只能放 `xxx.mk`product makefile。需 VINTF 相关 PRODUCT_* 时放 `rk3588s_s.mk`(不是 BoardConfig.mk |
| 17 | Soong 缓存污染 | 修复 BoardConfig 后重编仍然失败、`TARGET_PRODUCT=aosp_arm`,即使 lunch 命令本身正确 | 上次 lunch 失败后 Soong 把 `aosp_arm` 状态写进了 `out/soong/soong.variables``DeviceName: "generic"`),下次 build 读旧状态,即使 make.sh 调对了 lunch 也无效 | 清理 Soong 全局状态但保留 intermediates`sudo rm -f out/soong/soong.variables out/soong/build.ninja out/combined-*.ninja out/build-*.ninja out/build_date.txt out/build_progress.pb`(注意 Docker 跑的是 root宿主 zhangwenqi 删需 sudo。保留 `out/soong/.intermediates/`53G`out/target/product/rk3588s_s/`11G复用编译缓存 |
| 18 | TARGET_PRODUCT 差异 | make.sh 检测到 BoardConfig.mk 变化自动触发 `make installclean`,之后 TARGET_PRODUCT 丢失 | `build_android()` 内部 `make installclean` 可能因为环境不一致 fallback或者 `installclean` 会清掉 out/target/product/rk3588s_s/ 的部分文件,再次 lunch 时 Soong 对不上 | 尽量避免编译过程中修改 BoardConfig.mk。必须改时改完后清 Soong 状态(见坑 17再重编 |
| 8 | 资源管理 | Soong 阶段内存峰值 19GB会耗 Swap | Android 构建系统需要巨大内存 | 虚拟机分配 **20GB 内存 + 9GB Swap**(已配) |
| 9 | 编译效率 | 编译中断重启后重新跑全部阶段 | 未保留 out/ 缓存 | 不要 `rm -rf out/`,重启时 make.sh 会增量继续 |
| 10 | Docker 默认源 | `docker build``ubuntu:22.04` 超时 | Docker Hub 国内访问慢 | `/etc/docker/daemon.json` 配置镜像加速器 |
@ -790,37 +825,51 @@ cd merged && tar -xzf Android_13.tar.gz
cd ~/OrangePi_CM5/docker
sudo docker build -t android-builder-orangepi:13.x .
# 6. 主体编译(后台,约 6 小时)
sudo docker run -d --name orangepi-build \
# 6. 【前置】修补 SDK 打包残留问题(见 3.7.14 坑 11-13
cd ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13
# 6.1 Tablet DTS 注释 aging.dtsi已 commit 到 dev/macgit pull 即可)
# 6.2 创建 .git/HEAD 占位
for m in libhwjpeg libmpimmz; do
mkdir -p "hardware/rockchip/$m/.git"
echo "ref: refs/heads/main" > "hardware/rockchip/$m/.git/HEAD"
done
# 6.3 创建空 kernel config 片段占位
cd kernel-5.10/arch/arm64/configs
touch sdio_bcmdhd.config android-11.config aw87xx.config
cd ../../../..
# 7. 主体编译(后台,约 2-4 小时)
sudo docker run -d --name orangepi-tablet-build \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
bash -c './make.sh -FMu -b orangepicm5 --gapps > /tmp/build.log 2>&1'
bash -c 'cd /workspace && \
./make.sh -FMu -b orangepicm5-tablet 2>&1 | tee /workspace/tablet-build.log'
# 7. 等主体编译完成(会报 trust.img 缺失,正常)
# 8. 等主体编译完成(可能报 trust.img 缺失,走步骤 9-11 修复
# === 修复阶段 ===
# 8. 手动合成 Rockchip U-Boot
# === 修复阶段(若步骤 7 最后打包报 trust.img 缺失则需要) ===
# 9. 手动合成 Rockchip U-Boot
sudo docker run --rm \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace/u-boot \
android-builder-orangepi:13.x \
./make.sh rk3588
# 9. 创建 trust.img 伪装
# 10. 创建 trust.img 伪装
sudo cp -a ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/uboot.img \
~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/u-boot/trust.img
# 10. 只打包(复用已编译产物,约 5-10 分钟)
# 11. 只打包(复用已编译产物,约 5-10 分钟)
sudo docker run --rm \
-v ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13:/workspace \
-w /workspace \
android-builder-orangepi:13.x \
./make.sh -Mu -b orangepicm5 --gapps
./make.sh -Mu -b orangepicm5-tablet
# === 完成 ===
# 11. 验证 update.img
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_t/update.img
ls -lh ~/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_s/update.img
# 预期2.3GB
```
@ -893,7 +942,10 @@ curl -I https://releases.linaro.org/
---
# 第四部分Mac 端 SSH 连接配置
# 第四部分:三端 SSH 互通与文件传输配置
> 本部分覆盖 Mac / Linux 虚拟机 / Windows 三台机器之间的免密 SSH 配置以及大文件传输的默认策略Tailscale 直连,无需中转)。
> 4.1~4.5 是 Mac ↔ 虚拟机的基础配置;**4.6~4.8 是 2026-04-24 补齐的三端互通 + 文件传输规范**。
## 4.1 生成 SSH 密钥
@ -971,6 +1023,197 @@ macOS 原生支持 SMB通过 Tailscale 直接挂载虚拟机 Samba 共享目
- **驱动开发主力**Remote-SSH编辑 + 编译 + 调试 + git 全部在虚拟机)
- **辅助场景**SMB只看几个文件、拖文件备份
---
## 4.6 Mac → Windows SSH 免密(烧录机联动)
### 场景
Windows 是烧录机(装瑞芯微开发工具 RKDevToolMac 需要直接推送编译产物(如 `boot.img``update.img`)、远程启动 adb、查询 USB 设备状态等。不走 Mac 中转能省一次 2.3GB 拷贝。
### Windows 端一次性准备
1. **启用 OpenSSH 服务器**Windows 10/11 可选功能):
- 设置 → 应用 → 可选功能 → 添加功能 → "OpenSSH 服务器" → 安装
- PowerShell 管理员执行:
```powershell
Start-Service sshd
Set-Service sshd -StartupType Automatic
```
2. **防火墙**OpenSSH 安装通常自动加 22 端口入站规则,没有则手动加。
3. **确认用户名**SSH 用的是 Windows 账户名):`whoami` 查看,本项目账户是 `Admin``desktop-o5a0r5n\admin`)。
### Mac 端推公钥(一次性,需输一次 Windows 密码)
⚠️ **Admin 用户属于 Administrators 组,公钥必须放在 `C:\ProgramData\ssh\administrators_authorized_keys`,不是 `~/.ssh/authorized_keys`**。这是 Windows OpenSSH 的强制安全规则(`sshd_config``Match Group administrators` 指向这个文件)。
推公钥命令Mac 上执行,通过 stdin 把公钥管道给 PowerShell
```bash
# 1. Mac 确保有 ed25519 key没有就 ssh-keygen 生成)
[ -f ~/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
# 2. 一次性推公钥到 Windows输一次 Windows 密码)
cat ~/.ssh/id_ed25519.pub | ssh Admin@<Windows-Tailscale-IP> 'powershell -NoProfile -Command "$key = ($input | Out-String).TrimEnd(); New-Item -ItemType Directory -Force -Path C:\ProgramData\ssh | Out-Null; Add-Content -Path C:\ProgramData\ssh\administrators_authorized_keys -Value $key -Encoding ASCII; icacls.exe C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant Administrators:F /grant SYSTEM:F"'
```
### Mac 端 `~/.ssh/config` 加别名
```
Host windows-pc
HostName 100.70.150.78
User Admin
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
```
### 测试
```bash
ssh windows-pc 'whoami'
# 应输出 desktop-o5a0r5n\admin不问密码
```
### 中文路径处理(关键坑)
Windows OpenSSH 默认 shell 是 cmd中文路径经 SSH 通道后会被 GBK 误解析(乱码)。处理方式:**PowerShell `-EncodedCommand`UTF-16LE + Base64**
```bash
SCRIPT='Get-ChildItem -LiteralPath "F:\01_ZWQ\01_Linux_Drive\...\01_安卓13补丁修复WiFi&BLE配置问题" | Format-Table'
ENCODED=$(printf '%s' "$SCRIPT" | iconv -f UTF-8 -t UTF-16LE | base64 | tr -d '\n')
ssh windows-pc "powershell -NoProfile -EncodedCommand $ENCODED"
```
Base64 编码后是纯 ASCII完全绕过字符集和 shell 特殊字符(`&`、空格、引号)问题。
### scp 推文件到 Windows
```bash
scp ~/Downloads/boot.img 'windows-pc:F:/01_ZWQ/01_Linux_Drive/.../01_安卓13补丁修复WiFi&BLE配置问题/'
# Windows 路径用正斜杠 /,盘符用 X: 格式,中文路径 scp 能处理
```
---
## 4.7 Linux 虚拟机 → Windows SSH 免密(编译产物直推烧录机)
### 场景
编译产物 `update.img`2.3GB)要推到 Windows 烧录机。**直接从虚拟机推**最快,不经 Mac 中转(省一次 2.3GB 拷贝)。
### 配置流程Mac 作跳板全自动,无需 Windows 密码)
思路:虚拟机已有 ed25519 key之前给 GitHub 用),把公钥通过 **已免密的 Mac** 推送到 Windows——Mac 可以无密码 SSH 到 Windows所以**一次也不用输 Windows 密码**。
```bash
# 在 Mac 上执行这一段即可(自动走 Mac→虚拟机 + Mac→Windows 两条免密)
# 1. 取虚拟机公钥
VM_PUBKEY=$(ssh linux-vm 'cat ~/.ssh/id_ed25519.pub')
# 2. 通过 Mac 已免密连接 windows-pc把虚拟机公钥追加到 administrators_authorized_keys
SCRIPT="\$key = '$VM_PUBKEY'; \$auth = 'C:\\ProgramData\\ssh\\administrators_authorized_keys'; if ((Get-Content \$auth -ErrorAction SilentlyContinue) -notmatch [regex]::Escape(\$key)) { Add-Content -Path \$auth -Value \$key -Encoding ASCII; Write-Output 'APPENDED' } else { Write-Output 'ALREADY_PRESENT' }; icacls.exe \$auth /inheritance:r /grant 'Administrators:F' /grant 'SYSTEM:F' | Out-Null"
ENCODED=$(printf '%s' "$SCRIPT" | iconv -f UTF-8 -t UTF-16LE | base64 | tr -d '\n')
ssh windows-pc "powershell -NoProfile -EncodedCommand $ENCODED"
# 3. 虚拟机上配置别名
ssh linux-vm '
cat >> ~/.ssh/config << "EOF"
Host windows-pc
HostName 100.70.150.78
User Admin
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
StrictHostKeyChecking accept-new
EOF
chmod 600 ~/.ssh/config
ssh-keyscan -H 100.70.150.78 >> ~/.ssh/known_hosts 2>/dev/null
'
# 4. 测试
ssh linux-vm 'ssh windows-pc "whoami"'
# 应输出 desktop-o5a0r5n\admin
```
### 虚拟机推文件到 Windows一条命令
```bash
ssh linux-vm 'scp /home/zhangwenqi/OrangePi_CM5/Aandroid_OrangePi/merged/Android_13/rockdev/Image-rk3588s_s/update.img windows-pc:"F:/01_ZWQ/01_Linux_Drive/05_SDK_Dev/03_OrangePi_img镜像和补丁文件/01_安卓13补丁修复WiFi&BLE配置问题/"'
```
传输走 Tailscale WireGuard 直连(如果同一 LANP2P 直连不计 Tailscale relay 流量)。
---
## 4.8 三端拓扑总览 + 文件传输默认策略
### 三端免密互通拓扑(当前状态)
```
Mac (rdzleomac-studio, 100.77.198.124)
├── ssh linux-vm → 虚拟机4.1~4.3
└── ssh windows-pc → Windows4.6
虚拟机 (linux-vm, 100.123.82.91)
├── ssh windows-pc → Windows4.7
└── ssh github.com:443 → GitHub第八部分
Windows (windows-pc, 100.70.150.78)
└── (被动端:被 Mac 和虚拟机 SSH 进入)
```
三对链路全部免密 ed25519**两两可直连互传,不需要中转**。
### 文件传输默认策略
**规则 1直传优先避免中转**
| 场景 | 默认命令 |
|---|---|
| Mac 改的源码 → 虚拟机(迭代) | `scp 文件 linux-vm:~/...` |
| 编译产物(虚拟机) → Windows 烧录机 | `ssh linux-vm 'scp update.img windows-pc:/F:/...'` |
| Windows 烧录完成的日志 → Mac少量 | `scp windows-pc:/F:/xxx.log ~/` |
| Mac 文档改动 → GitHub | `git push`(走 HTTPS + ClashX |
**不走中转**`scp linux-vm:update.img ~/Downloads/ && scp ~/Downloads/update.img windows-pc:/F:/` 这种"虚拟机→Mac→Windows"的两段式传输 = 浪费时间 + 污染 Mac 磁盘。除非 Mac 真的需要这份文件。
**规则 2MD5 校验只对两端(源 + 目的)**
```bash
# 源端
SRC_MD5=$(ssh linux-vm 'md5sum /path/update.img' | awk '{print $1}')
# 目的端Windows 用 -EncodedCommand 处理中文路径)
SCRIPT='(Get-FileHash -LiteralPath "F:\...\update.img" -Algorithm MD5).Hash.ToLower()'
ENCODED=$(printf '%s' "$SCRIPT" | iconv -f UTF-8 -t UTF-16LE | base64 | tr -d '\n')
DST_MD5=$(ssh windows-pc "powershell -NoProfile -EncodedCommand $ENCODED" | grep -oE '[0-9a-f]{32}' | head -1)
[ "$SRC_MD5" = "$DST_MD5" ] && echo "✅ 一致" || echo "❌ 不一致"
```
**不做三端(源 + 通道 + 目的)校验**:通道节点(如 Mac不对数据做任何修改加多一次 MD5 计算2.3GB 约 10-30 秒)纯属浪费。
### SSH vs SMB 如何选2026-04-24 决策)
| 场景 | 协议 |
|---|---|
| Claude 自动化脚本(所有批处理) | **SSH/scp**(已配好,一条命令,脚本友好) |
| 用户手动偶尔操作Windows 资源管理器拖拽、在 Mac Finder 浏览 Linux 源码) | **SMB**(更直观) |
| 大文件首次传输 | SSH避免 SMB 挂载掉线、凭证复杂) |
**不为 SMB 配免密自动化**——SSH 已满足全部自动化需求,再配一套 Samba 凭证是"为了换而换",没有实际收益。
### 坑点速查
| 现象 | 根因 | 修复 |
|---|---|---|
| `ssh Admin@windows-pc` 提示 `Permission denied (publickey)` 即使推了公钥到 `~/.ssh/authorized_keys` | Admin 用户属于 Administrators 组Windows OpenSSH 要求改用 `C:\ProgramData\ssh\administrators_authorized_keys` | 见 4.6,把公钥加到那个文件并设 `icacls` 权限 |
| SSH 到 Windows 中文路径乱码 | 默认 shell 是 cmd字符集 GBK ≠ Mac UTF-8 | 用 PowerShell `-EncodedCommand`UTF-16LE + Base64绕过 |
| SSH 到 Windows 每次看到 `daemon not running; starting now at tcp:5037` | Windows OpenSSH 的 session 关闭后子进程被杀adb daemon 不持久 | 正常现象,不影响 adb 识别设备 |
| 虚拟机 → Windows 首次 `scp``Host key verification failed` | 虚拟机 `~/.ssh/known_hosts` 没 Windows 的 host key | `ssh-keyscan -H <Windows-IP> >> ~/.ssh/known_hosts` 预加 |
| Tailscale 流量消耗担心 | Mac/VM/Windows 同一 LAN 下 Tailscale P2P 直连,**不走 DERP relay不计 100GB/月额度** | `tailscale status` 看到 `direct <IP>:<port>` 就是直连 |
两者不冲突,可以同时配好,按需使用。
---
@ -1962,7 +2205,7 @@ Claude 收到开发任务后会自动执行:
│ ssh linux-vm 'cd ~/OrangePi_CM5/.../Android_13 && \ │
│ git fetch origin && \ │
│ git checkout dev/mac && git pull && \ │
│ ./make.sh -F -b orangepicm5'
│ ./make.sh -F -b orangepicm5-tablet' │
│ │
│ 编译输出直接进 Claude 的上下文 │
└──────────────────┬───────────────────────────────────────┘