用 Dropbear 为 Buildroot ARM64 设备配置密钥登录与端口转发

发布时间:2026/11/5 5:31:47
用 Dropbear 为 Buildroot ARM64 设备配置密钥登录与端口转发

用 Dropbear 为 Buildroot ARM64 设备配置密钥登录与端口转发

前几篇已经在 Ubuntu 20.04、Buildroot 2025.02.8 和 QEMU ARM64 环境中完成了服务启动、权限隔离、日志轮转与 TCP 重连实验。接下来解决设备远程维护中的一个基础问题:怎样通过 SSH 安全登录目标系统,并使用端口转发访问目标端可达的 TCP 服务。

本文使用 Buildroot 提供的 Dropbear SSH 服务端。实验包括公钥登录、禁用密码登录、通过 QEMU user-mode 网络将主机本机端口转发到 guest 的 SSH 端口,以及通过 SSH 本地转发访问 Ubuntu 主机上的 TCP 测试服务。

安全边界:本文为了验证方便,以 root 的 SSH 公钥登录 QEMU guest,并只把 QEMU 转发端口绑定到 Ubuntu 的127.0.0.1。不要把此配置不加审查地用于实体设备或暴露到局域网、互联网;实际产品应使用专用非 root 管理账户、持久保存并保护主机密钥,并制定访问控制和密钥轮换方案。

一、理解两层转发

本实验同时出现 QEMU 的hostfwd和 SSH 的-L。它们是两种不同的转发,不要混为一谈:

Ubuntu SSH 客户端 127.0.0.1:2222 | QEMU hostfwd v QEMU guest Dropbear:22 | SSH -L 隧道中的目标连接 v 10.0.2.2:5001(Ubuntu 主机上的 SSH 转发测试服务)
  • QEMUhostfwd把 Ubuntu 主机本机的 TCP2222转到 guest 的22,使主机可以连接 Dropbear。
  • SSH-L在 Ubuntu 本机监听18080;连接进入 SSH 隧道后,由 guest 连接10.0.2.2:5001。
  • -R是反向转发:监听端口建立在 SSH 服务端一侧。本篇解释方向,不把它与 QEMUhostfwd当成同一功能。

两个转发端口都绑定在127.0.0.1,实验只允许同一 Ubuntu 主机访问,不向局域网开放。

二、环境与操作约定

项目本文沿用的设置
开发主机Ubuntu 20.04 x86_64 虚拟机
Buildroot2025.02.8,~/buildroot-2025.02.8
输出目录~/buildroot-output-arm64-demo
外部目录~/buildroot-external-demo,标识ARM64_DEMO
Rootfs overlay~/buildroot-external-demo/board/qemu-arm64-demo/rootfs-overlay
目标系统QEMU ARM64,BusyBox init
SSH 服务端QEMU guest 中的 Dropbear,TCP 22
Ubuntu 转发端口127.0.0.1:2222
SSH 本地转发测试127.0.0.1:18080到10.0.2.2:5001

默认命令在Ubuntu 主机执行;标注QEMU 目标端的命令才在 ARM64 系统内执行。请沿用现有 Buildroot 输出目录和外部树,不要重新生成 defconfig 或在 QEMU 正使用镜像时构建。

Buildroot 2025.02.8 的 SysV 启动脚本/etc/init.d/S50dropbear会读取/etc/default/dropbear。它启动服务时会追加-R;缺少 host key 时,Dropbear 会在第一次收到 SSH 连接时按需生成所需密钥。服务端 host key 与 Ubuntu 上用于登录的用户密钥是两套不同密钥:前者用于识别服务器,后者用于证明客户端身份。

三、检查环境并备份

如果 QEMU 正在运行,先在QEMU 目标端执行:

poweroff

等待 QEMU 退出,再在Ubuntu 主机执行:

exportDEMO_OUT="$HOME/buildroot-output-arm64-demo"exportDROPBEAR_OVERLAY="$HOME/buildroot-external-demo/board/qemu-arm64-demo/rootfs-overlay"test-f"$DEMO_OUT/.config"printf'config check exit code: %s\n'"$?"test-d"$DROPBEAR_OVERLAY"printf'overlay check exit code: %s\n'"$?"grep-E'^BR2_(INIT_BUSYBOX|PACKAGE_DEBUG_DEMO)='"$DEMO_OUT/.config"

文件检查都应返回0,且 BusyBox init 与前文服务仍启用。若此 Buildroot 输出由外部 defconfig 管理,后续还要把 Dropbear 选项同步到实际使用的 defconfig,避免下一次重新生成配置时丢失。

先备份 Buildroot 配置和外部树中本篇会涉及的文件:

exportSSH_BACKUP="$HOME/dropbear-before-ssh-$(date+%Y%m%d-%H%M%S)"mkdir-p"$SSH_BACKUP"cp-p"$DEMO_OUT/.config""$SSH_BACKUP/buildroot.config"cp-a"$DROPBEAR_OVERLAY""$SSH_BACKUP/rootfs-overlay"printf'Backup: %s\n'"$SSH_BACKUP"

确认备份命令成功后再继续。若 overlay 已有/root/.ssh/authorized_keys或/etc/default/dropbear,先检查并备份其内容,不要直接覆盖已有密钥或 Dropbear 参数。

四、在 Ubuntu 主机生成登录密钥

在Ubuntu 主机生成一对 Ed25519 用户密钥。私钥保留在 Ubuntu 主机,不要复制到 Rootfs overlay、QEMU 镜像或版本库:

install-d-m700"$HOME/.ssh"exportSSH_KEY="$HOME/.ssh/buildroot-qemu-dropbear-ed25519"if[-e"$SSH_KEY"]||[-e"$SSH_KEY.pub"];thenprintf'Key path already exists; choose a different SSH_KEY.\n'>&2exit1fissh-keygen-ted25519-a64-f"$SSH_KEY"-C"buildroot-qemu-dropbear"

按提示设置私钥口令;输入时终端不会回显字符。若选择留空,私钥就没有口令保护,应严格保管文件权限并在实验后妥善删除或归档。若目标路径已有密钥文件,先选择新路径或确认备份,避免覆盖。

本次实际输出确认密钥对已生成(用户名路径已用$HOME简写;省略了用户专属的 fingerprint 和 randomart):

Generating public/private ed25519 key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in $HOME/.ssh/buildroot-qemu-dropbear-ed25519 Your public key has been saved in $HOME/.ssh/buildroot-qemu-dropbear-ed25519.pub

该输出确认密钥对已生成;下一步只会把.pub公钥安装到 guest,私钥留在 Ubuntu 主机。生成成功尚不代表 SSH 登录已验证。

将公钥放进 guest 的 rootfs overlay,并设置权限:

install-d-m700"$DROPBEAR_OVERLAY/root/.ssh"if[-e"$DROPBEAR_OVERLAY/root/.ssh/authorized_keys"];thenprintf'authorized_keys already exists; inspect and append the new public key without replacing existing entries.\n'>&2exit1fiinstall-m600"$SSH_KEY.pub"\"$DROPBEAR_OVERLAY/root/.ssh/authorized_keys"install-d-m755"$DROPBEAR_OVERLAY/etc/default"printf'%s\n''DROPBEAR_ARGS="-s"'\>"$DROPBEAR_OVERLAY/etc/default/dropbear"chmod644"$DROPBEAR_OVERLAY/etc/default/dropbear"

-s禁用密码登录,公钥认证仍可使用。Rootfs overlay 只包含公钥,不包含$SSH_KEY私钥。若已有authorized_keys,应先备份并把新公钥追加到原文件,而不是覆盖其他管理员密钥。

五、启用 Dropbear 并重建镜像

在Ubuntu 主机运行配置界面:

cd"$HOME/buildroot-2025.02.8"||exit1makeO="$DEMO_OUT"BR2_EXTERNAL="$HOME/buildroot-external-demo"menuconfig

在菜单中启用:

Target packages -> Networking applications -> dropbear

在 Dropbear 子菜单中,本实验按以下方式选择:

菜单项选择原因
dropbear勾选安装 SSH 服务端
client programs不勾选SSH 客户端使用 Ubuntu 主机上的ssh,guest 不需要再安装一份
disable reverse DNS lookups建议勾选本地 QEMU 实验不依赖反向 DNS,避免登录时等待名称查询
optimize for size保持默认本实验不以缩小镜像为目标
log dropbear access to wtmp/lastlog不勾选本实验不需要登录历史数据库
enable legacy crypto不勾选不启用已淘汰的旧加密算法
path to custom localoptions.h definitions file留空本实验不需要自定义编译宏

保存配置并退出。密码认证不是在此菜单中控制:后面通过/etc/default/dropbear中的DROPBEAR_ARGS="-s"禁用密码登录,同时保留公钥认证。确认服务端选项:

grep'^BR2_PACKAGE_DROPBEAR='"$DEMO_OUT/.config"

本轮 Ubuntu 主机实际输出为:

$ grep '^BR2_PACKAGE_DROPBEAR=' "$DEMO_OUT/.config" BR2_PACKAGE_DROPBEAR=y

这确认 Dropbear 服务端选项已启用。如配置由外部 defconfig 维护,请把该选项并入实际 defconfig,避免下次重新生成配置时丢失。不要运行make clean、distclean或重置整个配置。

QEMU 必须已退出。重新构建:

(set-eucd"$HOME/buildroot-2025.02.8"makeO="$DEMO_OUT"BR2_EXTERNAL="$HOME/buildroot-external-demo"-j2)build_status=$?printf'Build exit code: %s\n'"$build_status"

只有退出码为0才继续启动新镜像。本轮 Ubuntu 主机实际输出节选如下(用户主目录以$HOME代替):

ln -sf rootfs.ext2 "$DEMO_OUT/images/rootfs.ext4" >>> Executing post-image script board/qemu/post-image.sh Build exit code: 0

退出码0表示本次 Buildroot 构建及 post-image 脚本成功结束;输出还显示rootfs.ext4是指向rootfs.ext2的符号链接。这只确认镜像构建成功,Dropbear 启动、公钥登录和端口转发仍需按后续步骤在 QEMU 中验证。

六、启动 QEMU 并检查 Dropbear

在Ubuntu 主机启动 QEMU。与前文命令相比,唯一的网络变化是在-netdev user中加入只绑定回环地址的 SSHhostfwd:

exportDEMO_OUT="$HOME/buildroot-output-arm64-demo"/usr/bin/qemu-system-aarch64\-Mvirt\-cpucortex-a53\-smp2\-m1G\-nographic\-kernel"${DEMO_OUT:?请先设置 DEMO_OUT}/images/Image"\-append"rootwait root=/dev/vda console=ttyAMA0"\-drivefile="$DEMO_OUT/images/rootfs.ext2",if=none,format=raw,id=hd0\-devicevirtio-blk-device,drive=hd0\-netdevuser,id=net0,hostfwd=tcp:127.0.0.1:2222-:22\-devicevirtio-net-device,netdev=net0

QEMU 启动后,终端被串口控制台占用。以root登录后,以下检查在QEMU 目标端执行:

ps|grep'[d]ropbear'cat/etc/default/dropbearls-ld/root/.sshls-l/root/.ssh/authorized_keysls-l/etc/dropbear

本轮 QEMU 目标端实际输出如下:

# ps | grep '[d]ropbear' 122 root /usr/sbin/dropbear -s -R # cat /etc/default/dropbear DROPBEAR_ARGS="-s" # ls -ld /root/.ssh drwxr-xr-x 2 root root 1024 Nov 2 05:25 /root/.ssh # ls -l /root/.ssh/authorized_keys -rw-r--r-- 1 root root 105 Nov 2 05:25 /root/.ssh/authorized_keys # ls -l /etc/dropbear total 0

这确认 Dropbear 正在运行,启动参数包含禁用密码登录的-s和按需生成 host key 的-R。初次检查发现.ssh权限为755、authorized_keys为644,因此按下列步骤修正为本实验要求的700/600。首次 SSH 握手前/etc/dropbear为空是-R的预期行为。

修正当前 guest 的公钥权限:

chownroot:root /root/.ssh /root/.ssh/authorized_keyschmod700/root/.sshchmod600/root/.ssh/authorized_keysls-ld/root/.sshls-l/root/.ssh/authorized_keys

检查结果应显示/root/.ssh为drwx------,authorized_keys为-rw-------。

本轮修正后的实际输出为:

# chown root:root /root/.ssh /root/.ssh/authorized_keys # chmod 700 /root/.ssh # chmod 600 /root/.ssh/authorized_keys # ls -ld /root/.ssh drwx------ 2 root root 1024 Nov 2 05:25 /root/.ssh # ls -l /root/.ssh/authorized_keys -rw------- 1 root root 105 Nov 2 05:25 /root/.ssh/authorized_keys

这确认当前运行的 guest 权限已修正;无需重启 Dropbear。overlay 源文件的权限仍需按下方主机端步骤修正,以便后续重建时保留。

持久化权限:上面的chown/chmod已立即修正当前运行的 guest。为让后续重建镜像也保留权限,可在另一个Ubuntu 主机 shell修正 overlay 源文件;不必为此先关闭 QEMU,但修改 overlay 不会改变当前运行的 guest。DROPBEAR_OVERLAY是主机 shell 的变量,不会传入 guest;不要在 QEMU 的#提示符下执行以下命令。若之后要重建镜像,必须先在 QEMU 目标端执行poweroff并确认 QEMU 退出,再运行 Buildroot 构建:

exportDROPBEAR_OVERLAY="$HOME/buildroot-external-demo/board/qemu-arm64-demo/rootfs-overlay"chmod700"$DROPBEAR_OVERLAY/root/.ssh"chmod600"$DROPBEAR_OVERLAY/root/.ssh/authorized_keys"

在QEMU 目标端执行目录检查:

ls-ld/etc/dropbear readlink /etc/dropbear||truels-la/etc/dropbearls-la/var/run/dropbear

本轮首次 SSH 连接前的实际输出如下:

# ls -ld /etc/dropbear drwxr-xr-x 2 root root 1024 Nov 2 05:38 /etc/dropbear # readlink /etc/dropbear # ls -la /etc/dropbear total 2 drwxr-xr-x 2 root root 1024 Nov 2 05:38 . drwxr-xr-x 8 root root 1024 Nov 2 05:38 .. # ls -la /var/run/dropbear ls: /var/run/dropbear: No such file or directory

readlink没有输出,说明当前/etc/dropbear是真实目录;/var/run/dropbear不存在,在可写 rootfs 下符合S50dropbear将该链接替换为实际目录的处理方式。该目录首次握手前为空是预期的,host key 将由-R在首次连接时按需生成。

可写 ext2 镜像会保存生成后的 host key。下面直接通过 Ubuntu 主机发起首次连接,并在核对指纹后决定是否信任该 host key。

七、核对 host key 并测试公钥登录

本节的三个步骤都围绕首次 SSH 登录,按顺序在标明的终端执行。保持 QEMU 运行。

第一步:从 Ubuntu 主机触发首次握手,但先拒绝未知 host key。在 Ubuntu 主机另开一个终端执行:

exportSSH_KEY="$HOME/.ssh/buildroot-qemu-dropbear-ed25519"ssh-i"$SSH_KEY"\-oPreferredAuthentications=publickey\-oPasswordAuthentication=no\-oStrictHostKeyChecking=ask\-p2222root@127.0.0.1

首次连接会显示服务端 host key 指纹并询问是否继续。记录指纹,在提示处输入no,不要直接输入yes。客户端中止本次登录是预期结果;首次握手已触发 Dropbear 生成并保存 host key。本次实测输出如下:

The authenticity of host '[127.0.0.1]:2222 ([127.0.0.1]:2222)' can't be established. ECDSA key fingerprint is SHA256:xkEIJasYUsWkwjIgYAEIZ7WuozHkqkwIoZXcSfgxYek. Are you sure you want to continue connecting (yes/no/[fingerprint])? no Host key verification failed.

第二步:回到 QEMU 目标端核对指纹。

forhost_keyin/etc/dropbear/*_host_key;do[-f"$host_key"]||continueprintf'%s\n'"$host_key"dropbearkey-y-f"$host_key"|grep'Fingerprint:'done

本轮 QEMU 目标端的实际输出如下:

# for host_key in /etc/dropbear/*_host_key; do > [ -f "$host_key" ] || continue > printf '%s\n' "$host_key" > dropbearkey -y -f "$host_key" | grep 'Fingerprint:' > done /etc/dropbear/dropbear_ecdsa_host_key Fingerprint: SHA256:xkEIJasYUsWkwjIgYAEIZ7WuozHkqkwIoZXcSfgxYek

该 ECDSA 指纹与 Ubuntu 首次连接提示完全一致,host key 核验通过。若循环没有输出 key 文件,或指纹不一致,则停止登录并检查-R、/etc/dropbear写入状态和 QEMU 镜像,不要关闭 host key 校验来绕过问题。

第三步:指纹一致后再登录。在 Ubuntu 主机重新运行第一步的 SSH 命令;再次提示时确认指纹一致并输入yes。公钥认证成功后应进入 Buildroot guest shell;服务端配置-s,密码认证被禁用。本例虽使用 root 公钥登录以便做教学检查,但仅用于主机回环接口上的 QEMU 实验。真实设备应使用权限受限的专用管理账户,并尽可能禁用 root 远程登录。

可在 guest shell 中确认自己位于目标端并查看服务仍在运行:

uname-midpidof debug_demo

本轮 SSH 登录后的 QEMU guest 实际输出如下:

# uname -m aarch64 # id uid=0(root) gid=0(root) groups=0(root),10(wheel) # pidof debug_demo 128

输出确认当前 shell 位于 ARM64 guest,登录用户为 root,且debug_demo进程仍在运行。若这些命令是在 SSH 登录后的 shell 中执行,则公钥 SSH 登录已成功;PID128是本轮 guest 中该进程的进程号。

八、验证 SSH 本地端口转发

前文的 Python 服务一次只处理一个连接;debug_demo当前保持着到 TCP5000的连接,因此服务不会接受 SSH 隧道测试的新连接,直到该连接断开。这里单独使用 Ubuntu 主机 TCP5001做 SSH 转发测试,原有5000服务保持不变。

在Ubuntu 主机终端 D启动专用测试服务并保持运行。服务绑定0.0.0.0是为了让 QEMU guest 能访问10.0.2.2:5001,同时也会接受 Ubuntu 虚拟机其他网络接口上的连接;仅在可信、隔离的实验环境中临时运行,不要为此配置额外的宿主机或公网端口映射,测试后按Ctrl+C停止。

python3-u-<<'PY' import socket with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("0.0.0.0", 5001)) server.listen(4) print("SSH test server listening on 0.0.0.0:5001", flush=True) while True: connection, address = server.accept() with connection: with connection.makefile("rb") as stream: line = stream.readline(257) if line.endswith(b"\n") and len(line) <= 256: response = b"ACK " + line connection.sendall(response) print(f"received={line!r} sent={response!r}", flush=True) elif line: print(f"ignored incomplete or oversized request from {address}", flush=True) PY

另开 Ubuntu 主机终端确认服务在监听:

ss-lntp|grep':5001'

应能看到0.0.0.0:5001处于LISTEN状态。确认后再启动 SSH 隧道。

在Ubuntu 主机终端 C执行以下命令并保持前台运行:

exportSSH_KEY="$HOME/.ssh/buildroot-qemu-dropbear-ed25519"ssh-N\-oExitOnForwardFailure=yes\-i"$SSH_KEY"\-p2222\-L127.0.0.1:18080:10.0.2.2:5001\root@127.0.0.1

-N表示不在 guest 启动远程 shell;该 SSH 会话只维持转发。终端 C 无提示符、没有新输出是正常的。若端口18080已被占用,换一个本地未使用端口,并在下面的测试命令中同步修改。

另开一个Ubuntu 主机终端,用 Bash 的/dev/tcp发送一行并读取 ACK:

exec3<>/dev/tcp/127.0.0.1/18080printf'ssh-forward-check\n'>&3IFS=read-r-t5response<&3printf'response=%s\n'"$response"exec3<&-exec3>&-

预期输出为:

response=ACK ssh-forward-check

本轮 Ubuntu 主机实测收到:

response=ACK ssh-forward-check

这确认路径为 Ubuntu 本机端口18080→ SSH 本地转发 → guest →10.0.2.2:5001→ 专用 Python 测试服务。服务终端同时应记录收到请求并发送 ACK。结束时分别在终端 C 按Ctrl+C关闭 SSH 隧道,并在终端 D 按Ctrl+C停止专用测试服务;这不会停止 Dropbear 或debug-demo。

SSH 的-R与这里的-L方向相反:-R在 Dropbear 所在端建立监听端口,再把连接转回 SSH 客户端所在端。本实验只实际验证-L;不要把-R或 QEMU 的hostfwd误认为已经测试。

九、常见故障排查

现象检查方向
Connection refused连接127.0.0.1:2222确认 QEMU 的hostfwd将本机127.0.0.1:2222转到 guest22;在 guest 检查 Dropbear 是否启动和监听
Permission denied (publickey)确认公钥行在 guest 的/root/.ssh/authorized_keys;私钥与.pub配对;检查目录/文件 owner 和权限;确认/etc/default/dropbear中参数为-s而不是禁用公钥的选项
REMOTE HOST IDENTIFICATION HAS CHANGED不要关闭校验;先在 guest 控制台核对 host key 指纹。确认镜像确实重建后,再清理known_hosts中[127.0.0.1]:2222的旧记录
SSH 能登录但-L报绑定失败Ubuntu 本机端口18080已占用;改用另一空闲端口,且确认本地绑定为127.0.0.1
channel 1: open failed: connect failed: Connection refused或隧道建立但读不到 ACK-L最后一段目标地址由 guest 连接;确认 Ubuntu 主机专用 Python 测试服务正在0.0.0.0:5001监听、guest 可访问10.0.2.2:5001,且换行协议一致
局域网其他机器无法连接本文将 QEMU 转发和 SSH 本地转发都绑定在127.0.0.1,这是预期限制;不要直接改为0.0.0.0暴露服务

排查时可在 Ubuntu 客户端增加-vvv获取 SSH 连接诊断;诊断输出可能包含用户名、路径、主机 key 指纹和网络信息,公开前应先审查并脱敏。

十、实验边界与发布前记录

本篇演示的是 QEMU 本机回环场景下的 Dropbear 公钥登录和 SSH-L。它不覆盖真实网卡、DHCP、无线网络、VPN、外网防火墙、专用管理账户、密钥撤销/轮换、SSH agent 转发或生产设备的持久化密钥管理。-s禁用密码登录不等于完整安全加固;root 公钥登录仅为本实验便利,不建议直接用于产品设备。

截至本轮,Buildroot 构建、guest 的 Dropbear 启动状态、host key 指纹核验、公钥登录和 SSH 本地端口转发 ACK 均已有实测结果并记录在文中。其余未提供实测结果的内容仍按预期行为描述,不应误认为已在本次实验中验证。

十一、小结

通过在 Buildroot 中启用 Dropbear、把 Ubuntu 用户公钥放入 rootfs overlay,并以-s禁用密码登录,可以在 QEMU ARM64 中建立 SSH 公钥登录。本篇还通过只绑定127.0.0.1的 QEMUhostfwd接入 guest SSH,再用 SSH-L将主机本机端口安全转发到 guest 可达的主机 TCP 服务。QEMU 端口映射、SSH 本地转发与 SSH 反向转发是不同机制;理解各自的监听端和连接发起端,是避免配置错误和意外暴露服务的关键。