initial commit

This commit is contained in:
lapinot 2024-03-05 17:11:56 +01:00
commit 5b3e79c671
3 changed files with 436 additions and 0 deletions

220
src/mkimg-archlinux Executable file
View File

@ -0,0 +1,220 @@
#!/usr/bin/sh -eu
usage() {
echo "usage: mkimg-archlinux --name NAME --disk SIZE --ipv4 IPV4 --ipv6 IPV6"
exit 1
}
# TODO make a real cli
NAME="cosmos"
DISK_SIZE="10G"
IPV4="10.238.2.3"
IPV6="2a00:5881:4008:46ff::203"
IPV4_GATEWAY="10.238.2.254"
IPV6_GATEWAY="2a00:5881:4008:46ff::2ff"
fsroot="/tmp/$NAME"
unmount() {
echo "=> INFO: unmounting root partition"
while mountpoint "$fsroot" > /dev/null 2>&1; do
if ! umount -R "$fsroot" > /dev/null 2>&1; then
echo -n "."
sleep 5
fi
done
rm -df "$fsroot"
}
disconnect() {
echo "=> INFO: disconnecting disk image"
qemu-nbd --disconnect /dev/nbd0
sleep 2
rmmod nbd
}
cleanup() {
echo "=> ERROR: cleaning up"
unmount
disconnect
}
# run command as root inside the chroot
run_cmd() {
local cmd="$@"
echo "=> CHROOT: $cmd"
chroot "$fsroot" /bin/bash -c "$cmd"
}
## DISK SETUP ##
echo "=> INFO: creating disk image"
mkdir -p "/srv/vm/$NAME"
qemu-img create -f qcow2 "/srv/vm/$NAME/disk.qcow2" $DISK_SIZE
chown qemu:qemu "/srv/vm/$NAME/disk.qcow2"
echo "=> INFO: connecting disk image"
modprobe nbd
qemu-nbd --connect=/dev/nbd0 "/srv/vm/$NAME/disk.qcow2"
trap cleanup EXIT
echo "=> INFO: creating root partition"
sfdisk /dev/nbd0 << EOF
label: dos
,,linux,*
EOF
mkfs.ext4 /dev/nbd0p1
echo "=> INFO: mounting root partition"
mkdir "$fsroot"
mount /dev/nbd0p1 "$fsroot"
## INSTALLATION ##
echo "=> INFO: installing base packages"
pacstrap -K "$fsroot" base linux mkinitcpio syslinux neovim zsh openntpd
echo "=> INFO: writing mirrorlist"
reflector --country fr,de --latest 20 --protocol https --sort rate > "$fsroot/etc/pacman.d/mirrorlist"
echo "=> INFO: writing fstab"
cat > "$fsroot/etc/fstab" << EOF
/dev/vda1 / ext4 rw,relatime 0 1
EOF
echo "=> INFO: writing hostname"
echo $NAME > "$fsroot/etc/hostname"
echo "=> INFO: configuring locales"
sed -i "s/#en_US.UTF-8/en_US.UTF-8/" -i "$fsroot/etc/locale.gen"
run_cmd locale-gen
echo "=> INFO: configuring network"
cat > "$fsroot/etc/resolv.conf" << EOF
nameserver $IPV4_GATEWAY
EOF
cat > "$fsroot/etc/systemd/network/radon.network" << EOF
[Match]
Type=ether
[Network]
Address=$IPV4
Address=$IPV6
[Route]
Gateway=$IPV4_GATEWAY
GatewayOnLink=true
Destination=0.0.0.0/0
[Route]
Gateway=$IPV6_GATEWAY
GatewayOnLink=true
Destination=::/0
EOF
mount -t proc /proc "$fsroot/proc"
mount -t sysfs /sys "$fsroot/sys"
mount --rbind /dev "$fsroot/dev"
mount --make-rslave "$fsroot"
run_cmd systemctl enable systemd-networkd
run_cmd systemctl enable openntpd
echo "=> INFO: configuring bootloader"
mkdir -p "$fsroot/boot/syslinux"
cat > "$fsroot/boot/syslinux/syslinux.cfg" << EOF
DEFAULT arch
PROMPT 0
TIMEOUT 0
LABEL arch
LINUX ../vmlinuz-linux
APPEND root=/dev/vda1 rw earlyprintk=ttyS0 console=ttyS0
INITRD ../initramfs-linux.img
EOF
cp "$fsroot/usr/lib/syslinux/bios/ldlinux.c32" "$fsroot/boot/syslinux/"
run_cmd extlinux --install /boot/syslinux
cp "$fsroot/usr/lib/syslinux/bios/mbr.bin" /tmp/mbr.bin
echo "=> INFO: configuring initramfs"
cat > "$fsroot/etc/mkinitcpio.conf" << EOF
MODULES=(virtio virtio_pci virtio_blk ext4)
BINARIES=()
FILES=()
HOOKS=(base)
EOF
sed -i "s/PRESETS=('default' 'fallback')/PRESETS=('default')/" "$fsroot/etc/mkinitcpio.d/linux.preset"
rm "$fsroot/boot/initramfs-linux-fallback.img"
run_cmd mkinitcpio -P
#echo "=> INFO: configuring zsh"
#
#cat > "$fsroot/root/.zshrc" << EOF
#HISTFILE=~/.histfile
#HISTSIZE=10000
#SAVEHIST=10000
#setopt appendhistory nomatch
#
#bindkey -e
#
#zstyle :compinstall filename '/root/.zshrc'
#autoload -Uz compinit
#compinit
#
#PS1='[%n@%m %~]$ '
#export EDITOR=nvim
#
#term-resize() {
# if [[ -t 0 && $# -eq 0 ]]; then
# local IFS='[;' escape geometry x y
# echo -ne '\e7\e[r\e[999;999H\e[6n\e8'
# read -t 5 -sd R escape geometry || {
# echo unsupported terminal emulator. >&2
# return 1
# }
# x="${geometry##*;}" y="${geometry%%;*}"
# if [[ ${COLUMNS} -eq "${x}" && ${LINES} -eq "${y}" ]]; then
# echo "${TERM} ${x}x${y}"
# elif [[ "$x" -gt 0 && "$y" -gt 0 ]]; then
# echo "${COLUMNS}x${LINES} -> ${x}x${y}"
# stty cols ${x} rows ${y}
# else
# echo unsupported terminal emulator. >&2
# return 1
# fi
# else
# echo 'Usage: term-resize'
# fi
#}
#EOF
echo "=> INFO: setting root password"
passwd=$(openssl rand -base64 12)
echo "root:$passwd" | run_cmd chpasswd
echo "=> INFO: setting qemu config file"
cat > "/srv/vm/$NAME/config" << EOF
NCPU=2
MEMORY=2048M
EOF
## WRAPPING UP
trap - EXIT
unmount
echo "=> INFO: writing the MBR"
dd bs=440 count=1 conv=notrunc if=/tmp/mbr.bin of=/dev/nbd0
rm /tmp/mbr.bin
disconnect
echo "=> INFO: root password: $passwd"

73
src/sys-backup Executable file
View File

@ -0,0 +1,73 @@
#!/usr/bin/sh
### CONFIG
VG_NAME=nvme # volume group of the target
ORIG_NAME=system # logical volume name to backup
SNAP_NAME=tmp-snap # name of temporary snapshot
BORG_REPO=/data/backup/radon-host # path to borg repository
### SETUP
TMPDIR=$(mktemp -d)
info() { printf "%s\n" "$*" >&2; }
cleanup() {
cd /
umount --force --quiet "/dev/$VG_NAME/$SNAP_NAME"
rm --recursive --force "$TMPDIR"
lvremove --force "$VG_NAME/$SNAP_NAME"
}
trap 'info "Backup interrupted"; cleanup; exit 2' INT TERM
### THE ACTUAL THING
info "Checking for old snapshot"
if lvs --options lv_name "$VG_NAME" | grep --quiet "$SNAP_NAME"; then
cleanup
fi
info "Creating temporary snapshot"
lvcreate --size 1G --snapshot --name "$SNAP_NAME" "$VG_NAME/$ORIG_NAME"
mount "/dev/$VG_NAME/$SNAP_NAME" "$TMPDIR"
info "Starting backup"
cd "$TMPDIR"
borg create \
--verbose \
--stats \
--show-rc \
--compression zstd,1 \
--exclude-caches \
--exclude 'archive' \
--exclude 'backup' \
--exclude 'content' \
--exclude 'dev' \
--exclude 'mnt' \
--exclude 'proc' \
--exclude 'run' \
--exclude 'sys' \
--exclude 'tmp' \
--exclude 'var/tmp/*' \
--exclude 'var/cache/*' \
"$BORG_REPO"::'system-{now:%Y-%m-%d}' \
.
info "Pruning repository"
borg prune \
--list \
--glob-archives 'system-*' \
--show-rc \
--keep-daily 7 \
--keep-weekly 2 \
--keep-monthly 10 \
"$BORG_REPO"
info "Compacting repository"
borg compact "$BORG_REPO"
info "Removing snapshot"
cleanup

143
src/vm-mgmt Executable file
View File

@ -0,0 +1,143 @@
#!/bin/bash -eu
QEMU=/usr/bin/qemu-system-x86_64
VIRTIOFSD=/usr/lib/qemu/virtiofsd
SOCAT=/usr/bin/socat
IP=/usr/bin/ip
CFG_BASE=/srv/vm
RUN_BASE=/run/vm
check_vm() {
if [[ ! -e $MONITOR_PATH ]]; then
echo "error: monitor socket not found, is VM '$VM_NAME' running?"
exit 1
fi
}
monitor_cmd() {
check_vm
echo $1 | $SOCAT - unix-connect:$MONITOR_PATH
}
do_start() {
mkdir -p /dev/hugepages/qemu $RUN_DIR
chown qemu:qemu /dev/hugepages/qemu $RUN_DIR
# read config variables
QEMU_EXTRA_ARGS=""
FS_SHARE=""
MEM_OPTS=""
. $CFG_DIR/config
ID_HEX="$(printf %02x $VM_ID)"
ID_DEC="$(printf %03d $VM_ID)"
MAC_ADDR="52:54:00:00:00:$ID_HEX"
IPV4_ADDR="10.238.2.$ID_DEC"
IPV6_ADDR="2a00:5881:4008:46ff::2$ID_HEX"
# setup network
$IP tuntap add dev $IFNAME mode tap group qemu
$IP link set dev $IFNAME up
$IP address add 10.238.1.1 dev $IFNAME
$IP address add 2a00:5881:4008:46ff::101 dev $IFNAME
$IP route add $IPV4_ADDR dev $IFNAME
$IP route add $IPV6_ADDR dev $IFNAME
# setup virtiofs daemon and opts
if [ ! -z $FS_SHARE ]; then
VHOST_PATH=$RUN_DIR/vhost-fs.sock
QEMU_EXTRA_ARGS="$QEMU_EXTRA_ARGS -chardev socket,id=vhost0,path=$VHOST_PATH"
QEMU_EXTRA_ARGS="$QEMU_EXTRA_ARGS -device vhost-user-fs-pci,queue-size=1024,chardev=vhost0,tag=fs0"
MEM_OPTS=",share=on"
$VIRTIOFSD --socket-path=$VHOST_PATH --socket-group=qemu -o source=$FS_SHARE -o cache=always &
fi
exec setpriv --reuid=qemu --regid=qemu --init-groups --inh-caps=-all --reset-env \
$QEMU -nodefaults -no-user-config -nographic -sandbox on -enable-kvm \
-machine pc -cpu host -smp $NCPU -m $MEMORY \
-object memory-backend-file,id=mem,size=$MEMORY,mem-path=/dev/hugepages/qemu$MEM_OPTS -numa node,memdev=mem \
-monitor unix:$MONITOR_PATH,server=on,wait=off \
-serial unix:$CONSOLE_PATH,server=on,wait=off \
-drive file=$DISK_IMG,format=qcow2,id=disk0,if=none \
-device virtio-blk-pci,drive=disk0 \
-netdev tap,id=net0,ifname=$IFNAME,script=no,downscript=no,vhost=on \
-device virtio-net-pci,mac=$MAC_ADDR,netdev=net0 \
$QEMU_EXTRA_ARGS
}
do_console() {
check_vm
echo "info: connecting to console: '$VM_NAME'"
echo "info: press ctrl-o to exit"
exec $SOCAT -,escape=0x0f,raw,echo=0 unix-connect:$CONSOLE_PATH
}
do_monitor() {
check_vm
echo "info: connecting to monitor: '$VM_NAME'"
echo "info: press ctrl-c to exit"
exec $SOCAT readline unix-connect:$MONITOR_PATH
}
do_stop() {
monitor_cmd system_powerdown
$IP link del $IFNAME
}
do_mount() {
fsroot="/tmp/$VM_NAME"
modprobe nbd
qemu-nbd --connect=/dev/nbd0 "$DISK_IMG"
mkdir "$fsroot"
mount /dev/nbd0p1 "$fsroot"
mount -t proc /proc "$fsroot/proc"
mount -t sysfs /sys "$fsroot/sys"
mount --rbind /dev "$fsroot/dev"
mount --make-rslave "$fsroot"
echo "info: mounted VM '$VM_NAME' rootfs at '/tmp/$VM_NAME'"
}
do_umount() {
if ! mountpoint "/tmp/$VM_NAME" > /dev/null 2>&1; then
echo "error: mountpoint not found, is VM '$VM_NAME' mounted?"
exit 1
fi
umount -R "/tmp/$VM_NAME"
rm -df "/tmp/$VM_NAME"
qemu-nbd --disconnect /dev/nbd0 > /dev/null
rmmod nbd
echo "info: unmounted VM '$VM_NAME' successfully"
}
usage() {
echo "usage: vm-mgmt ( start | stop | reboot | console | monitor | mount | umount ) NAME"
exit 1
}
test $# -lt 2 && usage
VM_NAME=$2
RUN_DIR=$RUN_BASE/$VM_NAME
CFG_DIR=$CFG_BASE/$VM_NAME
CONSOLE_PATH=$RUN_DIR/console.sock
MONITOR_PATH=$RUN_DIR/monitor.sock
DISK_IMG=$CFG_DIR/disk.qcow2
IFNAME=vm-$VM_NAME
case $1 in
start) do_start;;
stop) do_stop;;
reboot) monitor_cmd system_reset;;
console) do_console;;
monitor) do_monitor;;
mount) do_mount;;
umount) do_umount;;
*) usage;;
esac