1
0
mirror of https://github.com/marcan/takeover.sh.git synced 2020-11-18 19:38:40 -08:00

Initial commit

This commit is contained in:
Hector Martin 2017-02-11 01:21:32 +09:00
commit ca1e75afc2
3 changed files with 191 additions and 0 deletions

83
README.md Normal file
View File

@ -0,0 +1,83 @@
# takeover.sh
A script to completely take over a running Linux system remotely, allowing you
to log into an in-memory rescue environment, unmount the original root
filesystem, and do anything you want, all without rebooting. Replace one distro
with another without touching a physical console.
## WARNING WARNING WARNING WARNING
This is experimental. Do not use this script if you don't understand exactly
how it works. Do not use this script on any system you care about. Do not use
this script on any system you expect to be up. Do not run this script unless
you can afford to get physical access to fix a botched takeover. If anything
goes wrong, your system will most likely panic.
That said, this script will not (itself) make any permanent changes to your
existing root filesystem (assuming you run it from a tmpfs), so as long as you
can remotely reboot your box using an out-of-band mechanism, you *should* be OK.
But don't blame me if it eats your dog.
This script does not have any provisions for exiting *out* of the new
environment back into something sane. You *will* have to reboot when you're
done. If you get anything wrong, your machine won't boot. Tough luck.
This is not a guide for newbies. I'm deliberately not giving you commands you
can copy and paste. If you can't figure out what to do exactly without
handholding, this script is not for you.
## Compatibility
This script is designed for systems using sysvinit that support the `telinit u`
command to reload `/sbin/init`. If your system uses something else, you will
have to adapt it, or this might not work at all. You're on your own here.
You should always test this in a VM first. You can grab a tarball of your live
root filesystem, extract it into a VM image, get your VM up and running (boot
loader setup is left as an exercise for the reader), then try the process there
and see if it works. Hint: `mount --bind / /mnt` will get you a view of your
root filesystem on `/mnt` without any other filesystems that are mounted on top.
## Usage
You need to decide on what rescue environment you want. I recommend
[SystemRescueCD](https://www.system-rescue-cd.org/), which comes with many
useful tools (you have to loopmount the ISO and then use `unsquashfs`).
Obviously, whatever you pick has to fit into free RAM, with room to spare. If
your chosen rescue environment has `/lib/modules`, you may want to get rid of
it to save space, as its kernel modules won't be useful on the host kernel
anyway.
1. Create a directory `/takeover` on your target system and mount a tmpfs on it
2. Extract your rescue environment there. Make sure it works by chrooting into
it and running a few commands. Make sure you do not bork filesystem
permissions. Exit the chroot.
3. Grab a recent copy of `busybox` (statically linked) and put it in
`/takeover/busybox`. You can find binaries
[here](https://www.busybox.net/downloads/binaries/1.26.2-defconfig-multiarch/).
Make sure it works by trying something like `/takeover/busybox sh`.
4. Copy the contents of this repository into `/takeover`.
5. Compile `fakeinit.c`. It must be compiled such that it works inside the
takeover environment. If your rescue environment has `gcc`, you can just
compile it inside the chroot: `chroot /takeover gcc /fakeinit.c -o /fakeinit`.
Otherwise, you might want to statically link it.
6. Shut down as many services as you can on your host. `takeover.sh` will by
default set up an SSHd listening on port 80, though you may edit this in
the script.
7. Run `sh /takeover/takeover.sh` and follow the prompts.
If everything worked, congratulations! You may now use your new SSH session
to kill any remaining old daemons (`kill -9` is recommended to make sure they
don't try to do anything silly during shutdown), and then unmount all
filesystems under `/old_root`, including `/old_root` itself. You may want to
first copy `/old_root/lib/modules` into your new tmpfs in case you need any old
kernel modules.
You are now running entirely from RAM and should be able to do as you please.
Note that you may still have to clean up LVM volumes (`dmsetup` is your friend)
and similar before you can safely repartition your disk and install Gentoo
Linux, which is of course the whole reason you're doing this crazy thing to
begin with.
When you're done, unmount all filesystems, then `reboot -f` or `echo b >
/proc/sysrq-trigger` and cross your fingers.

21
fakeinit.c Normal file
View File

@ -0,0 +1,21 @@
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
sigset_t set;
int status, i;
for (i = 0; i < 64; i++)
close(i);
if (getpid() != 1) return 1;
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, 0);
for (;;) wait(&status);
}

87
takeover.sh Executable file
View File

@ -0,0 +1,87 @@
#!/bin/sh
set -e
TO=/takeover
OLD_TELINIT=/sbin/telinit
PORT=80
cd "$TO"
if [ ! -e fakeinit ]; then
./busybox echo "Please compile fakeinit.c first"
fi
./busybox echo "Please set a root password for sshd"
./busybox chroot . /bin/passwd
./busybox echo "Setting up target filesystem..."
./busybox rm -f etc/mtab
./busybox ln -s /proc/mounts etc/mtab
./busybox mkdir -p old_root
./busybox echo "Mounting pseudo-filesystems..."
./busybox mount -t tmpfs tmp tmp
./busybox mount -t proc proc proc
./busybox mount -t sysfs sys sys
if ! ./busybox mount -t devtmpfs dev dev; then
./busybox mount -t tmpfs dev dev
./busybox cp -a /dev/ dev/
./busybox rm -rf dev/pts
./busybox mkdir dev/pts
fi
./busybox mount -t devpts devpts dev/pts
TTY="$(./busybox tty)"
./busybox echo "Checking and switching TTY..."
exec <"$TO/$TTY" >"$TO/$TTY" 2>"$TO/$TTY"
./busybox echo "Type 'OK' to continue"
./busybox echo -n "> "
read a
if [ "$a" != "OK" ] ; then
exit 1
fi
./busybox echo "Preparing init..."
./busybox cp $OLD_TELINIT tmp/telinit
./busybox cat >tmp/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 pivot_root . old_root
./busybox echo "Chrooting and running init..."
exec ./busybox chroot . /fakeinit
EOF
./busybox chmod +x tmp/init
./busybox echo "Starting secondary sshd"
./busybox chroot . /usr/bin/ssh-keygen -A
./busybox chroot . /usr/sbin/sshd -p $PORT
./busybox echo "You should SSH into the secondary sshd now."
./busybox echo "Type OK to continue"
./busybox echo -n "> "
read a
if [ "$a" != "OK" ] ; then
exit 1
fi
./busybox echo "About to take over init. This script will now pause for a few seconds."
./busybox echo "If the takeover was successful, you will see output from the new init."
./busybox echo "You may then kill the remnants of this session and any remaining"
./busybox echo "processes from your new SSH session, and umount the old root filesystem."
./busybox mount --bind tmp /sbin
./tmp/telinit u
./busybox sleep 10