menhera.sh/menhera.sh

266 lines
7.4 KiB
Bash
Raw Normal View History

2019-04-09 15:51:15 +08:00
#!/bin/bash
2019-04-09 15:54:26 +08:00
set -Eeuo pipefail
2020-03-20 23:15:31 +08:00
# disable command path hashing as we are going to move fast and break things
set +h
2019-04-09 15:51:15 +08:00
# config
WORKDIR="/tmp/menhera"
2021-05-14 23:07:47 +08:00
MIRROR="https://iso-ultralite.meson.cc/archlinux"
ROOTFS="${MIRROR}/iso/latest/arch/x86_64/airootfs.sfs"
2019-04-09 15:51:15 +08:00
declare -A ARCH_MAP=(
["x86_64"]="amd64"
)
2019-04-09 15:51:15 +08:00
# internal global variables
OLDROOT="/"
NEWROOT=""
MACHINE_TYPE=$(uname -m)
ARCH_ID=${ARCH_MAP[$MACHINE_TYPE]:-$MACHINE_TYPE}
2021-03-09 11:16:36 +08:00
# fix possible PATH problems
2019-06-04 15:33:13 +08:00
export PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
menhera::reset_sshd_config() {
cat > /etc/ssh/sshd_config <<EOF
PermitRootLogin yes
AcceptEnv LANG LC_*
EOF
}
2019-04-13 11:05:42 +08:00
# environment compatibility
2019-09-01 22:46:31 +08:00
menhera::__compat_restart_ssh() {
2019-04-13 11:05:42 +08:00
if [ -x "$(command -v systemctl)" ]; then
systemctl daemon-reload
if ! systemctl restart sshd; then
echo "SSH daemon start failed, try resetting config..."
menhera::reset_sshd_config
if ! systemctl restart sshd; then
echo "SSH daemon fail to start, dropping you to a shell..."
sh
fi
fi
elif false && [ -x "$(command -v service)" ]; then # there is no such thing in archlinux
if ! service ssh restart; then
echo "SSH daemon start failed, try resetting config..."
menhera::reset_sshd_config
if ! service ssh restart; then
echo "SSH daemon fail to start, dropping you to a shell..."
sh
fi
fi
2019-04-13 11:05:42 +08:00
else
echo "ERROR: Cannot restart SSH server, init system not recoginzed" >&2
2019-04-13 11:05:42 +08:00
exit 1
fi
}
2019-09-01 22:46:31 +08:00
menhera::__compat_reload_init() {
2019-04-13 11:05:42 +08:00
if [ -x "$(command -v systemctl)" ]; then
systemctl daemon-reexec
elif [ -x "$(command -v telinit)" ]; then
telinit u
else
2021-03-09 11:17:29 +08:00
echo "ERROR: Cannot re-exec init, init system not recognized" >&2
2019-04-13 11:05:42 +08:00
exit 1
fi
}
# helper functions
2019-04-09 15:51:15 +08:00
# https://stackoverflow.com/a/3232082/2646069
2019-09-01 22:46:31 +08:00
menhera::confirm() {
2019-04-09 15:51:15 +08:00
# call with a prompt string or use a default
read -r -p "${1:-Are you sure? [y/N]} " response
case "$response" in
[yY][eE][sS]|[yY])
true
;;
*)
false
;;
esac
}
2019-04-13 11:05:42 +08:00
# jobs
2019-09-01 22:46:31 +08:00
menhera::get_rootfs() {
if [ -z "${ROOTFS}" ]; then
2019-04-09 16:59:41 +08:00
echo "Getting rootfs URL..."
2019-04-09 17:59:37 +08:00
# forgive me for parsing HTML with these shit
# and hope it works
ROOTFS_TIME=$(wget -qO- --show-progress "https://images.linuxcontainers.org/images/debian/buster/${ARCH_ID}/default/?C=M;O=D" | grep -oP '(\d{8}_\d{2}:\d{2})' | head -n 1)
ROOTFS="https://images.linuxcontainers.org/images/debian/buster/${ARCH_ID}/default/${ROOTFS_TIME}/rootfs.squashfs"
else
2019-04-09 16:59:41 +08:00
echo "\$ROOTFS is set to '$ROOTFS'"
fi
}
2019-09-01 22:46:31 +08:00
menhera::sync_filesystem() {
2019-04-09 15:51:15 +08:00
echo "Syncing..."
sync
sync
}
2019-09-01 22:46:31 +08:00
menhera::prepare_environment() {
2019-04-09 15:51:15 +08:00
echo "Loading kernel modules..."
modprobe overlay
modprobe squashfs
2019-04-09 17:58:16 +08:00
sysctl kernel.panic=10
2019-04-09 15:51:15 +08:00
echo "Creating workspace in '${WORKDIR}'..."
# workspace
mkdir -p "${WORKDIR}"
2019-09-02 00:02:09 +08:00
mount -t tmpfs -o size=100% tmpfs "${WORKDIR}"
2019-04-09 15:51:15 +08:00
# new rootfs
mkdir -p "${WORKDIR}/newroot"
# readonly part of new rootfs
mkdir -p "${WORKDIR}/newrootro"
# writeable part of new rootfs
mkdir -p "${WORKDIR}/newrootrw"
# overlayfs workdir
mkdir -p "${WORKDIR}/overlayfs_workdir"
echo "Downloading temporary rootfs..."
2019-09-01 23:35:00 +08:00
wget -q --show-progress -O "${WORKDIR}/rootfs.squashfs" "${ROOTFS}"
2019-04-09 15:51:15 +08:00
}
2019-09-01 22:46:31 +08:00
menhera::mount_new_rootfs() {
2019-04-09 15:51:15 +08:00
echo "Mounting temporary rootfs..."
mount -t squashfs "${WORKDIR}/rootfs.squashfs" "${WORKDIR}/newrootro"
mount -t overlay overlay -o rw,lowerdir="${WORKDIR}/newrootro",upperdir="${WORKDIR}/newrootrw",workdir="${WORKDIR}/overlayfs_workdir" "${WORKDIR}/newroot"
NEWROOT="${WORKDIR}/newroot"
}
2019-09-01 22:46:31 +08:00
menhera::install_software() {
return
2019-04-09 15:51:15 +08:00
echo "Installing OpenSSH Server into new rootfs..."
# disable APT cache
echo -e 'Dir::Cache "";\nDir::Cache::archives "";' > "${NEWROOT}/etc/apt/apt.conf.d/00_disable-cache-directories"
2019-04-09 15:51:15 +08:00
DEBIAN_FRONTEND=noninteractive chroot "${NEWROOT}" apt-get update -y
DEBIAN_FRONTEND=noninteractive chroot "${NEWROOT}" apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install -y openssh-server
2019-04-09 15:51:15 +08:00
}
2019-09-01 22:46:31 +08:00
menhera::copy_config() {
2019-04-09 15:51:15 +08:00
echo "Copying important config into new rootfs..."
2020-03-02 21:42:55 +08:00
! cp -axL "${OLDROOT}/etc/resolv.conf" "${NEWROOT}/etc"
2019-04-09 15:59:03 +08:00
! cp -axr "${OLDROOT}/etc/ssh" "${NEWROOT}/etc"
! cp -ax "${OLDROOT}/etc/"{passwd,shadow} "${NEWROOT}/etc"
! cp -axr "${OLDROOT}/root/.ssh" "${NEWROOT}/root"
2019-04-09 15:51:15 +08:00
chroot "${NEWROOT}" chsh -s /bin/bash root
2019-04-09 16:25:26 +08:00
cat > "${NEWROOT}/etc/motd" <<EOF
2019-04-09 18:15:07 +08:00
Download menhera.sh at https://github.com/Jamesits/menhera.sh
2019-04-09 17:08:29 +08:00
!!!NOTICE!!!
2019-04-09 16:25:26 +08:00
This is a minimal RAM system created by menhera.sh. Feel free to format your disk, but don't blame anyone
except yourself if you lost important files or your system is broken.
2019-04-09 18:15:07 +08:00
If you think you've done something wrong, reboot immediately -- there is still hope.
Your original rootfs is at /mnt/oldroot. Be careful dealing with it.
2019-04-09 16:25:26 +08:00
Have a lot of fun...
EOF
2019-04-09 15:51:15 +08:00
}
2019-09-01 22:46:31 +08:00
menhera::swap_root() {
2019-04-09 15:51:15 +08:00
echo "Swapping rootfs..."
# prepare future mount point for our old rootfs
mkdir -p "${WORKDIR}/newroot/mnt/oldroot"
mount --make-rprivate /
# swap root
pivot_root "${WORKDIR}/newroot" "${WORKDIR}/newroot/mnt/oldroot"
OLDROOT="/mnt/oldroot"
NEWROOT="/"
# move mounts
for i in dev proc sys run; do
if [ -d "${OLDROOT}/$i" ]; then
mount --move "${OLDROOT}/$i" "${NEWROOT}/$i"
fi
done
2019-09-02 00:02:09 +08:00
mount -t tmpfs -o size=100% tmpfs "${NEWROOT}/tmp"
2019-04-09 15:51:15 +08:00
2019-04-09 16:34:06 +08:00
mkdir -p "${WORKDIR}"
mount --move "${OLDROOT}/${WORKDIR}" "${WORKDIR}"
2019-04-09 15:51:15 +08:00
echo "Restarting SSH daemon..."
2019-09-01 22:59:02 +08:00
menhera::__compat_restart_ssh
}
2019-04-09 15:51:15 +08:00
2019-09-01 22:46:31 +08:00
menhera::clear_processes() {
2019-04-09 16:25:26 +08:00
echo "Disabling swap..."
swapoff -a
echo "Restarting init process..."
2019-09-01 22:59:02 +08:00
menhera::__compat_reload_init
2019-04-13 11:05:42 +08:00
# hope 15s is enough
2019-04-09 15:51:15 +08:00
sleep 15
echo "Killing all programs still using the old root..."
fuser -kvm "${OLDROOT}" -15
2019-04-09 16:25:26 +08:00
# in most cases the parent process of this script will be killed, so goodbye
2019-04-09 16:12:17 +08:00
}
2019-04-09 15:51:15 +08:00
2019-04-09 18:17:53 +08:00
# main procedure
2019-08-24 14:42:32 +08:00
LIBRARY_ONLY=0
while test $# -gt 0
do
case "$1" in
--lib) LIBRARY_ONLY=1
;;
esac
shift
done
if [[ $LIBRARY_ONLY -eq 1 ]]; then
# acting as a library only
2019-08-24 15:03:00 +08:00
return 0
2019-08-24 14:42:32 +08:00
fi
2019-04-09 18:17:53 +08:00
2019-04-09 15:51:15 +08:00
if [[ $EUID -ne 0 ]]; then
2019-04-09 16:12:17 +08:00
echo "This script must be run as root"
exit 1
2019-04-09 15:51:15 +08:00
fi
echo -e "We will start a temporary RAM system as your recovery environment."
echo -e "Note that this script will kill programs and umount filesystems without prompting."
echo -e "Please confirm:"
echo -e "\tYou have closed all programs you can, and backed up all important data"
echo -e "\tYou can SSH into your system as root user"
2019-09-01 22:49:56 +08:00
menhera::confirm || exit -1
2019-04-09 15:51:15 +08:00
2019-09-01 22:46:31 +08:00
menhera::get_rootfs
menhera::sync_filesystem
2019-04-09 15:51:15 +08:00
2019-09-01 22:46:31 +08:00
menhera::prepare_environment
menhera::mount_new_rootfs
menhera::copy_config
menhera::install_software
menhera::swap_root
2019-04-09 15:51:15 +08:00
2019-04-10 10:01:22 +08:00
echo -e "If you are connecting from SSH, please create a second session to this host use root and"
echo -e "confirm you can get a shell."
2019-04-09 15:51:15 +08:00
echo -e "After your confirmation, we are going to kill the old SSH server."
2019-09-01 22:49:56 +08:00
if menhera::confirm; then
2019-09-01 22:46:31 +08:00
menhera::clear_processes
2019-04-10 10:01:22 +08:00
else
echo -e "Please manually issue a reboot to recover your old OS. If you believe there is a bug in menhera.sh, "
echo -e "raise a ticket at https://github.com/Jamesits/menhera.sh/issues ."
exit 1
fi