原文地址:https://yzddmr6.com/posts/android-run-docker/
背景
最近收拾東西翻出了抽屜里吃灰的小米6。小米6當(dāng)年可以說(shuō)是神機(jī)一部,最好的835遇到了最好的MIUI9。如今放在抽屜里吃灰實(shí)在可惜,想著拿來(lái)做點(diǎn)什么讓它繼續(xù)發(fā)揮余熱。
隨后就萌生了一個(gè)想法:在手機(jī)上跑Docker,這樣的話就可以用到很多打包好的Docker應(yīng)用了。后來(lái)發(fā)現(xiàn)有這個(gè)想法的人不止我一個(gè),有很多大佬已經(jīng)實(shí)現(xiàn)了。原生安卓無(wú)法直接運(yùn)行Docker的原因是:安卓雖然基于Linux,但是內(nèi)核閹割了很多東西,很多Docker相關(guān)的的內(nèi)核選項(xiàng)沒(méi)有開(kāi)啟,所以需要通過(guò)刷機(jī)來(lái)進(jìn)行修改。
本人總共嘗試了兩種方案:一種是重新編譯安卓?jī)?nèi)核,開(kāi)啟對(duì)應(yīng)選項(xiàng)。不過(guò)在本人的小米6,Linux內(nèi)核4.4版本,LineageOS 19.1上失敗了;另一種是直接刷入原生的Linux系統(tǒng),成功啟動(dòng)了Docker。在這里跟大家分享一下刷機(jī)的過(guò)程。
方案一:重新編譯安卓?jī)?nèi)核
如果可以通過(guò)修改安卓?jī)?nèi)核來(lái)開(kāi)啟Docker應(yīng)該是最理想的方案:這樣可以在保留手機(jī)原有功能架構(gòu)的基礎(chǔ)上來(lái)提高我們的可玩性。老外寫(xiě)過(guò)一篇詳細(xì)的教程,可以按照這個(gè)來(lái):https://gist.github.com/FreddieOliveira/efe850df7ff3951cb62d74bd770dce27
不過(guò)很遺憾,最后這種方案失敗了,一直出現(xiàn)報(bào)錯(cuò)。找了半天也沒(méi)有找到解決辦法,希望知道原因的小伙伴告知我一下。
準(zhǔn)備工作
首先要找一份第三方維護(hù)的你的手機(jī)內(nèi)核的源碼,如lineageOS,PixelExperience等。這些內(nèi)核代碼熱度較高,更新頻繁,有什么bug馬上就被修復(fù)了,編譯的時(shí)候成功率較大。
另外注意,如果是小米手機(jī),最好不要用小米官方github上的內(nèi)核。本人親身體會(huì),編譯過(guò)程不僅一堆BUG,刷入系統(tǒng)后還開(kāi)不了機(jī)。后來(lái)看到看雪的帖子,很多人也遇到了同樣的情況:https://bbs.pediy.com/thread-262263.htm
經(jīng)過(guò)一番查找對(duì)比,最后選擇以lineageOS維護(hù)的小米6(sagit)的內(nèi)核源碼作為基礎(chǔ):https://github.com/LineageOS/android_kernel_xiaomi_msm8998
git clone https://github.com/LineageOS/android_kernel_xiaomi_msm8998 --depth=1
sagit是小米6的手機(jī)代號(hào),這個(gè)代號(hào)獨(dú)一無(wú)二,可以百度搜一下自己手機(jī)的對(duì)應(yīng)代號(hào)。
修改內(nèi)核
我們首先不做修改,編譯一次看報(bào)不報(bào)錯(cuò)。
cd ./android_kernel_xiaomi_msm8998
sudo apt install build-essential openssl pkg-config libssl-dev libncurses5-dev pkg-config minizip libelf-dev flex bison libc6-dev libidn11-dev rsync bc liblz4-tool
sudo apt install gcc-aarch64-linux-gnu dpkg-dev dpkg git
export ARCH=arm64
export SUBARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make O=out sagit_defconfig
make O=out -j$(nproc)
error:CROSS_COMPILE_ARM32 not defined or empty

kernel config 里面刪掉 CONFIG_COMPAT_VDSO
error: statement with no effect [-Werror=unused-value]
../drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg.c: In function ‘hdd_cfg_print’:
../drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_cfg.c:6896:43: error: statement with no effect [-Werror=unused-value]
error: ‘staid’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
911 | hdd_dhcp_indication(pAdapter, staid, skb, QDF_RX);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CC drivers/soc/qcom/early_random.o
CC drivers/video/fbdev/msm/mdss_mdp_ctl.o
CC drivers/video/fbdev/msm/mdss_mdp_pipe.o
臨時(shí)解決辦法: 增加 -Wno-error=unused-value -Wno-error=maybe-uninitialized,見(jiàn)一種加一種。最后覺(jué)得太麻煩了,直接Makefile里增加-w選項(xiàng),屏蔽所有警告。
開(kāi)啟內(nèi)核支持
安裝termux。這里為了控制方便我開(kāi)啟了ssh,用電腦連接上去操作。然后下載check腳本看缺少哪些內(nèi)核選項(xiàng)。
pkg install tsu
pkg install wget
wget https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
chmod +x check-config.sh
sed -i '1s_.*_#!/data/data/com.termux/files/usr/bin/bash_' check-config.sh
sudo ./check-config.sh

Generally Necessary下面必須要全部是綠色才有可能安裝Docker,這里看到還是有很多內(nèi)核選項(xiàng)是缺失的。
常規(guī)的修改內(nèi)核選項(xiàng)是需要通過(guò)menuconfig,但是缺的太多了,一個(gè)個(gè)手動(dòng)開(kāi)啟很麻煩。我們可以用一個(gè)偷懶的方法:直接編輯defconfig文件,手動(dòng)添加進(jìn)去??梢园凑湛岚采系囊粋€(gè)帖子來(lái)操作:
vim arch/arm64/configs/sagit_defcofig
加入以下內(nèi)容,并保存。
CONFIG_NAMESPACES=y
CONFIG_NET_NS=y
CONFIG_PID_NS=y
CONFIG_IPC_NS=y
CONFIG_UTS_NS=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_SCHED=y
CONFIG_CPUSETS=y
CONFIG_MEMCG=y
CONFIG_KEYS=y
CONFIG_VETH=y
CONFIG_BRIDGE=y
CONFIG_BRIDGE_NETFILTER=y
CONFIG_IP_NF_FILTER=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
CONFIG_NETFILTER_XT_MATCH_IPVS=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_IP_NF_NAT=y
CONFIG_NF_NAT=y
CONFIG_POSIX_MQUEUE=y
CONFIG_NF_NAT_IPV4=y
CONFIG_NF_NAT_NEEDED=y
CONFIG_CGROUP_BPF=y
CONFIG_USER_NS=y
CONFIG_SECCOMP=y
CONFIG_SECCOMP_FILTER=y
CONFIG_CGROUP_PIDS=y
CONFIG_MEMCG_SWAP=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_IOSCHED_CFQ=y
CONFIG_CFQ_GROUP_IOSCHED=y
CONFIG_BLK_CGROUP=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_NET_CLS_CGROUP=y
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_FAIR_GROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_VS=y
CONFIG_IP_VS_NFCT=y
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_RR=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_APPARMOR=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_VXLAN=y CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_CRYPTO=y CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_SEQIV=y
CONFIG_CRYPTO_GHASH=y CONFIG_XFRM=y
CONFIG_XFRM_USER=y
CONFIG_XFRM_ALGO=y
CONFIG_INET_ESP=y
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_IPVLAN=y
CONFIG_MACVLAN=y
CONFIG_DUMMY=y
CONFIG_NF_NAT_FTP=y
CONFIG_NF_CONNTRACK_FTP=y
CONFIG_NF_NAT_TFTP=y
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_AUFS_FS=y
CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_THIN_PROVISIONING=y
CONFIG_OVERLAY_FS=y
但是注意,這里是有坑的:按照這個(gè)帖子中的操作后刷入內(nèi)核,再跑一遍check腳本會(huì)發(fā)現(xiàn)還是會(huì)有missing,原因是部分內(nèi)核選項(xiàng)需要依賴另外的選項(xiàng),而這些另外的依賴沒(méi)有開(kāi)啟,所以還是missing的狀態(tài)。

所以最完整的做法,是加入之后再手動(dòng)make menuconfig,看哪些缺失開(kāi)哪些。

rm -rf out
make O=out sagit_defconfig
make O=out menuconfig
make O=out savedefconfig
make O=out -j$(nproc)
經(jīng)過(guò)等待之后,如果出現(xiàn)如下提示,就說(shuō)明我們的內(nèi)核已經(jīng)編譯好了。

刷入內(nèi)核
刷入內(nèi)核使用的是Anykernel3,詳細(xì)可以看這篇教程:https://www.akr-developers.com/d/125
修改完配置文件后,把上一步arch/arm64/boot/Image.gz-dtb放到Anykernel3的根目錄下,然后在根目錄中執(zhí)行zip -r <壓縮包名.zip> *即可
最后通過(guò)twrp:adb sideload xxx.zip刷入內(nèi)核。

Termux安裝docker
Termux里面再跑一邊check腳本,保證Generally Necessary是綠的。然后進(jìn)入下一步:安裝Docker。
通過(guò)Termux執(zhí)行以下命令
apt update && apt upgrade -y
pkg install root-repo
pkg install golang make cmake ndk-multilib tsu tmux docker
然后編譯tini
mkdir $TMPDIR/docker-build
$ cd $TMPDIR/docker-build
$ wget https://github.com/krallin/tini/archive/v0.19.0.tar.gz
$ tar xf v0.19.0.tar.gz
$ cd tini-0.19.0
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PREFIX ..
$ make -j8
$ make install
$ ln -s $PREFIX/bin/tini-static $PREFIX/bin/docker-init
編譯的時(shí)候會(huì)有一個(gè)報(bào)錯(cuò)
/data/data/com.termux/files/usr/tmp/docker-build/tini-0.19.0/src/tini.c:434:19: error: a function declaration without a prototype is deprecated in all versions of C [-Werror,-Wstrict-prototypes]
void reaper_check () {
^
void
手動(dòng)修一下,在報(bào)錯(cuò)的地方加一個(gè)void,或者make的時(shí)候加入gcc參數(shù)忽略報(bào)錯(cuò)。

啟動(dòng)Docker
sudo dockerd --iptables=false &>/dev/null &
測(cè)試Docker是否正常運(yùn)行
sudo docker run hello-world
這里我的設(shè)備報(bào)錯(cuò)了,搜了很久也沒(méi)找到原因跟解決辦法,在老外帖子下面的評(píng)論區(qū)也有人回復(fù)說(shuō)遇到了一模一樣的情況。無(wú)奈只好放棄。
報(bào)錯(cuò)日志
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "mqueue" to rootfs at "/dev/mqueue": mount mqueue:/dev/mqueue (via /proc/self/fd/6), flags: 0xe: device or resource busy: unknown.

方案二:刷入postmarkOS
安卓?jī)?nèi)核閹割的東西太多了,鬼知道改了什么東西。修改安卓?jī)?nèi)核的方式行不通,只能轉(zhuǎn)換思路。那么能否在手機(jī)上去安裝一個(gè)完整的Linux呢,當(dāng)然是可以的,那就是postmarkOS。
postmarkOS是一個(gè)運(yùn)行在手機(jī)上的Linux系統(tǒng),基于Alpine Linux,比Ubuntu Touch更為接近原始的Linux,并且盡量使用Mainline kernel。官網(wǎng):https://wiki.postmarketos.org/
準(zhǔn)備工作
之前研究這個(gè)項(xiàng)目的時(shí)候,官方還沒(méi)有合并小米6的分支:https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3134, 只能手動(dòng)本地編譯一波。當(dāng)時(shí)查看底層代碼后發(fā)現(xiàn)postmarketOS官方的安裝工具pmbootstrap對(duì)本地開(kāi)發(fā)分支不太友好,例如構(gòu)建rootfs下載驅(qū)動(dòng)的時(shí)候,會(huì)硬編碼從pmos的gitlab項(xiàng)目去拉取驅(qū)動(dòng),而我們使用的是本地修改的分支,當(dāng)然是下載不到的。后來(lái)又魔改了一波pmbootstrap的代碼,將硬編碼的部分改為本地獲取。不過(guò)網(wǎng)上對(duì)于pmOS移植這塊的文檔很少,也可能是我操作的姿勢(shì)不對(duì),最后勉強(qiáng)build好了一個(gè)zip,刷入系統(tǒng)但是開(kāi)不了機(jī)。
不過(guò)好消息是兩周前官方已經(jīng)把小米6的分支合并進(jìn)去了,也就意味著我們不用自己去手動(dòng)移植了。更詳細(xì)的教程可以參考這一篇:https://www.cnblogs.com/hongshao/p/14351045.html。
首先在Linux中執(zhí)行以下命令:
pip3 install --user pmbootstrap
pip3 install --user --upgrade pmbootstrap
pmbootstrap init

刷入postmarkOS
在官方的教程中 ,默認(rèn)是通過(guò)pmbootstrap flasher 命令直接刷入,但是我選擇了制作卡刷包來(lái)刷入系統(tǒng),這樣卡刷包可以后期反復(fù)使用:https://wiki.postmarketos.org/wiki/Installation_from_recovery_mode
注意一定要加 --recovery-install-partition=data參數(shù),不然刷入后你的分區(qū)會(huì)非常的小。
pmbootstrap install --android-recovery-zip --recovery-install-partition=data
pmbootstrap export
cd $(dirname $(readlink /tmp/postmarketOS-export/pmos-*.zip))
這里有一個(gè)坑,拿到zip文件后這個(gè)時(shí)候還不能直接adb sideload,會(huì)報(bào)錯(cuò):

正確的做法是要先進(jìn)入twrp,然后unmount所有的分區(qū),再進(jìn)行sideload刷入。
adb sideload pmos-xiaomi-sagit.zip
刷完后twrp會(huì)有一個(gè)提示:掛載data失敗。別著急,因?yàn)閐ata分區(qū)已經(jīng)被我們修改了,其實(shí)系統(tǒng)已經(jīng)刷入完成了。
重啟就可以進(jìn)入我們刷好的postmarketOS系統(tǒng)了。

安裝RNDIS驅(qū)動(dòng)
首先按照官方的教程,開(kāi)啟手機(jī)的SSH:https://wiki.postmarketos.org/wiki/SSH。
這里出現(xiàn)了一個(gè)問(wèn)題,開(kāi)啟SSH服務(wù)后連接電腦,提示無(wú)法識(shí)別設(shè)備,原因是電腦上沒(méi)有對(duì)應(yīng)的驅(qū)動(dòng),需要按照我下面的步驟來(lái)安裝。



安裝完畢后就可以正常識(shí)別手機(jī)設(shè)備了,成功連接上了SSH。

通過(guò)USB共享網(wǎng)絡(luò)
當(dāng)前的系統(tǒng)版本W(wǎng)IFI有BUG不能用,修復(fù)的分支官方還沒(méi)有merge:https://gitlab.com/msm8998-mainline/linux/-/merge_requests/2,所以我們要想一些另外的辦法來(lái)聯(lián)網(wǎng)。
按照官方教程 https://wiki.postmarketos.org/wiki/USB_Internet,在Ubuntu中以root權(quán)限輸入以下命令:
sysctl net.ipv4.ip_forward=1
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s 172.16.42.0/24 -j ACCEPT
iptables -A POSTROUTING -t nat -j MASQUERADE -s 172.16.42.0/24
iptables-save
ufw disable
然后ping一下百度試試,這個(gè)時(shí)候已經(jīng)可以聯(lián)網(wǎng)了。

Hello from Docker!
終于要進(jìn)入安裝Docker的環(huán)節(jié)了,首先更新一下系統(tǒng),然后apk add docker

Hello from Docker! 我們終于成功在手機(jī)上運(yùn)行了Docker。

看下htop

空間使用

玩法擴(kuò)展
小米6的處理器是驍龍835,8核,10nm,2.45GHz,對(duì)比樹(shù)莓派4B CPU BCM2711 4核 28nm 1.5 GHz。舊手機(jī)拿來(lái)當(dāng)開(kāi)發(fā)板,這不比樹(shù)莓派香?觸摸屏WIFI藍(lán)牙紅外都配齊了。
根據(jù)cpubenchmark的跑分對(duì)比,835的跑分幾乎是樹(shù)莓派CPU的三倍:https://www.cpubenchmark.net/compare/4297vs3919/BCM2711-vs-Qualcomm-Technologies,-Inc-MSM8998

不過(guò)美中不足的是小米的手機(jī)一直是USB2.0,IO簡(jiǎn)直是龜速。。。外接個(gè)硬盤(pán)也就十幾M速度。。。跑重IO的業(yè)務(wù)是不太行了。
現(xiàn)在,我們的小米6搖身一變,變成了一個(gè)性能還不錯(cuò)的Linux開(kāi)發(fā)板。我們可以安裝php+nginx搭個(gè)博客,或者在家里跑Homeassistant等等。
作為一個(gè)安全從業(yè)者,還是會(huì)想是否能在安全場(chǎng)景下有所應(yīng)用。想象一個(gè)場(chǎng)景:在近源滲透的時(shí)候,如果拿著電腦或者樹(shù)莓派,一眼就看起來(lái)很奇怪。但是如果我們把這些東西裝進(jìn)手機(jī),這樣偽裝性就會(huì)大大增加,之前也有師傅研究過(guò)這個(gè)課題:https://www.anquanke.com/post/id/204544。 Kali雖然有NetHunter,但是受限于安卓系統(tǒng),NetHunter還是很難發(fā)揮出原生Kali的所有功能;而安裝pmOS后的手機(jī)是一個(gè)完整的Linux,理論上能夠?qū)崿F(xiàn)所有原生Kali的功能。不過(guò)以上只是猜想,具體可行性還有待驗(yàn)證。
最后
終于給手機(jī)安裝上了Docker,不過(guò)然后呢?好像也并不會(huì)真的用到它。不過(guò)刷機(jī)享受的就是折騰的過(guò)程,在手機(jī)上跑Docker,不覺(jué)得很酷嗎?作為一名理工男我覺(jué)得這太酷了,很符合我對(duì)未來(lái)生活的想象,科技并帶著趣味。