This commit is contained in:
oliver
2026-05-27 16:28:42 -03:00
parent eb4d83e9e9
commit ce01dd64f7
9 changed files with 609 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# Build output
output/
data/
# Nebula certificates and keys (user-provided secrets)
nebula/*.crt
nebula/*.key
+54
View File
@@ -0,0 +1,54 @@
FROM alpine:3.22
# Base tools
RUN apk add --no-cache \
bash \
coreutils \
util-linux \
tar \
gzip \
xz \
cpio \
curl \
wget \
ca-certificates \
rsync \
file \
grep \
gawk \
sed \
vim
# Filesystem + disk tools (needed by chroot-setup.sh and for Alpine install)
RUN apk add --no-cache \
dosfstools \
e2fsprogs \
xfsprogs \
btrfs-progs \
parted \
sfdisk \
blkid \
lsblk \
wipefs \
util-linux-misc \
findmnt
# ISO + boot tools
RUN apk add --no-cache \
xorriso \
syslinux \
mtools \
squashfs-tools \
libarchive-tools \
busybox-static
# GRUB (BIOS + EFI) — available if needed for alternative builds
RUN apk add --no-cache \
grub \
grub-bios \
grub-efi \
efibootmgr
WORKDIR /work
CMD ["/bin/bash"]
+119
View File
@@ -0,0 +1,119 @@
# Alpine Linux USB Rescue ISO
A bootable hybrid ISO that:
- Gets an IP via **DHCP on `eth0`**
- Automatically opens a **Nebula overlay tunnel**
- Runs **OpenSSH** (login: `root` / `alpine`)
- Carries all tools needed to **install Alpine onto the host machine** (`setup-alpine`, `setup-disk`, `parted`, …)
The ISO is a hybrid image — it can be written to a USB stick with `dd` *or* burned to a CD/DVD.
---
## Prerequisites
- Docker + Docker Compose
- A Nebula certificate set for this node (see below)
---
## 1. Place Nebula certificates
Put the following files in `nebula/`:
```
nebula/
nebula ← binary (already included)
config.yml ← already included, edit as needed
ca.crt ← your Nebula CA certificate ← YOU PROVIDE
host.crt ← this node's certificate ← YOU PROVIDE
host.key ← this node's private key ← YOU PROVIDE
```
`ca.crt`, `host.crt`, and `host.key` are gitignored.
---
## 2. Build the ISO
```bash
docker compose run --rm alpine bash /scripts/create.sh
```
The ISO is written to `output/rescue-alpine.iso`.
---
## 3. Write to USB
```bash
dd if=output/rescue-alpine.iso of=/dev/sdX bs=4M status=progress && sync
```
Replace `/dev/sdX` with your USB device (check with `lsblk`). **All data on the device will be erased.**
Or burn to CD/DVD:
```bash
cdrecord dev=/dev/sr0 output/rescue-alpine.iso
```
---
## 4. Boot and connect
1. Insert USB and boot the target machine from it.
2. The system comes up fully in RAM — no writes to the host disk.
3. On boot, `eth0` gets an IP via DHCP, then the Nebula tunnel comes up automatically.
4. SSH in via the Nebula overlay IP (see `nebula/config.yml`):
```bash
ssh root@<nebula-ip>
# password: alpine
```
---
## 5. Install Alpine onto the host machine
Once SSH'd in (or at the local console):
```bash
# Interactive guided installer — partitions disk, installs Alpine
setup-alpine
# Or use setup-disk for more control
setup-disk -m sys /dev/sda
```
All required tools (`parted`, `e2fsprogs`, `dosfstools`, `btrfs-progs`, `alpine-conf`, …) are pre-installed.
---
## Default credentials
| Field | Value |
|----------|----------|
| Username | `root` |
| Password | `alpine` |
Change this in `scripts/chroot-setup.sh` before building if needed.
---
## Architecture
```
ISO (hybrid — bootable on USB or CD)
└── isolinux (BIOS boot)
├── vmlinuz-lts
└── initramfs.gz ← entire Alpine system packed as cpio+gzip
├── eth0 DHCP (openrc networking)
├── nebula → /usr/local/bin/nebula
├── /etc/nebula/ (config + certs)
├── OpenSSH (sshd)
└── Alpine install tools
```
The system runs **entirely in RAM** — the host disk is never touched until you explicitly run the installer.
Executable
+2
View File
@@ -0,0 +1,2 @@
docker compose run --rm alpine bash /scripts/create.sh
+15
View File
@@ -0,0 +1,15 @@
services:
alpine:
build: .
devices:
- /dev/loop-control:/dev/loop-control
privileged: true
volumes:
- ./data:/work
- ./scripts:/scripts:ro
- ./nebula:/nebula:ro
- ./output:/output
- /dev:/dev
working_dir: /work
tty: true
stdin_open: true
+66
View File
@@ -0,0 +1,66 @@
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/host.crt
key: /etc/nebula/host.key
static_host_map:
"192.168.9.1": ["167.71.79.60:4242"]
lighthouse:
am_lighthouse: false
interval: 60
hosts:
- "192.168.9.1"
listen:
host: 0.0.0.0
port: 4242
punchy:
punch: true
relay:
am_relay: false
use_relays: true
tun:
disabled: false
dev: nebula2
drop_local_broadcast: false
drop_multicast: false
tx_queue: 500
mtu: 1300
routes:
#- mtu: 8800
# route: 10.0.0.0/16
unsafe_routes:
logging:
level: info
format: text
firewall:
outbound_action: drop
inbound_action: drop
conntrack:
tcp_timeout: 12m
udp_timeout: 3m
default_timeout: 10m
outbound:
- port: any
proto: any
host: any
inbound:
- port: any #ping
proto: icmp
groups:
- admin
- port: 22 #GIT
proto: tcp
groups:
- admin
BIN
View File
Binary file not shown.
+135
View File
@@ -0,0 +1,135 @@
#!/bin/sh
set -eu
# ---------------------------------------------------------------------------
# chroot-setup.sh — Runs inside the Alpine chroot to configure the system
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# 1. APK repositories
# ---------------------------------------------------------------------------
echo "==> Configuring APK repositories"
cat > /etc/apk/repositories <<'EOF'
https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
https://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF
apk update
# ---------------------------------------------------------------------------
# 2. Install packages
# ---------------------------------------------------------------------------
echo "==> Installing packages"
apk add --no-cache \
linux-lts \
mkinitfs \
openrc \
busybox \
musl \
libc-utils \
util-linux \
e2fsprogs \
dosfstools \
btrfs-progs \
parted \
sfdisk \
openssh \
dhcpcd \
iproute2 \
iputils \
net-tools \
curl \
wget \
ca-certificates \
vim \
mc \
bind-tools \
nmap \
rsync \
alpine-conf
# ---------------------------------------------------------------------------
# 3. Generate initramfs
# ---------------------------------------------------------------------------
echo "==> Generating initramfs with mkinitfs"
mkinitfs $(ls /lib/modules/ | tail -1)
# ---------------------------------------------------------------------------
# 4. Network configuration
# ---------------------------------------------------------------------------
echo "==> Configuring network interfaces"
mkdir -p /etc/network
cat > /etc/network/interfaces <<'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
EOF
# ---------------------------------------------------------------------------
# 5. SSH configuration
# ---------------------------------------------------------------------------
echo "==> Configuring sshd"
sed -i \
-e 's/^#*\s*PermitRootLogin.*/PermitRootLogin yes/' \
-e 's/^#*\s*PasswordAuthentication.*/PasswordAuthentication yes/' \
/etc/ssh/sshd_config
# Ensure the directives exist if sed found nothing to replace
grep -q '^PermitRootLogin' /etc/ssh/sshd_config || echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
grep -q '^PasswordAuthentication' /etc/ssh/sshd_config || echo 'PasswordAuthentication yes' >> /etc/ssh/sshd_config
echo "==> Generating SSH host keys"
ssh-keygen -A
# ---------------------------------------------------------------------------
# 6. Root password
# ---------------------------------------------------------------------------
echo "==> Setting root password"
echo "root:alpine" | chpasswd
# ---------------------------------------------------------------------------
# 7. Nebula OpenRC service
# ---------------------------------------------------------------------------
echo "==> Creating /etc/init.d/nebula"
cat > /etc/init.d/nebula <<'EOF'
#!/sbin/openrc-run
description="Nebula overlay network"
command="/usr/local/bin/nebula"
command_args="-config /etc/nebula/config.yml"
command_background=true
pidfile="/run/nebula.pid"
depend() {
need net
after networking
}
start_pre() {
checkpath -f -m 0600 -o root:root /run/nebula.pid
}
EOF
chmod +x /etc/init.d/nebula
# ---------------------------------------------------------------------------
# 8. OpenRC service enablement
# ---------------------------------------------------------------------------
echo "==> Enabling services"
rc-update add sshd default
rc-update add networking default
rc-update add nebula default
# Also ensure basic boot services are present
rc-update add devfs sysinit || true
rc-update add dmesg sysinit || true
rc-update add mdev sysinit || true
rc-update add hwdrivers sysinit || true
# ---------------------------------------------------------------------------
# 9. Hostname
# ---------------------------------------------------------------------------
echo "==> Setting hostname to 'rescue'"
echo "rescue" > /etc/hostname
echo "==> chroot-setup.sh complete"
+211
View File
@@ -0,0 +1,211 @@
#!/bin/bash
set -euo pipefail
# ---------------------------------------------------------------------------
# create.sh — Build a bootable Alpine Linux rescue ISO
#
# Run inside the Docker build container:
# docker compose run --rm alpine bash /scripts/create.sh
#
# The resulting ISO is a hybrid image: it can be burned to a CD/DVD or
# written directly to a USB stick with dd.
# ---------------------------------------------------------------------------
ALPINE_MIRROR="https://dl-cdn.alpinelinux.org/alpine"
ALPINE_BRANCH="latest-stable"
ALPINE_ARCH="x86_64"
MINIROOTFS_BASE="${ALPINE_MIRROR}/${ALPINE_BRANCH}/releases/${ALPINE_ARCH}"
ISO="/output/rescue-alpine.iso"
CHROOT="/mnt/alpine-chroot"
ISOROOT="/tmp/isoroot"
mkdir -p /output "$CHROOT" "$ISOROOT"
# ---------------------------------------------------------------------------
# 1. Fetch latest Alpine minirootfs
# ---------------------------------------------------------------------------
echo "==> Fetching latest Alpine minirootfs index"
TARBALL=$(curl -fsSL "${MINIROOTFS_BASE}/" \
| grep -oP 'alpine-minirootfs-[0-9.]+-x86_64\.tar\.gz' \
| sort -V | tail -1)
[[ -z "$TARBALL" ]] && { echo "ERROR: could not determine minirootfs tarball" >&2; exit 1; }
TARBALL_LOCAL="/tmp/${TARBALL}"
if [[ ! -f "$TARBALL_LOCAL" ]]; then
echo "==> Downloading ${MINIROOTFS_BASE}/${TARBALL}"
curl -fsSL -o "$TARBALL_LOCAL" "${MINIROOTFS_BASE}/${TARBALL}"
else
echo "==> Using cached ${TARBALL_LOCAL}"
fi
# ---------------------------------------------------------------------------
# 2. Extract minirootfs into chroot
# ---------------------------------------------------------------------------
echo "==> Extracting minirootfs into ${CHROOT}"
tar -xzf "$TARBALL_LOCAL" -C "$CHROOT"
# ---------------------------------------------------------------------------
# 3. Copy Nebula binary and config into chroot
# ---------------------------------------------------------------------------
echo "==> Installing Nebula binary"
mkdir -p "${CHROOT}/usr/local/bin"
cp /nebula/nebula "${CHROOT}/usr/local/bin/nebula"
chmod +x "${CHROOT}/usr/local/bin/nebula"
echo "==> Installing Nebula config and certificates"
mkdir -p "${CHROOT}/etc/nebula"
cp /nebula/config.yml "${CHROOT}/etc/nebula/config.yml"
for f in /nebula/*.crt /nebula/*.key; do
[[ -f "$f" ]] && cp "$f" "${CHROOT}/etc/nebula/"
done
# ---------------------------------------------------------------------------
# 4. Bind-mount virtual filesystems for chroot
# ---------------------------------------------------------------------------
echo "==> Bind-mounting virtual filesystems"
mount --bind /dev "${CHROOT}/dev"
mount --bind /proc "${CHROOT}/proc"
mount --bind /sys "${CHROOT}/sys"
mkdir -p "${CHROOT}/run"
mount --bind /run "${CHROOT}/run"
cp /etc/resolv.conf "${CHROOT}/etc/resolv.conf"
# Make scripts available inside chroot
mkdir -p "${CHROOT}/scripts"
mount --bind /scripts "${CHROOT}/scripts"
# ---------------------------------------------------------------------------
# 5. Run in-chroot configuration
# ---------------------------------------------------------------------------
echo "==> Running chroot-setup.sh inside chroot"
chroot "$CHROOT" /bin/sh /scripts/chroot-setup.sh
# ---------------------------------------------------------------------------
# 6. Unmount virtual filesystems
# ---------------------------------------------------------------------------
echo "==> Unmounting virtual filesystems"
umount "${CHROOT}/scripts" || true
umount "${CHROOT}/run" || true
umount "${CHROOT}/sys" || true
umount "${CHROOT}/proc" || true
umount "${CHROOT}/dev" || true
# ---------------------------------------------------------------------------
# 7. Pack the full system as a squashfs (xz compressed)
# ---------------------------------------------------------------------------
echo "==> Creating squashfs from chroot (xz — this may take a while)"
mksquashfs "$CHROOT" "${ISOROOT}/rootfs.squashfs" \
-comp xz -Xdict-size 100% -noappend \
-e boot/initramfs-lts 2>/dev/null || \
mksquashfs "$CHROOT" "${ISOROOT}/rootfs.squashfs" \
-comp xz -noappend
echo " squashfs size: $(du -sh "${ISOROOT}/rootfs.squashfs" | cut -f1)"
# ---------------------------------------------------------------------------
# 8. Copy kernel into ISO root
# ---------------------------------------------------------------------------
echo "==> Copying kernel"
cp "${CHROOT}/boot/vmlinuz-lts" "${ISOROOT}/vmlinuz"
# ---------------------------------------------------------------------------
# 9. Build tiny initramfs: busybox + init script that mounts the squashfs
# ---------------------------------------------------------------------------
echo "==> Building tiny initramfs"
INITRD="/tmp/initrd"
rm -rf "$INITRD"
mkdir -p "${INITRD}"/{bin,dev,proc,sys,mnt/boot,mnt/squash,mnt/upper,mnt/work,mnt/newroot}
# Static busybox — no external library deps
BBOX=$(find /bin /usr/bin /usr/sbin -name 'busybox.static' -o -name 'busybox' 2>/dev/null \
| xargs -r file 2>/dev/null | grep -i 'statically linked' | cut -d: -f1 | head -1)
[[ -z "$BBOX" ]] && { echo "ERROR: cannot find a statically linked busybox" >&2; exit 1; }
echo " using static busybox: $BBOX"
cp "$BBOX" "${INITRD}/bin/busybox"
chmod +x "${INITRD}/bin/busybox"
for cmd in sh mount umount mkdir mknod switch_root mdev sleep; do
ln -sf busybox "${INITRD}/bin/${cmd}"
done
cat > "${INITRD}/init" <<'INIT_EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev 2>/dev/null || mdev -s
# Scan block devices for the ISO containing rootfs.squashfs
found=0
for try in 1 2 3 4 5; do
for dev in /dev/sr0 /dev/sda /dev/sda1 /dev/sdb /dev/sdb1 /dev/sdc /dev/sdc1; do
[ -b "$dev" ] || continue
mount -t iso9660 -o ro "$dev" /mnt/boot 2>/dev/null || \
mount -o ro "$dev" /mnt/boot 2>/dev/null || continue
if [ -f /mnt/boot/rootfs.squashfs ]; then
found=1
break 2
fi
umount /mnt/boot 2>/dev/null
done
sleep 1
done
if [ "$found" != "1" ]; then
echo "ERROR: rootfs.squashfs not found — dropping to shell"
exec sh
fi
mount -t squashfs -o ro /mnt/boot/rootfs.squashfs /mnt/squash
mount -t tmpfs tmpfs /mnt/upper
mkdir -p /mnt/upper/rw /mnt/upper/work
mount -t overlay overlay \
-o lowerdir=/mnt/squash,upperdir=/mnt/upper/rw,workdir=/mnt/upper/work \
/mnt/newroot
exec switch_root /mnt/newroot /sbin/init
INIT_EOF
chmod +x "${INITRD}/init"
( cd "$INITRD" && find . | cpio --quiet -oH newc | gzip -9 ) > "${ISOROOT}/initramfs.gz"
echo " initramfs size: $(du -sh "${ISOROOT}/initramfs.gz" | cut -f1)"
echo " initramfs contents:"
zcat "${ISOROOT}/initramfs.gz" | cpio -t 2>/dev/null
# ---------------------------------------------------------------------------
# 10. Build ISO with grub-mkrescue (BIOS + UEFI hybrid)
# grub-mkrescue uses its own file loader — no BIOS INT 13h size limits.
# ---------------------------------------------------------------------------
echo "==> Writing GRUB config"
mkdir -p "${ISOROOT}/boot/grub"
cat > "${ISOROOT}/boot/grub/grub.cfg" <<'EOF'
set timeout=5
set default=0
menuentry "Alpine Linux USB Rescue" {
linux /vmlinuz quiet init=/sbin/init
initrd /initramfs.gz
}
EOF
echo "==> Building hybrid ISO with grub-mkrescue: ${ISO}"
grub-mkrescue -o "$ISO" "$ISOROOT" -- -V ALPINE_RESCUE
# ---------------------------------------------------------------------------
# Done
# ---------------------------------------------------------------------------
ISO_SIZE=$(du -sh "$ISO" | cut -f1)
echo ""
echo "================================================================"
echo " Build complete!"
echo " ISO : ${ISO} (${ISO_SIZE})"
echo ""
echo " Burn to USB:"
echo " dd if=${ISO} of=/dev/sdX bs=4M status=progress && sync"
echo ""
echo " Burn to CD/DVD:"
echo " cdrecord dev=/dev/sr0 ${ISO}"
echo ""
echo " Default login : root / alpine"
echo " Nebula tunnel : auto-starts on boot (needs certs in nebula/)"
echo "================================================================"