在 Android 上创建 GNU/Linux 容器

date_range 2020年04月29日 (Wed) account_box ホロ   dashboard Linux  

那这次再说吧……

再一次提醒没有耐心和同理心的家伙们去用其它即用的工具像是 TermuxArch 和 Linux Deploy ……

所以这回咱们要干什么?

由于咱不可能把所有发行版都装一次,也不可能在各种手机上都测试一遍,于是咱只能拿 咱手边的家伙举个例子,例如咱手上的小米平板4 ……

  • 一部 aarch64 / arm64 设备

现在的手机 CPU 基本上都是 64 位了吧,不知道的话搜索一下手上 CPU 的型号应该就能看个大概。

  • 比较新的 Android 系统,最好有 root 权限和完整 busybox 支持。

如果汝已经动手给手机安装了第三方 ROM,那应该不是什么难事。 某蓝绿海厂等受害者可以尝试 UserLAnd……

  • 一些剩余存储空间 (这不是废话么)
  • 安装好终端模拟器和合适的键盘。

或者 Termux 也可以,在里面装上 tsu 以后可以让 Termux 里的 Bash 以 root 用户运行。

作为好几年的 Arch Linux 受害 爱好者,咱当然就装一个 ArchLinux ARM 试试看啦 (虽然这不是官方分支,以及不是有 TermuxArch 了么……)

那么我们的目标是,没有蛀牙 ……

  • 能够在 chroot 下基本正常工作

例如正常的使用包管理器、安装软件以及用它们等等。

  • 能够访问网络和内存设备 (踩坑警告)
  • 能够通过外部访问 (ssh 或者 vnc 啥的)

如有任何不适还请装作没事

天降大坑 nosuid ……(?)

某些手机上的 /data 挂载的时候加上了 nosuid 选项,于是 sudo 就炸了……

sudo: effective uid is not 0, is /sbin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?

这种情况可以考虑创建一个磁盘映像把容器放进去(大概吧……)

# 找个地方创建一个映像文件
dd if=/dev/zero of=/path/to/root.img bs=1048576 count=4096

# 给映像创建文件系统和挂载
mke2fs -t ext4 -F /path/to/root.img
mkdir -p /data/linux/arch
mount -t ext4 -o loop /path/to/root.img /data/linux/arch

如果肯定不用 sudo 的话能不能当做没事……

要有空间,于是……

丢了个 tarball 下来让汝自己 bootstrap 去(啥?)

安装 GNU/Linux 发行版的过程,其实就是在解决一个先有鸡还是先有蛋的问题,这个过程就是 bootstrap。 大概就是……

  • 从别的什么可以启动的地方把要安装的 GNU/Linux 发行版的最小的部份下载回来,安装上。
  • 通过一些方法进入这个最小的部份中(例如 chroot ),为目标设备特化配置。

只不过大部分发行版的安装过程中被自动化了(Arch / Gentoo 等用户表示情绪稳定。)。 在 Android 上安装,同样要经过这个过程。

于是,首先咱们要下载 Arch Linux ARM 的 tarball ,可以在电脑上下载下来发送到手机上,也可以用手机 的浏览器直接下载,也可以用 curl 或者 wget:

wget http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz

如果直接从官方仓库下载不够快的话,可以通过镜像网站下载,例如:

https://mirrors.tuna.tsinghua.edu.cn/archlinuxarm/os/ArchLinuxARM-aarch64-latest.tar.gz

然后创建一个目录存放解压出来的基本系统,再解压出来刚下载的 tarball:

# 因为 SD 卡可能放不下权限等神奇的原因,就放在内存设备上好了……
# mkdir -p /data/linux/arch
# 以及 Arch Linux ARM 官方是建议用 bsdtar 的,要是汝的手机上凑巧有(不管是
# 系统里的还是 Termux 的),就用上吧……
# bsdtar -xpf /path/to/ArchLinuxARM-aarch64-latest.tar.gz -C /path/to/mountpoint
# 不然 tar 也是可以的。
# tar -xzvf /path/to/ArchLinuxARM-aarch64-latest.tar.gz -C /path/to/mountpoint

Chroot on Android

在普通的 GNU/Linux 发行版上 chroot 时,咱们大抵会这么做:

# 切换到 chroot 的目标目录并挂载上 /dev /sys 这样的伪文件系统
# cd /location/of/new/root
# mount -t proc proc proc/
# mount --rbind /sys sys/
# mount --rbind /dev dev/
# 通过给定的 shell 进入 chroot
# chroot /location/of/new/root /bin/bash

在 Android 上其实也差不多:

# mount -o bind /dev /data/linux/arch
# mount -o bind /sys /data/linux/arch/sys
# mount -o bind /proc /data/linux/arch/proc
# mount -t tmpfs tmpfs /data/linux/arch/tmp

以及记得修改一下 chroot 里面的 /etc/resolv.conf , Arch 这个默认是到 /run/systemd/resolve/resolv.conf 的软链接。 反正里面也没有 systemd 用 ,于是 删掉重建好了……

# /data/linux/arch/etc/resolv.conf

nameserver 8.8.8.8
nameserver 8.8.4.4

如果有必要的话,可以同时修改里面的镜像仓库地址。

# /data/linux/arch/etc/pacman.d/mirrorlist

Server = https://mirrors.ustc.edu.cn/archlinuxarm/$arch/$repo

然后就和平常一样 chroot 进去咯~

clover:/data/linux # chroot /data/linux/arch /bin/bash
[root@localhost /]#

起始配置

The bootstrap environment is really barebones (no nano or lvm2). Therefore, we need to set up pacman in order to download other necessary packages.

这下知道为啥要在外面改 /etc/resolv.conf 了吧(当然汝要是独辟蹊径的话那当咱没说……)

正如 官方说明提示的一样 ,初始化 pacman 密钥环和获得密钥:

[root@localhost /]# pacman-key --init
gpg: /etc/pacman.d/gnupg/trustdb.gpg: trustdb created
gpg: no ultimately trusted keys found
gpg: starting migration from earlier GnuPG versions
gpg: porting secret keys from '/etc/pacman.d/gnupg/secring.gpg' to gpg-agent
gpg: migration succeeded
gpg: Generating pacman keyring master key...
gpg: key 56753AA14274D5A7 marked as ultimately trusted
gpg: directory '/etc/pacman.d/gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/etc/pacman.d/gnupg/openpgp-revocs.d/46C9147EE071F7E5D16A085856753AA14274D5A7.rev'
gpg: Done
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
[root@localhost /]# pacman-key --populate archlinuxarm
==> Appending keys from archlinuxarm.gpg...
==> Locally signing trusted keys in keyring...
-> Locally signing key 69DD6C8FD314223E14362848BF7EEF7A9C6B5765...
-> Locally signing key 02922214DE8981D14DC2ACABBC704E86B823CD25...
-> Locally signing key 9D22B7BB678DC056B1F7723CB55C5315DCD9EE1A...
==> Importing owner trust values...
gpg: setting ownertrust to 4
gpg: inserting ownertrust of 4
gpg: setting ownertrust to 4
==> Updating trust database...
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   3  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1  valid:   3  signed:   1  trust: 0-, 0q, 0n, 3m, 0f, 0u
gpg: depth: 2  valid:   1  signed:   0  trust: 1-, 0q, 0n, 0m, 0f, 0u
[root@localhost /]#

接下来安装(和更新)基本系统,如果有别的需要的话也可以装别的。

[root@localhost /]# pacman -Syu base base-devel nano --needed

如果不幸遇到了像是 error: could not determine cachedir mount point /var/cache/pacman/pkg 这样的错误, 那在 /etc/pacman.conf 里把 Misc options 下面的 CheckSpace 注释掉应该能绕过 (于是有别的方法嘛)

基于正经的 GNU/Linux 用户不会日用 root 账户这一指导原则(啥?),咱们也要新建一个用户:

[root@localhost /]# useradd -m -s /bin/bash horo
[root@localhost /]# passwd horo

有必要的话也可以修改 sudoers 文件(记得用 visudo),把汝刚刚创建的用户添加到 sudoers 中。

以及介于 Android 的魔改属性,只有特定的组可以进行像是访问网络或者访问 SD 卡等操作,于是 还要在 chroot 里新建相应的组,并把新建的用户加到这些组去:

# groupadd 可以用 -g 参数制定新增组的 id ,至于这些组分别是啥
# 看后面的组名应该就知道了吧……
groupadd -g 3001 android_bt
groupadd -g 3002 android_bt-net
groupadd -g 3003 android_inet
groupadd -g 3004 android_net-raw
groupadd -g 1015 sdcard-rw
groupadd -g 1028 sdcard-r
# 然后把新建的用户添加到合适的组中
gpasswd -a horo android_bt
gpasswd -a horo android_bt-net
gpasswd -a horo android_inet
gpasswd -a horo android_net-raw
gpasswd -a horo sdcard-rw
gpasswd -a horo sdcard-r

最后(?),因为没有 systemd,所以请像新装 Arch Linux 的时候一样手动设置一下 locales:

  • /etc/locale.gen 是一个仅包含注释文档的文本文件。指定您需要的本地化类型,去掉对应行前面的注释符号(#)就可以啦,还是用 nano 打开,建议选择帶UTF-8的項:
# nano /etc/locale.gen

en_US.UTF-8 UTF-8
  • 执行 locale-gen 以生成 locale 讯息:
# locale-gen
  • 创建 locale.conf 并提交本地化选项:
# echo 用来输出某些文字,后面的大于号表示把输出保存到某个文件里啦~

# 或者可以用文字编辑器新建这个文件加上这一行。

# echo LANG=en_US.UTF-8 > /etc/locale.conf
  • 设置用户级别的 locale

    用 su 切换到刚建立的用户,然后编辑 ~/.bashrc 修改自己的 Locale ,例如:

LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

(为啥不是 ~/.config/locale.conf 了啊…… 其实咱也不知道…… )

于是现在大概就有了这个样子:

于是现在大概就有了这个样子

(这个在终端里显示发行版等信息的软件是 screenfetch 啦,也有人喜欢另一个 小修改版 neofetch )

设置 SSH 和 SD 卡访问

SSH 的话,生成好主机密钥然后再启动 sshd 就可以:

## 在有 systemd 这样的 init 系统的发行版上启动 sshd 时会帮汝运行这一步,
## 不过这里就只有自己代劳啦……
# ssh-keygen -A
## 这里一定要是绝对路径,不然就会出 "sshd re-exec requires execution with an absolute path" 错误。
# /usr/bin/sshd

然后就可以用一个新的终端模拟器窗口通过 ssh 连接进来啦……

clover:/ $ ssh horo@127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256: .
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
horo@127.0.0.1's password:
[horo@localhost ~]$

要是汝在登录时遇到了 PTY allocation failed on channel 0 这样的错误,在 chroot 里(重新) 挂载一下 /dev/pts 试试?

# umount /dev/pts
# mount -t devpts devpts /dev/pts

以及可以的话记得修改 /etc/sshd_config 把 sshd 限定到只允许本地连接?

要把 SD 卡挂载到哪里的话,在 chroot 的外面运行:

## 当然汝的 /sdcard 可能是汝的内存设备什么的,别忘了自己调整路径……
# mkdir /data/linux/arch/mnt/sdcard
# mount -o bind /sdcard /data/linux/arch/mnt/sdcard

设置 VNC 访问

首先安装桌面和 VNC Server ,考虑到两大桌面的资源需求和或多或少有点依赖 GNOME,比较合适的是 xfce:

## 当然汝可以看需要装些别的,像是中文字体什么的。
# pacman -S xorg xfce4 xfce4-goodies tigervnc

接下来启动 vncserver,设置一个密码:

[horo@localhost ~]$ vncserver

You will require a password to access your desktops.

Password:
Verify:
# 要创建一个只读桌面用的密码嘛?
Would you like to enter a view-only password (y/n)?
xauth:  file /home/horo/.Xauthority does not exist

# 桌面的位置
New 'localhost:1 (horo)' desktop is localhost:1

Creating default startup script /home/horo/.vnc/xstartup
Creating default config /home/horo/.vnc/config
Starting applications specified in /home/horo/.vnc/xstartup
Log file is /home/horo/.vnc/localhost:1.log

然后先用 vncserver -kill :1 中止掉现有的 VNC Server 进程,修改一下新生成的配置文件。

# /home/horo/.vnc/xstartup

#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
exec xfce4-session

# /home/horo/.vnc/config

## Supported server options to pass to vncserver upon invocation can be listed
## in this file. See the following manpages for more: vncserver(1) Xvnc(1).
## Several common ones are shown below. Uncomment and modify to your liking.
##
# securitytypes=vncauth,tlsvnc
# desktop=sandbox
# 可以在这里修改分辨率和限定本地连接,具体的参数可以查阅相应的手册页来了解。 (vncserver(1) Xvnc(1))
geometry=1920x1080
# localhost
# alwaysshared

如果一切正常的话应该会连上去之后有 xfce 的界面的,但是咱这回就只有 Unable to contact settings server 这个错误,以后再说吧(跑) 不过隔壁 Linux Deploy 出的 Debian 是好的……

总结?

所以有 Linux Deploy 那样好用(?)的工具了为啥不直接拿来用呢(划掉)

以及纯粹是闲的,找一台正经的电脑装正经的 GNU/Linux 不好么 (x)


keyboard_arrow_left 上一篇文章: 在 Android 上使用 GNU/Linux 工具 keyboard_arrow_right 下一篇文章: 切点洋葱 - 搭建一个洋葱服务

想要表达对咱的支持的话,汝可以:

需要 JavaScript 支持来使用 Isso 😂