proot multistrap best - hanyong/note GitHub Wiki
proot multistrap 最佳实践
更新:2017-12-15 环境:ubuntu 16.04
基本观念:
-
先搭建一个最小的可用环境, 其他软件包使用 apt-get 按需安装。 配合 ubuntu 的 command-not-found,很容易知道需要安装的软件包。
-
为安全考虑,可使用 proot 尽量避免使用真实 root 权限。
最小软件包整理
许多软件包的安装配置都依赖一些基础软件包,但是其依赖说明上没有包含这些依赖,可能它们假设这些依赖必定是已经存在的。
如许多软件包的安装配置都会使用 sed, sh, bash 等,反过来 bash 又依赖这些软件包,如果严格声明的话将会产生依赖循环。
multistrap 等默认会将 Priority: required
的软件包全部装上,所以通常不会有问题。
但其中包含许多我们不需要的软件包。
经过不断试错整理,以下为必须最小软件包集合:
libc-bin dash coreutils findutils mawk sed grep bash base-passwd base-files
我们可以在此基础上添加 dpkg 或 apt 得到具备安装软件包能力的最小基础环境。
-
libc-bin 到 grep 为 dpkg 必须的基本包。
-
许多软件配置脚本和正常运行还依赖 bash base-passwd base-files 。
-
locales 也是很基本的软件包,其又依赖 gzip,这些软件包可以不加入最小环境,稍后使用 apt 安装。
-
稍后可使用如下命令查询 required 软件包列表:
aptitude search '?Priority(required) ?Architecture(amd64)' -F '%p' ```
fakeinit 脚本
ubuntu 14.04 测试,其他版本未测试。
一些软件包安装过程会调用 initctl 等,可设置 fakeinit 。 最小包包含 initscripts(?),第一阶段后执行:
TARGET=. sbin/fakeinit.sh
添加 fakeinit 脚本:
cat > sbin/fakeinit.sh <<'EOF'
#!/bin/sh
if ! [ -e "$TARGET/usr/sbin/policy-rc.d.REAL" ]; then
test -e "$TARGET/usr/sbin/policy-rc.d" || touch "$TARGET/usr/sbin/policy-rc.d"
mv -T "$TARGET/usr/sbin/policy-rc.d" "$TARGET/usr/sbin/policy-rc.d.REAL"
echo \
"#!/bin/sh
exit 101" > "$TARGET/usr/sbin/policy-rc.d"
chmod 755 "$TARGET/usr/sbin/policy-rc.d"
fi
if ! [ -e "$TARGET/sbin/start-stop-daemon.REAL" ]; then
mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL"
echo \
"#!/bin/sh
echo
echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon"
chmod 755 "$TARGET/sbin/start-stop-daemon"
fi
if ! [ -e "$TARGET/sbin/initctl.REAL" ]; then
if [ -x "$TARGET/sbin/initctl" ]; then
mv "$TARGET/sbin/initctl" "$TARGET/sbin/initctl.REAL"
echo \
"#!/bin/sh
if [ \"\$1\" = version ]; then exec /sbin/initctl.REAL \"\$@\"; fi
echo
echo \"Warning: Fake initctl called, doing nothing\"" > "$TARGET/sbin/initctl"
chmod 755 "$TARGET/sbin/initctl"
fi
fi
EOF
chmod 755 sbin/fakeinit.sh
cat > sbin/unfakeinit.sh <<'EOF'
#!/bin/sh
if [ -x "$TARGET/sbin/initctl.REAL" ]; then
mv "$TARGET/sbin/initctl.REAL" "$TARGET/sbin/initctl"
fi
if [ -x "$TARGET/sbin/start-stop-daemon.REAL" ]; then
mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon"
fi
if [ -e "$TARGET/usr/sbin/policy-rc.d.REAL" ]; then
mv -T "$TARGET/usr/sbin/policy-rc.d.REAL" "$TARGET/usr/sbin/policy-rc.d"
test -z "$(cat "$TARGET/usr/sbin/policy-rc.d")" && rm -f "$TARGET/usr/sbin/policy-rc.d"
fi
EOF
chmod 755 sbin/unfakeinit.sh
multistrap 安装
准备一个最小配置文件 ubu1604.conf 如下:
[General]
bootstrap=Ubuntu
aptsources=Ubuntu
unpack=true
omitrequired=true
omitpreinst=true
ignorenativearch=true
allowrecommends=false
arch=
multiarch=
[Ubuntu]
#packages=libc-bin dash coreutils findutils mawk sed grep bash base-passwd base-files dpkg
packages=libc-bin dash coreutils findutils mawk sed grep bash base-passwd base-files apt
source=http://cn.archive.ubuntu.com/ubuntu/
keyring=
suite=xenial
omitdebsrc=true
components=main
- 配置名必须全部为小写字母,读起来有点吃力。
- 初始安装尽量少的包,配置
omitrequired
,手动指定包列表。 - 宿主机上正常已安装 ubuntu-keyring,apt 已依赖 ubuntu-keyring,不必配置
keyring
,否则 multistrap 会在宿主机上请求 root 权限进行下载 keyring 等操作。 - multistrap 执行 preinst 或安装本地包需要 root 权限执行 chroot,配置
omitpreinst
和ignorenativearch
避免此操作。 - multistrap 未执行 preinst,则必须手动执行。参考
man multistrap
相关说明。 multiarch
影响安装后的系统 dpkg 配置,参考dpkg --print-foreign-architectures
。- multistrap 支持配置
cleanup
清理下载的软件包,但我们稍后依赖dpkg -i
安装软件包,不要清理。事后可执行apt-get clean
清理。
第一阶段:
rm -rf ubu1604/ && env LANG=C LANGUAGE= multistrap -d ubu1604/ -f ubu1604.conf | tee multistrap.log && ln multistrap-config.sh packages.txt ubu1604/
第二阶段:
env LANG=C LANGUAGE= PATH=/usr/sbin:/usr/bin:/sbin:/bin proot -r ubu1604/ -b /proc -b /sys -b /dev -b /dev/pts -w / -0 bash multistrap-config.sh
-
参考
man multistrap
说明,可手动执行 preinst 后调用dpkg --configure -a
。 但多个 preinst 需要确定顺序,configure 也因为顺序和依赖问题失败,所以考虑写一个脚本完成第二阶段配置。 -
如何决定配置软件包的顺序?主要依赖下载包的顺序,有失败的地方再手动调整。 从 multistrap 操作标准输出日志解析下载包顺序:
cat multistrap.log | sed '1,/^After this operation/ d' | grep -P '^Get:\d+' | awk '{print $5}' > packages.txt ```
首先尝试配置 dpkg 最小环境:
-
配置
libc6
时报错其依赖libgcc1
,先配置libgcc1
又报错其依赖libc6
,循环依赖。两个包同时配置可解决此问题。 -
配置
base-files
时报错:
Setting up base-files (9.4ubuntu4) ... chown: invalid user: 'root:root' dpkg: error processing package base-files (--configure): subprocess installed post-installation script returned error exit status 1 Errors were encountered while processing: base-files ```
用户信息 `root:root` 等应该是 `base-passwd` 提供的,将其移动到 base-passwd 之后。
- 配置
bash
时报错依赖 base-files,将其移动到 base-files 之后。
然后尝试配置 apt 最小环境:
-
执行
debconf
的 preinst 时报错:
dpkg-maintscript-helper: error: couldn't identify the package ```
看了下这个包在 dpkg 之后,dpkg 环境应该已经完全安装配置完成。
dpkg 之后的软件包尝试使用 dpkg -i
直接从 deb 包安装。
-
dpkg -i
安装passwd
时报错如下:
Setting up passwd (1:4.2-3.1ubuntu5) ... chown: cannot access '/etc/shadow': No such file or directory chown: cannot access '/etc/gshadow': No such file or directory chmod: cannot access '/etc/shadow': No such file or directory chmod: cannot access '/etc/gshadow': No such file or directory Please correct the error and rerun `/sbin/shadowconfig on' dpkg: error processing package passwd (--install): subprocess installed post-installation script returned error exit status 1 Errors were encountered while processing: passwd ```
查了一下 `/etc/shadow` 等文件应该就是由 passwd 软件包管理的。
看日志提示以上错误发生在 /sbin/shadowconfig on
,这个命令应该就是生成 shadow 等文件。
看了下这个文件是个脚本,其中调用了 pwconv
,而看 man pwconv
其就是生成 shadow 文件的。
```
The pwconv command creates shadow from passwd and an optionally existing shadow. ```
测了一下果然 pwconv 执行失败(退出码1),看文档其有使用 `/etc/login.defs` 中的配置,此文件来自包 `login`,尝试添加此软件包。
- 安装 login 时也发生报错
chown: invalid user: 'root:root'
,尝试将其移动到 base-files 之后,安装成功。
总结:
libc6
,libgcc1
同时 configure 。- 调整
base-files
在base-passwd
之后。 - 调整
bash
在base-files
之后。 - apt 最小环境需手动添加
login
软件包,调整到base-files
之后。
最终配置脚本 multistrap-config.sh 如下:
#!/bin/bash
# multistrap target environment second stage configure
set -e
export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
export LC_ALL=C LANGUAGE=C LANG=C
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
#cat multistrap.log | sed '1,/^After this operation/ d' | grep -P '^Get:\d+' | awk '{print $5}' > packages.txt
afterDpkg=false
cat packages.txt | while read line ; do
if test -z "${line}" ; then
continue
fi
if ! "${afterDpkg}" ; then
echo ">>>> process ${line}"
for e in ${line} ; do
if test -e /var/lib/dpkg/info/"${e}".preinst ; then
echo ">> preinst ${e}"
/var/lib/dpkg/info/"${e}".preinst install
fi
if test "${e}" = "dpkg" ; then
afterDpkg=true
fi
done
echo ">> configure ${line}"
dpkg --configure ${line}
else
echo ">>>> install ${line}"
debList=()
for e in ${line} ; do
debList=( "${debList[@]}" /var/cache/apt/archives/"${e}"_*.deb )
done
dpkg -i "${debList[@]}"
fi
done
#dpkg --configure -a
拷贝 resolv.conf
,使 proot 到目标环境后可访问网络。
```
rsync -avi -L /etc/resolv.conf ubu1604/etc/ ```
目标环境可移动 multistrap 设置的 apt sources 为默认配置:
mv /etc/apt/sources.list.d/multistrap-ubuntu.list /etc/apt/sources.list
基本配置和软件包安装
apt-get update
apt-get install locales gzip bzip2 xz-utils -y
locale-gen en_US.UTF-8 zh_CN.UTF-8 zh_CN.GBK zh_CN.GB18030
apt-get install aptitude -y
aptitude install command-not-found -y