Skip to main content

takeover

  • 将 VPS 替换为 AlpineLinux 环境
  • 用于平台不支持自定义镜像或者没有 AlpineLinux 的场景

准备环境

mkdir /takeover
mount -t tmpfs tmpfs /takeover

cd /takeover
mkdir -p etc
cp /etc/resolv.conf etc # 保留 DNS 配置

curl -LO https://raw.githubusercontent.com/marcan/takeover.sh/master/takeover.sh
curl -LO https://raw.githubusercontent.com/marcan/takeover.sh/master/fakeinit.c
curl -LO https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox

curl -LO https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/main/x86_64/apk-tools-static-2.14.6-r2.apk
mkdir apk-tools-static
tar zxvf apk-tools-static-*.apk -C .
./sbin/apk.static --version

chmod +x takeover.sh
chmod +x busybox
# apt update; apt install -y gcc # for debian/ubuntu
gcc fakeinit.c -o fakeinit -static # compile fakeinit as static
ldd fakeinit # ensure it's static: not a dynamic executable

# Setup the chroot environment
chroot /takeover /busybox sh
# --root /takeover
# apk.static -X http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.17/main --allow-untrusted --initdb add alpine-base
#apk.static -X http://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/main --allow-untrusted --initdb add alpine-base
apk.static -X http://dl-cdn.alpinelinux.org/latest-stable/main --allow-untrusted --initdb add alpine-base

# /etc/apk/repositories
# for China
# setup-apkrepos http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/main http://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/community
setup-apkrepos -c -1 # choose first mirror dl-cdn.alpinelinux.org
apk add shadow util-linux
# ln -s /usr/bin/passwd /bin/passwd # takeover.sh use this
setup-sshd -c openssh # 安装 ssh
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config # 允许 root 使用密码登录
exit # 退出 chroot

takeover

  • takeover 前注意 /etc/network/interfaces 是否是特殊情况
  • 完成 takeover 后原先系统的 init 就和 ssh 进入的环境没关系了
  • takeover 不会对原系统有任何影响
TO=/takeover
# e.g. systemd
OLD_INIT=$(readlink /proc/1/exe)
PORT=80

# 为 SSH 设置新的密码
./busybox chroot . /usr/bin/passwd
# 使用 busybox 准备环境
./busybox sh
rm -f etc/mtab
ln -s /proc/mounts etc/mtab
mkdir -p old_root
mount -t tmpfs tmp tmp
mount -t proc proc proc
mount -t sysfs sys sys
mount -t devtmpfs dev dev
mount --bind /dev/pts dev/pts
exit

# /dev/pts/0
TTY="$(./busybox tty)"

exec < "$TO/$TTY" > "$TO/$TTY" 2> "$TO/$TTY"
# 准备新的 init
./busybox cat > tmp/${OLD_INIT##*/} << EOF
#!${TO}/busybox sh

exec <"${TO}/${TTY}" >"${TO}/${TTY}" 2>"${TO}/${TTY}"
cd "${TO}"

./busybox echo "Init takeover successful"
./busybox echo "Pivoting root..."
./busybox mount --make-rprivate /
./busybox unshare -m
./busybox pivot_root . old_root
./busybox echo "Chrooting and running init..."
exec ./busybox chroot . /fakeinit
EOF
./busybox chmod +x tmp/${OLD_INIT##*/}

# 启动 SSH
# ssh root@HOSTNAME -p 80
./busybox chroot . /usr/bin/ssh-keygen -A
./busybox chroot . /usr/sbin/sshd -p $PORT -o PermitRootLogin=yes

./busybox mount --bind tmp/${OLD_INIT##*/} ${OLD_INIT} # 切换 init
telinit u # 重新加载 init

reinstall

# 进入新的 ssh 环境
ssh root@HOSTNAME -p 80

cleanup

  • 清理旧的进程
  • 最终 unmount /old_root
umount -R /old_root

setup

  • setup-disk 会把当前系统环境做到硬盘
  • 因此 apk add 的内容也会在安装后的系统里
apk add curl nano bash file util-linux
# setup /etc/network/interfaces 如果不是 DHCP 需要根据情况调整
setup-interfaces -a

# 确保网络启动
# ln -fs /etc/init.d/networking /etc/runlevels/boot
rc-update add networking boot

# 假设 /dev/vda 为安装盘
# 假设是在云平台因此使用 virt
ERASE_DISKS=/dev/vda setup-disk -m sys -s 0 -k virt /dev/vda

# 可以二次检查
mount /dev/vda2 /mnt
mount /dev/vda1 /mnt/boot
  • 重启进入新的系统

FAQ

pivot_root: (null): Resource busy

  • 需要修改脚本增加 unshare -m
./busybox unshare -m