* Join the tailnet from a cidata tailscale_authkey A tailscale_authkey file on the autoinstall drive gets the installed machine onto the tailnet on its own: the tailscale package is bundled in the offline mirror and installed only when the key is present, the key is staged root-only at /etc/tailscale/authkey, and a oneshot first-boot unit runs tailscale up once the network is really there. On success the key is deleted and the unit disables itself; on failure both survive to retry on the next boot, so an offline install joins whenever it first gets connectivity. ufw allows traffic in on tailscale0 via the same write-the-rule-then-ignore-the-chroot-exit-code dance as the SSH phase. Bundling rather than fetching at first boot is deliberate: pacman -Sy tailscale on a fresh machine is a partial upgrade, and pacman -Syu means an unattended full system upgrade racing the user's first login. The offline mirror is a coherent snapshot; installing from it keeps the target consistent and the install offline-capable. See plans/tailscale.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xc74h4WSYpuYPGuYbFAR1q * Do not hold boot hostage for the tailnet join Review caught that the join unit was Type=oneshot wanted by multi-user.target: target units implicitly gain After= for their Wants=, so an offline machine's boot -- SDDM included -- stalled for the full 10-minute timeout, and once timed out the join never retried within that boot. Type=simple counts as started the moment it forks, so boot proceeds while the join retries in the background for as long as the boot lasts. Cleanup moves inside the script, sequenced after tailscale up succeeds, which preserves the retry-across-boots behavior: key and unit survive a boot with no connectivity, and the key never outlives a successful join. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xc74h4WSYpuYPGuYbFAR1q --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
58 lines
2.0 KiB
Bash
Executable File
58 lines
2.0 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Autoinstall: a drive labeled cidata (the cloud-init NoCloud label, so Proxmox,
|
|
# libvirt and Packer all know how to attach one) carrying the configurator's own
|
|
# output files stands in for the wizard. Copy those files into /root and
|
|
# everything downstream runs the ordinary path against ordinary inputs.
|
|
#
|
|
# Exits 0 when an autoinstall drive was loaded and the configurator can be
|
|
# skipped. The required pair is the same one the orchestrator treats as
|
|
# mandatory; the rest are optional there too, so don't be stricter here.
|
|
# Anything less than the pair means this isn't an autoinstall drive, and the
|
|
# caller falls back to the wizard.
|
|
#
|
|
# The optional argument prefixes every path the script touches; tests use it to
|
|
# run against a sandbox instead of the live system.
|
|
|
|
set -uo pipefail
|
|
|
|
root=${1:-}
|
|
by_label=$root/dev/disk/by-label
|
|
dest=$root/root
|
|
mnt=$root/run/cidata
|
|
|
|
# The by-label symlinks come from udev, and on a fast boot this can run before
|
|
# device enumeration has finished. Wait for the queue to drain instead of
|
|
# racing it and silently dropping a headless install into the wizard.
|
|
udevadm settle 2>/dev/null || true
|
|
|
|
device=""
|
|
for label in cidata CIDATA; do
|
|
[[ -e $by_label/$label ]] && { device=$by_label/$label; break; }
|
|
done
|
|
[[ -n $device ]] || exit 1
|
|
|
|
mkdir -p "$mnt"
|
|
mount -o ro "$device" "$mnt" || exit 1
|
|
|
|
loaded=1
|
|
if [[ -f $mnt/user_configuration.json && -f $mnt/user_credentials.json ]]; then
|
|
files=("$mnt"/user_configuration.json "$mnt"/user_credentials.json)
|
|
for file in user_full_name.txt user_email_address.txt user_encrypt_installation.txt authorized_keys tailscale_authkey; do
|
|
[[ -f $mnt/$file ]] && files+=("$mnt/$file")
|
|
done
|
|
if cp "${files[@]}" "$dest"/; then
|
|
loaded=0
|
|
else
|
|
# A partial copy must not leave files behind: the wizard cleans up only
|
|
# what it writes itself, so a half-copied authorized_keys would leak into
|
|
# the interactive install that follows.
|
|
for file in "${files[@]}"; do
|
|
rm -f "$dest/${file##*/}"
|
|
done
|
|
fi
|
|
fi
|
|
|
|
umount "$mnt"
|
|
exit $loaded
|