초기 구현: TailRescue Headscale ISO 프로젝트 정리

This commit is contained in:
2026-06-01 19:46:05 +09:00
commit 6334bf0eb8
9 changed files with 480 additions and 0 deletions

195
scripts/build-live-iso.sh Executable file
View File

@@ -0,0 +1,195 @@
#!/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 <<TAILSRC
deb [signed-by=/etc/apt/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/debian ${DIST} main
TAILSRC
mkdir -p config/packages.chroot
(cd config/packages.chroot && apt-get download tailscale)
mkdir -p config/includes.chroot/etc/tailrescue
cp "$OLDPWD/$RESCUE_ENV" config/includes.chroot/etc/tailrescue/rescue.env
chmod 600 config/includes.chroot/etc/tailrescue/rescue.env
mkdir -p config/includes.chroot/usr/local/bin config/includes.chroot/etc/systemd/system
cat > 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@<tailscale-ip>"
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"

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
HEADSCALE_HOST=${HEADSCALE_HOST:-root@192.168.0.100}
HEADSCALE_CONTAINER=${HEADSCALE_CONTAINER:-headscale}
USER_ID=${USER_ID:-1}
EXPIRATION=${EXPIRATION:-24h}
TAGS=${TAGS:-tag:rescue}
ssh "$HEADSCALE_HOST" "docker exec $HEADSCALE_CONTAINER headscale preauthkeys create -u $USER_ID --reusable --ephemeral --expiration $EXPIRATION --tags $TAGS -o json"

28
scripts/test-proxmox-vm.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
VMID=${VMID:-990}
ISO=${ISO:-local:iso/tailrescue-headscale-test.iso}
STORAGE=${STORAGE:-local-lvm}
BRIDGE=${BRIDGE:-vmbr0}
HEADSCALE_HOST=${HEADSCALE_HOST:-root@192.168.0.100}
HEADSCALE_CONTAINER=${HEADSCALE_CONTAINER:-headscale}
qm stop "$VMID" --skiplock 1 || true
qm destroy "$VMID" --purge 1 --destroy-unreferenced-disks 1 || true
qm create "$VMID" \
--name tailrescue-test \
--memory 2048 \
--cores 2 \
--net0 virtio,bridge="$BRIDGE" \
--ostype l26 \
--scsihw virtio-scsi-single \
--vga std \
--boot order=ide2 \
--ide2 "$ISO",media=cdrom \
--agent enabled=0
qm set "$VMID" --scsi0 "$STORAGE":8
qm start "$VMID"
sleep 3
qm sendkey "$VMID" ret || true
sleep 75
ssh "$HEADSCALE_HOST" "docker exec $HEADSCALE_CONTAINER headscale nodes list | grep -i tailrescue || true"