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:
commit
ca1e75afc2
83
README.md
Normal file
83
README.md
Normal 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
21
fakeinit.c
Normal 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
87
takeover.sh
Executable 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
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user