초기 구현: TailRescue Headscale ISO 프로젝트 정리
This commit is contained in:
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# build outputs
|
||||||
|
*.iso
|
||||||
|
*.img
|
||||||
|
*.qcow2
|
||||||
|
*.raw
|
||||||
|
*.squashfs
|
||||||
|
*.packages
|
||||||
|
*.files
|
||||||
|
*.buildlog
|
||||||
|
/rootfs/
|
||||||
|
/chroot/
|
||||||
|
/binary/
|
||||||
|
/cache/
|
||||||
|
/build/
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# secrets / local field config
|
||||||
|
.env
|
||||||
|
*.env
|
||||||
|
rescue.env
|
||||||
|
secret.env
|
||||||
|
preauth*.txt
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
id_*
|
||||||
|
!*.pub
|
||||||
|
|
||||||
|
# temp
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
.DS_Store
|
||||||
73
README.md
Normal file
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# TailRescue Headscale Live ISO
|
||||||
|
|
||||||
|
Debian Live 기반 구조/백업용 rescue ISO 프로젝트입니다.
|
||||||
|
|
||||||
|
목표:
|
||||||
|
|
||||||
|
- Ventoy/iVentoy/Proxmox ISO 부팅
|
||||||
|
- DHCP로 유선 네트워크 자동 연결
|
||||||
|
- PharmQ Headscale에 Tailscale 자동 등록
|
||||||
|
- 외부에서 Tailnet IP로 SSH 접속
|
||||||
|
- Windows/산업용 PC 디스크 확인 및 NTFS read-only 마운트
|
||||||
|
- rsync/rclone/restic/gddrescue로 안전한 백업 지원
|
||||||
|
|
||||||
|
## 현재 검증 상태
|
||||||
|
|
||||||
|
검증일: 2026-06-01
|
||||||
|
|
||||||
|
- 빌드 호스트: `pve7`
|
||||||
|
- 테스트 VM: `pve7` VMID `990`
|
||||||
|
- Headscale login server: `https://head.pharmq.kr`
|
||||||
|
- 자동등록 노드 예: `tailrescue-44a29acb-3hhi3pl0`
|
||||||
|
- Tailnet SSH 검증: `ssh rescue@100.64.0.80` 성공
|
||||||
|
- passwordless sudo 검증 성공
|
||||||
|
- 디스크 인식 검증: `/dev/sda`, `/dev/sdb`
|
||||||
|
- NTFS 도구 포함 확인: `ntfs-3g`, `ntfs-3g.probe`, `ntfsfix`
|
||||||
|
|
||||||
|
## 저장소 정책
|
||||||
|
|
||||||
|
Git에는 다음만 보관합니다.
|
||||||
|
|
||||||
|
- live-build 설정 템플릿
|
||||||
|
- 빌드/검증 스크립트
|
||||||
|
- runbook/docs
|
||||||
|
- Hermes skill
|
||||||
|
|
||||||
|
Git에는 다음을 넣지 않습니다.
|
||||||
|
|
||||||
|
- 완성 ISO (`*.iso`)
|
||||||
|
- Headscale preauth key
|
||||||
|
- password 원문
|
||||||
|
- private SSH key
|
||||||
|
- 빌드 chroot/cache/binary 산출물
|
||||||
|
|
||||||
|
ISO는 Gitea Release attachment, 별도 artifact storage, 또는 `/root/tailrescue-dist` 같은 내부 보관소에 둡니다.
|
||||||
|
|
||||||
|
## 빠른 사용
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp templates/rescue.env.example rescue.env
|
||||||
|
# rescue.env에 현장용 preauth key/password/authorized key 설정
|
||||||
|
./scripts/build-live-iso.sh
|
||||||
|
./scripts/test-proxmox-vm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 현장 흐름
|
||||||
|
|
||||||
|
1. 최신 ISO를 Ventoy USB에 복사
|
||||||
|
2. 대상 PC에서 ISO 부팅
|
||||||
|
3. Debian Live 메뉴에서 Enter
|
||||||
|
4. 1~2분 대기
|
||||||
|
5. Headscale에서 `tailrescue-*` 노드 IP 확인
|
||||||
|
6. `ssh rescue@100.64.x.y`
|
||||||
|
7. `rescue-status`, `list-disks`
|
||||||
|
8. `sudo mount-ntfs-ro /dev/sdXN /mnt/windows`
|
||||||
|
9. 백업 실행
|
||||||
|
|
||||||
|
## 보안 원칙
|
||||||
|
|
||||||
|
- 현장별 1회용/단기 Headscale preauth key 사용
|
||||||
|
- password fallback은 Tailnet-only MVP용이며 현장마다 교체
|
||||||
|
- 가능하면 public-key auth 우선
|
||||||
|
- 원본 NTFS는 read-only 마운트 기본
|
||||||
|
- 작업 후 ephemeral `tailrescue-*` 노드 정리
|
||||||
39
docs/runbook.md
Normal file
39
docs/runbook.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# TailRescue 운영 Runbook
|
||||||
|
|
||||||
|
## 산출물 관리
|
||||||
|
|
||||||
|
- Git: 소스/문서/스크립트/스킬만 저장
|
||||||
|
- ISO: Gitea Release attachment 또는 내부 artifact path에 저장
|
||||||
|
- 현재 PoC ISO hash: `3d7995cfdf58c62f6ee167458079a7eaa1d2a79ac56e5f019cab1ec856943ddd`
|
||||||
|
|
||||||
|
## 빌드
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/headscale-create-preauth.sh
|
||||||
|
cp templates/rescue.env.example rescue.env
|
||||||
|
cp templates/authorized_keys.example templates/authorized_keys
|
||||||
|
# rescue.env에는 현장용 preauth key/password를 넣고, authorized_keys에는 공개키만 넣는다.
|
||||||
|
./scripts/build-live-iso.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 검증
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp /root/tailrescue-dist/$(cat /root/tailrescue-dist/latest.txt) /var/lib/vz/template/iso/tailrescue-headscale-test.iso
|
||||||
|
./scripts/test-proxmox-vm.sh
|
||||||
|
ssh rescue@100.64.x.y 'echo SSH_OK; sudo -n true; rescue-status; list-disks'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 현장
|
||||||
|
|
||||||
|
- Ventoy USB에 ISO 복사
|
||||||
|
- 대상 PC에서 ISO 선택 후 Enter
|
||||||
|
- Headscale node list에서 `tailrescue-*` 확인
|
||||||
|
- `ssh rescue@100.64.x.y`
|
||||||
|
- `sudo mount-ntfs-ro /dev/sdXN /mnt/windows`
|
||||||
|
|
||||||
|
## 장애 대응
|
||||||
|
|
||||||
|
- Headscale에 노드가 안 뜸: DHCP/NIC/firmware/케이블 확인, `ip -br a`, `journalctl -u tailrescue-firstboot`
|
||||||
|
- SSH가 안 됨: `systemctl status ssh`, `/var/log/auth.log`, `id rescue`, `sudo passwd -S rescue`
|
||||||
|
- 내장 NIC 미인식: Realtek RTL8153/RTL8156 또는 ASIX AX88179 USB LAN 동글 사용
|
||||||
195
scripts/build-live-iso.sh
Executable file
195
scripts/build-live-iso.sh
Executable 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"
|
||||||
8
scripts/headscale-create-preauth.sh
Executable file
8
scripts/headscale-create-preauth.sh
Executable 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
28
scripts/test-proxmox-vm.sh
Executable 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"
|
||||||
98
skills/devops/tailrescue-headscale-live-iso/SKILL.md
Normal file
98
skills/devops/tailrescue-headscale-live-iso/SKILL.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
name: tailrescue-headscale-live-iso
|
||||||
|
description: Use when building, testing, or operating a Ventoy/iVentoy Debian Live rescue ISO that auto-enrolls into PharmQ Headscale/Tailscale for remote SSH and Windows/NTFS backup.
|
||||||
|
version: 1.0.0
|
||||||
|
author: Hermes Agent
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
tags: [devops, live-iso, headscale, tailscale, ventoy, rescue, ntfs]
|
||||||
|
related_skills: [gitea-pat-askpass-push, nested-pve-auto-install-lab]
|
||||||
|
---
|
||||||
|
|
||||||
|
# TailRescue Headscale Live ISO
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This project builds a Debian Live rescue ISO for field PCs. It should boot from Ventoy/iVentoy/Proxmox, get DHCP on common Ethernet NICs, enroll into `https://head.pharmq.kr`, start OpenSSH, and expose disk/NTFS read-only backup helpers.
|
||||||
|
|
||||||
|
## Current Verified Baseline
|
||||||
|
|
||||||
|
- Build host: `pve7`
|
||||||
|
- Test VM: `pve7` VMID `990`
|
||||||
|
- Verified SSH over Tailnet: `rescue@100.64.0.80`
|
||||||
|
- Verified commands: `rescue-status`, `list-disks`
|
||||||
|
- Verified packages: `tailscale`, `openssh-server`, `ntfs-3g`, firmware packages
|
||||||
|
- Verified ISO hash: `3d7995cfdf58c62f6ee167458079a7eaa1d2a79ac56e5f019cab1ec856943ddd`
|
||||||
|
|
||||||
|
## Repository Policy
|
||||||
|
|
||||||
|
Commit scripts, docs, templates, and this skill. Do not commit ISO files, `rescue.env`, preauth keys, passwords, private keys, live-build `chroot/`, `binary/`, `cache/`, or other build artifacts.
|
||||||
|
|
||||||
|
## Build Flow
|
||||||
|
|
||||||
|
1. Create a short-lived Headscale preauth key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/headscale-create-preauth.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create local secrets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp templates/rescue.env.example rescue.env
|
||||||
|
cp templates/authorized_keys.example templates/authorized_keys
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Fill `rescue.env` and `templates/authorized_keys` without committing them.
|
||||||
|
4. Build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/build-live-iso.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proxmox Test Flow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp /root/tailrescue-dist/$(cat /root/tailrescue-dist/latest.txt) /var/lib/vz/template/iso/tailrescue-headscale-test.iso
|
||||||
|
./scripts/test-proxmox-vm.sh
|
||||||
|
ssh rescue@100.64.x.y 'echo SSH_OK; sudo -n true; rescue-status; list-disks'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Field Flow
|
||||||
|
|
||||||
|
1. Copy ISO to Ventoy USB or iVentoy ISO folder.
|
||||||
|
2. Boot target PC and select the ISO.
|
||||||
|
3. Press Enter at Debian Live menu if needed.
|
||||||
|
4. Wait 1-2 minutes.
|
||||||
|
5. Find `tailrescue-*` in Headscale.
|
||||||
|
6. SSH to `rescue@100.64.x.y`.
|
||||||
|
7. Run `list-disks`.
|
||||||
|
8. Mount Windows partition read-only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mount-ntfs-ro /dev/sdXN /mnt/windows
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ethernet Coverage
|
||||||
|
|
||||||
|
The ISO includes Debian 12 kernel and broad firmware: `firmware-linux`, `firmware-linux-nonfree`, `firmware-misc-nonfree`, `firmware-realtek`, `firmware-atheros`, `firmware-brcm80211`, `firmware-bnx2`, `firmware-bnx2x`, `firmware-iwlwifi`, `firmware-libertas`. Carry USB Ethernet dongles such as Realtek RTL8153/RTL8156 or ASIX AX88179 for field fallback.
|
||||||
|
|
||||||
|
## Common Pitfalls
|
||||||
|
|
||||||
|
1. SSH may connect but auth can fail if the rescue user is only created in firstboot. Keep the chroot hook that creates `rescue`, sudoers, sshd config, and authorized keys at build time.
|
||||||
|
2. Headscale duplicate ephemeral nodes can appear because live ISOs reuse machine IDs. Use the newest online Tailnet IP.
|
||||||
|
3. Never write to real rescue disks by default. Mount NTFS read-only.
|
||||||
|
4. Separate Ventoy/iVentoy boot problems from Linux NIC/firmware problems.
|
||||||
|
5. Rotate preauth keys and fallback passwords per field build.
|
||||||
|
|
||||||
|
## Verification Checklist
|
||||||
|
|
||||||
|
- [ ] ISO boots in Proxmox VM.
|
||||||
|
- [ ] Headscale node appears as `tailrescue-*`.
|
||||||
|
- [ ] `ssh rescue@100.64.x.y` works with public key.
|
||||||
|
- [ ] `sudo -n true` works.
|
||||||
|
- [ ] `rescue-status` shows LAN and `tailscale0` IPs.
|
||||||
|
- [ ] `list-disks` shows internal disks.
|
||||||
|
- [ ] `ntfs-3g`, `ntfs-3g.probe`, and `ntfsfix` are present.
|
||||||
|
- [ ] No secrets or ISO files are staged in Git.
|
||||||
1
templates/authorized_keys.example
Normal file
1
templates/authorized_keys.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Put SSH public keys here, one per line. Do not put private keys here.
|
||||||
7
templates/rescue.env.example
Normal file
7
templates/rescue.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Copy to rescue.env. Do not commit rescue.env.
|
||||||
|
TAILSCALE_LOGIN_SERVER=https://head.pharmq.kr
|
||||||
|
TAILSCALE_AUTHKEY=PASTE_FIELD_PREAUTH_KEY_HERE
|
||||||
|
TAILSCALE_TAGS=tag:rescue
|
||||||
|
RESCUE_USER=rescue
|
||||||
|
RESCUE_PASSWORD=CHANGE_PER_FIELD_BUILD
|
||||||
|
# Optional: paste one or more public keys into templates/authorized_keys before build.
|
||||||
Reference in New Issue
Block a user