把闲置小米手机改成 Ubuntu 服务器
我手里有一台闲置的小米手机,系统是 Android 13,已经安装 Magisk,Termux 也能取得 root 权限。我打算让它长期插电,运行 SSH 和监控程序。
这台手机最后保留了两个 SSH 端口:22 进入 Ubuntu,8022 进入 Termux。平时用 22;Ubuntu 起不来或需要修改 Android 文件时,再用 8022。下面按实际安装顺序来写。递归挂载事故和恢复过程放在下一篇。
1. 准备的软件
这次使用的版本如下:
| 软件 | 版本或要求 |
|---|---|
| Android | 13,已 Root |
| Magisk | 能执行 /data/adb/service.d/ 中的脚本 |
| BusyBox NDK | v1.36.1 |
| chroot-distro | v2.1.0 |
| Ubuntu | 24.04.3 LTS |
| Termux | 已安装 OpenSSH、Termux:API 和 termux-api |
脚本可以下载为完整附件包,每个文件的安装位置写在附件说明。附件里的 Wi-Fi 配置是空的,需要换成自己的值;Cloudflare 和监控相关的占位属于组网与监控篇,本文用不到。
不同手机的 Magisk、SELinux、电池管理和 cmd wifi 可能不一样。下面的步骤适合复现这台手机的做法;换机型时,挂载路径、电池控制节点和 cmd wifi 的行为都要先验证再照搬。
2. 安装 BusyBox、chroot-distro 和 Ubuntu
先在 Magisk 中安装 BusyBox NDK,再安装 chroot-distro。两个模块装完后重启手机,否则 chroot-distro 需要的挂载还没有准备好。
进入 Termux,下载并安装 Ubuntu:
chroot-distro download ubuntu
chroot-distro install ubuntu
进入 Ubuntu 后检查版本:
cat /etc/os-release
id
我这里显示 Ubuntu 24.04.3 LTS,当前用户是 root。这里的 Ubuntu 是用 chroot 方式运行的:用户空间放在 Android 的一个目录里,共用 Android 内核,没有普通 Ubuntu 的 systemd,也不能按虚拟机的方式使用独立内核。
安装时遇到过一个容易忽略的问题。我从电脑用 SSH 执行 Termux 命令时,非交互会话里没有平时的 $PREFIX 环境变量,安装命令因此把文件写到了 /bin/chroot-distro,系统拒绝写入。把命令里的 $PREFIX 替换成 /data/data/com.termux/files/usr 后执行正常。
通过 SSH 自动执行 Termux 命令时,不要假定 $PREFIX、PATH 和交互终端里完全一样。
3. Ubuntu 里没有 systemd
普通 Ubuntu 可以用 systemctl enable --now ssh 设置开机启动,这个 chroot 里没有 systemd,也没有 journald。sshd、rsyslog、fail2ban 和 Beszel Agent 都需要脚本启动。
我的做法是:Magisk 在 Android 启动后运行 94-ubuntu-services.sh;这个脚本挂载 Ubuntu,然后执行 Ubuntu 里的 /usr/local/sbin/phone-server-services。后一个脚本每 60 秒检查一次 sshd 是否在监听。
94-ubuntu-services.sh 只在首次成功时执行一次 chroot-distro mount。phone-server-services 退出后,脚本只重新启动它,不再重复挂载,避免挂载逐次累积;初次挂载失败时会等待 30 秒重试。
附件中的 phone-server-services 默认只启动 sshd。rsyslog、fail2ban 和 Beszel 需要先安装并完成各自配置,再把文件顶部的开关从 0 改为 1。目前这台手机的 rsyslog 和 fail2ban 还没有确认长期正常,因此附件没有默认开启。
附件里的 ENABLE_BESZEL 保持为 0,不包含任何凭据。复现这台手机已有的 Beszel 监控时,先在 Beszel Hub 中新增 phone-server,把 Hub 生成的 /etc/default/beszel-agent 放入 Ubuntu,再把设备上 /usr/local/sbin/phone-server-services 实际副本中的 ENABLE_BESZEL 改为 1。
4. 配置两个 SSH 端口
| 端口 | 程序 | 什么时候使用 |
|---|---|---|
| 22 | Ubuntu 的 sshd | 日常登录、运行 Ubuntu 命令 |
| 8022 | Termux 的 sshd | Ubuntu 无法启动时修改 Android 或 Magisk 文件 |
先在 Ubuntu 中安装 OpenSSH Server 和 sudo:
apt update
apt install -y openssh-server sudo nano
创建普通用户,并让它可以使用 sudo:
adduser <用户名>
usermod -aG sudo <用户名>
为这个用户创建 SSH 目录:
install -d -m 700 -o <用户名> -g <用户名> /home/<用户名>/.ssh
nano /home/<用户名>/.ssh/authorized_keys
chown <用户名>:<用户名> /home/<用户名>/.ssh/authorized_keys
chmod 600 /home/<用户名>/.ssh/authorized_keys
把客户端的公钥写入 authorized_keys。下一步先启动 sshd,确认普通用户可以通过公钥登录,再关闭 root 和密码登录。
Ubuntu 没有 systemd,第一次启动 sshd 时要自己创建运行目录:
install -d -m 755 /run/sshd
/usr/sbin/sshd -t
/usr/sbin/sshd
保留当前的 Termux 8022 会话,再从另一台设备测试 Ubuntu 22。确认公钥登录成功后,编辑 /etc/ssh/sshd_config,加入或修改:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
可以用下面的命令查看 sshd 最终采用的值:
sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication'
语法没有问题后,让正在运行的 sshd 重新读取配置:
/usr/sbin/sshd -t
kill -HUP "$(cat /run/sshd.pid)"
我第一次检查时出现过“密码仍然能登录”的假象。原因是 macOS 复用了已经登录过的 SSH ControlMaster 连接,新的终端并没有重新认证。重新测试时要禁用连接复用:
ssh -o ControlMaster=no -o ControlPath=none <用户>@<手机地址>
禁用复用后,密码登录被拒绝,公钥登录成功。
早期的开机脚本还用过 pgrep -f /usr/sbin/sshd 检查 sshd。这个命令可能匹配到正在执行检查的命令本身,于是脚本以为 sshd 已经启动。后来改为检查 22 端口是否在监听。
5. 设置 ACC 充电限制
手机长期接电,我用 ACC(Advanced Charging Controller,按电量和温度控制充电的 Magisk 模块)限制充电,安装与配置方法沿用手机搭建服务器第二期。我在 ACC 中使用下面的参数:
- 电量低于 75% 时恢复充电;
- 电量达到 85% 时暂停充电;
- 温度达到 45°C 时停止充电;
- 温度降到 40°C 时允许恢复。
设置后检查过一次:手机接着电源,电量高于 85%,系统显示外部电源已连接、充电电流为 0、ACC 状态为 Idle。当时充电已经暂停。至于电池寿命能延长多少,还没有长期数据。
6. 复制开机脚本
需要部署的主要文件如下:
| 文件 | 放到哪里 | 作用 |
|---|---|---|
60-termux-wake-keeper.sh |
/data/adb/service.d/ |
开机后启动 wake keeper |
94-ubuntu-services.sh |
/data/adb/service.d/ |
挂载一次 Ubuntu,并启动 Ubuntu 内的服务 |
98-termux-sshd.sh |
/data/adb/service.d/ |
启动 Termux 的 sshd(8022),首次解锁前等待重试 |
99-phone-server.sh |
/data/adb/service.d/ |
启动 /data/local/wifi-keeper/wifi-keeper.sh |
phone-server-services |
Ubuntu 的 /usr/local/sbin/ |
chroot 内服务管理脚本,默认只启动 sshd |
termux-wake-keeper.sh |
/data/local/termux-wake-keeper/ |
定期刷新 wake lock |
wifi-keeper.sh |
/data/local/wifi-keeper/ |
检查 Wi-Fi 和 IPv6,断线时尝试重新连接 |
termux-wake-keeper.sh 在附件包里;wifi-keeper.sh 的完整脚本来自参考资料中的第四期文章,填入自己的 Wi-Fi 后再放到 /data/local/wifi-keeper/。
wake keeper 通过 Termux:API 持有 wake lock(阻止 Android 休眠的锁),并每 60 秒续锁,避免 Android 休眠后杀掉后台任务。
复制完成后设置可执行权限。不要覆盖 service.d 中原有的其他 Magisk 脚本。
Android 侧文件在 Android 的 root shell 中执行:
chmod 755 /data/adb/service.d/60-termux-wake-keeper.sh
chmod 755 /data/adb/service.d/94-ubuntu-services.sh
chmod 755 /data/adb/service.d/98-termux-sshd.sh
chmod 755 /data/adb/service.d/99-phone-server.sh
chmod 755 /data/local/termux-wake-keeper/termux-wake-keeper.sh
chmod 755 /data/local/termux-wake-keeper/stop-termux-wake-keeper.sh
chmod 755 /data/local/wifi-keeper/wifi-keeper.sh
phone-server-services 在 Ubuntu 文件系统内,进入 Ubuntu chroot 后执行:
chmod 755 /usr/local/sbin/phone-server-services
关闭 chroot-distro 默认的 Android 根目录绑定:
chroot-distro android-bind disable
这一步会修改 /data/local/chroot-distro/.config/android_bind。关闭后,Ubuntu 不再把 Android 的 /data 挂进去。
7. 重启后怎样检查
重启手机后要先解锁一次。Termux 的文件放在 Android 凭据加密存储中,首次解锁之前,8022 和 Termux API 都不可用。98-termux-sshd.sh 会等待并重试,但它不能代替人工解锁。
解锁后按下面的顺序检查:
- 从另一台机器连接 Termux 的 8022;
- 连接 Ubuntu 的 22;
- 查看
/data/local/ubuntu-services-boot.log; - 查看 Ubuntu 的
/var/log/phone-server-services.log; - 在 Ubuntu 里执行
mount | wc -l,隔一段时间再看一次,数字应保持稳定。
如果 8022 能连而 22 不能连,先看 94-ubuntu-services.sh 的日志。如果两个端口都不能连,先确认手机已经解锁、Wi-Fi 正常、Termux 没有被系统清理。
参考资料
- 手机搭建服务器第一期:设备、Root 与公网条件
- 手机搭建服务器第二期:ACC 与供电
- 手机搭建服务器第四期:Wi-Fi、IPv6 与 DDNS
- 手机搭建服务器第五期:Termux 与 Ubuntu
- chroot-distro Magisk 模块文档
手机接入 Tailscale、SSH 配置和监控的步骤写在组网与监控篇。