#!/usr/bin/env bash set -euo pipefail WORKDIR=${WORKDIR:-/root/tailrescue-live} OUTDIR=${OUTDIR:-/root/tailrescue-dist} ISO_NAME=${ISO_NAME:-tailrescue-headscale-$(date +%Y%m%d-%H%M).iso} DIST=${DIST:-bookworm} RESCUE_ENV=${RESCUE_ENV:-rescue.env} AUTHORIZED_KEYS=${AUTHORIZED_KEYS:-templates/authorized_keys} if [[ ! -f "$RESCUE_ENV" ]]; then echo "missing $RESCUE_ENV; copy templates/rescue.env.example and fill field secrets" >&2 exit 2 fi install -d "$WORKDIR" "$OUTDIR" rm -rf "$WORKDIR" mkdir -p "$WORKDIR" cd "$WORKDIR" lb config \ --distribution "$DIST" \ --archive-areas "main contrib non-free non-free-firmware" \ --binary-images iso-hybrid \ --bootappend-live "boot=live components hostname=tailrescue username=rescue" cat > config/package-lists/tailrescue.list.chroot <<"PKGS" systemd-sysv openssh-server sudo curl ca-certificates gnupg iptables nftables iproute2 iputils-ping dnsutils net-tools isc-dhcp-client pciutils usbutils lshw parted gdisk fdisk ntfs-3g rsync rclone restic smartmontools gddrescue pv jq tmux vim-tiny less firmware-linux firmware-linux-free firmware-linux-nonfree firmware-misc-nonfree firmware-realtek firmware-atheros firmware-brcm80211 firmware-bnx2 firmware-bnx2x firmware-iwlwifi firmware-libertas PKGS mkdir -p config/includes.chroot/etc/apt/keyrings config/includes.chroot/etc/apt/sources.list.d curl -fsSL https://pkgs.tailscale.com/stable/debian/${DIST}.noarmor.gpg \ -o config/includes.chroot/etc/apt/keyrings/tailscale-archive-keyring.gpg cat > config/includes.chroot/etc/apt/sources.list.d/tailscale.list < config/includes.chroot/usr/local/bin/rescue-status <<"RS" #!/bin/bash set -euo pipefail echo "== TailRescue Status ==" echo "Hostname: $(hostname)" echo "Date: $(date -Is)" echo "LAN IPs:" ip -br addr || true echo echo "Routes:" ip route || true echo echo "Tailscale:" tailscale status || true echo echo "Disks:" lsblk -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS,MODEL || true echo echo "SSH: ssh rescue@" RS chmod +x config/includes.chroot/usr/local/bin/rescue-status cat > config/includes.chroot/usr/local/bin/list-disks <<"LD" #!/bin/bash exec lsblk -o NAME,TYPE,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS,MODEL,SERIAL "$@" LD chmod +x config/includes.chroot/usr/local/bin/list-disks cat > config/includes.chroot/usr/local/bin/mount-ntfs-ro <<"MN" #!/bin/bash set -euo pipefail if [ "$#" -ne 2 ]; then echo "usage: mount-ntfs-ro /dev/sdXN /mnt/windows" >&2; exit 2; fi mkdir -p "$2" exec ntfs-3g -o ro,show_sys_files,streams_interface=windows "$1" "$2" MN chmod +x config/includes.chroot/usr/local/bin/mount-ntfs-ro cat > config/includes.chroot/usr/local/bin/tailrescue-firstboot <<"FB" #!/bin/bash set -u exec > >(tee -a /var/log/tailrescue-firstboot.log) 2>&1 set -x ENV_FILE=/etc/tailrescue/rescue.env [ -f "$ENV_FILE" ] && . "$ENV_FILE" RESCUE_USER=${RESCUE_USER:-rescue} TAILSCALE_LOGIN_SERVER=${TAILSCALE_LOGIN_SERVER:-https://head.pharmq.kr} TAILSCALE_TAGS=${TAILSCALE_TAGS:-tag:rescue} mkdir -p /run/sshd systemctl enable --now ssh || systemctl enable --now sshd || service ssh restart || /usr/sbin/sshd || true systemctl enable --now tailscaled || service tailscaled start || true sleep 3 if [ -n "${TAILSCALE_AUTHKEY:-}" ]; then tailscale up --login-server "$TAILSCALE_LOGIN_SERVER" --authkey "$TAILSCALE_AUTHKEY" --hostname "tailrescue-$(cat /etc/machine-id | cut -c1-8)" --advertise-tags "$TAILSCALE_TAGS" || true fi rescue-status || true FB chmod +x config/includes.chroot/usr/local/bin/tailrescue-firstboot cat > config/includes.chroot/etc/systemd/system/tailrescue-firstboot.service <<"SVC" [Unit] Description=TailRescue first boot SSH and Headscale enrollment After=network-online.target tailscaled.service ssh.service Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/local/bin/tailrescue-firstboot RemainAfterExit=yes [Install] WantedBy=multi-user.target SVC ln -sf /etc/systemd/system/tailrescue-firstboot.service config/includes.chroot/etc/systemd/system/multi-user.target.wants/tailrescue-firstboot.service 2>/dev/null || true mkdir -p config/hooks/normal cat > config/hooks/normal/0900-tailrescue-users.hook.chroot <<"HOOK" #!/bin/bash set -eux . /etc/tailrescue/rescue.env || true USER_NAME=${RESCUE_USER:-rescue} PASS_PLAIN=${RESCUE_PASSWORD:-} if ! id "$USER_NAME" >/dev/null 2>&1; then useradd -m -s /bin/bash "$USER_NAME"; fi if [ -n "$PASS_PLAIN" ]; then echo "$USER_NAME:$PASS_PLAIN" | chpasswd; fi usermod -aG sudo "$USER_NAME" echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/90-rescue chmod 0440 /etc/sudoers.d/90-rescue mkdir -p /home/$USER_NAME/.ssh /root/.ssh if [ -f /etc/tailrescue/authorized_keys ]; then cp /etc/tailrescue/authorized_keys /home/$USER_NAME/.ssh/authorized_keys cp /etc/tailrescue/authorized_keys /root/.ssh/authorized_keys fi chown -R $USER_NAME:$USER_NAME /home/$USER_NAME/.ssh chmod 700 /home/$USER_NAME/.ssh /root/.ssh chmod 600 /home/$USER_NAME/.ssh/authorized_keys /root/.ssh/authorized_keys 2>/dev/null || true sed -i "s/^#\?PasswordAuthentication .*/PasswordAuthentication yes/" /etc/ssh/sshd_config || true sed -i "s/^#\?PermitRootLogin .*/PermitRootLogin prohibit-password/" /etc/ssh/sshd_config || true sed -i "s/^#\?PubkeyAuthentication .*/PubkeyAuthentication yes/" /etc/ssh/sshd_config || true grep -q "^AllowUsers " /etc/ssh/sshd_config && sed -i "s/^AllowUsers .*/AllowUsers $USER_NAME root/" /etc/ssh/sshd_config || echo "AllowUsers $USER_NAME root" >> /etc/ssh/sshd_config HOOK chmod +x config/hooks/normal/0900-tailrescue-users.hook.chroot if [[ -f "$OLDPWD/$AUTHORIZED_KEYS" ]]; then cp "$OLDPWD/$AUTHORIZED_KEYS" config/includes.chroot/etc/tailrescue/authorized_keys elif [[ -f "$OLDPWD/templates/authorized_keys.example" ]]; then cp "$OLDPWD/templates/authorized_keys.example" config/includes.chroot/etc/tailrescue/authorized_keys fi lb build cp -f live-image-amd64.hybrid.iso "$OUTDIR/$ISO_NAME" (cd "$OUTDIR" && sha256sum "$ISO_NAME" > SHA256SUMS && echo "$ISO_NAME" > latest.txt) echo "$OUTDIR/$ISO_NAME"