Separate personal provisioning from the generic installer
build-iso / build (push) Failing after 25s
build-iso / build (push) Failing after 25s
The install hardcoded one specific forge: bootstrap and the archinstall custom-commands fetched from a private Gitea, and firstboot cloned a private dotfiles repo from it. Anyone else cloning this got an installer that could not work and that pointed at a repo they cannot read. Split the two phases. The base install (LUKS + btrfs + snapper + yay) is now generic and runs from a fresh clone with no configuration. The personal phase (vault unlock, forge key registration, dotfiles, yadm bootstrap) reads a gitignored site.env; with none present firstboot reports what it is skipping and leaves a complete system. One fetch root, ARCH_TURNKEY_BASE, now feeds bootstrap, the archinstall custom-commands (via an @@BASE@@ placeholder) and the ISO autorun, defaulting to the public mirror. iso/build.sh bakes site.env into a custom ISO so an unattended personal install stays one boot.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Personal site config — never commit. See configs/site.env.example / docs/SITE.md.
|
||||
site.env
|
||||
@@ -13,7 +13,7 @@ auto-detection, and a two-phase flow built entirely on mature tools —
|
||||
Boot the current Arch ISO, connect to the network, then run one line:
|
||||
|
||||
```
|
||||
curl -fsSL arch.onetick.ninja | bash
|
||||
curl -fsSL https://raw.githubusercontent.com/sudolulo/arch-turnkey/master/bootstrap.sh | bash
|
||||
```
|
||||
|
||||
That preflight-checks the machine, installs `archinstall`, pulls the config, and
|
||||
@@ -83,17 +83,27 @@ iso/ # archiso profile + build script (zero-touch ISO)
|
||||
docs/ # detailed install guide + nginx redirect snippet
|
||||
```
|
||||
|
||||
## Fork it
|
||||
## Making it yours
|
||||
|
||||
Point it at your own infrastructure by editing the Gitea/dotfiles URLs in
|
||||
`bootstrap.sh` and `firstboot.sh`, and set `VAULT_URL` / `VAULT_EMAIL` (or let
|
||||
`firstboot` prompt). The design — archinstall + yadm classes + snapper — is
|
||||
generic; only those URLs are installation-specific.
|
||||
Nothing installation-specific is committed here. The install runs in two phases:
|
||||
|
||||
1. **Base** — archinstall: LUKS + btrfs subvolumes + snapper + yay. Generic; works from a
|
||||
fresh clone with no configuration at all.
|
||||
2. **Personal** — unlock a vault, register the new machine's SSH key with your forge, clone
|
||||
your dotfiles, run `yadm bootstrap`. This needs *your* infrastructure, so it reads a
|
||||
gitignored **`site.env`** that you supply. Without one, firstboot says what it's skipping
|
||||
and leaves you a complete, clean Arch system.
|
||||
|
||||
Copy `configs/site.env.example` to `site.env`, fill it in, and either bake it into a custom
|
||||
ISO (`iso/build.sh`) for a near-unattended install or drop it at `/root/site.env` on the live
|
||||
ISO. Set `ARCH_TURNKEY_BASE` if you want the install to fetch from your own forge rather than
|
||||
this repo's GitHub mirror. Full details: **[docs/SITE.md](docs/SITE.md)**.
|
||||
|
||||
## Requirements
|
||||
|
||||
- UEFI machine, current Arch ISO
|
||||
- A yadm dotfiles repo with a `~/.config/pkglist/` (common + class-* + per-host)
|
||||
- Optional: a Vaultwarden item `arch-install` (password = Gitea token with
|
||||
`write:public_key`; custom field `archive-pass` = dotfiles archive passphrase)
|
||||
to skip all manual secret entry
|
||||
- For the personal phase only (all optional — omit and you get the base install):
|
||||
- A yadm dotfiles repo with a `~/.config/pkglist/` (common + class-* + per-host)
|
||||
- A Vaultwarden/Bitwarden item `arch-install` (password = a forge token with
|
||||
`write:public_key`; custom field `archive-pass` = dotfiles archive passphrase) to
|
||||
collapse all secret entry behind one master-password prompt
|
||||
|
||||
+23
-1
@@ -4,7 +4,16 @@
|
||||
# Installs archinstall, pulls the config, launches the guided install, then reboots.
|
||||
set -euo pipefail
|
||||
|
||||
BASE="https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master"
|
||||
# Where the rest of the tree is fetched from. Defaults to the public GitHub mirror so a
|
||||
# plain `curl | bash` works for anyone. Point it at any fork or your own forge with:
|
||||
# ARCH_TURNKEY_BASE=https://git.example.com/you/arch-turnkey/raw/branch/master
|
||||
BASE="${ARCH_TURNKEY_BASE:-https://raw.githubusercontent.com/sudolulo/arch-turnkey/master}"
|
||||
|
||||
# Optional site config: personal provisioning (dotfiles repo, vault, forge). Absent by
|
||||
# default, so a stranger gets a clean generic install. Bake one into a custom ISO with
|
||||
# iso/build.sh to keep your own install fully unattended — see docs/SITE.md.
|
||||
SITE="${ARCH_TURNKEY_SITE:-/root/site.env}"
|
||||
|
||||
# interactive I/O must come from the console, since this runs via `curl | bash`
|
||||
TTY=/dev/tty
|
||||
|
||||
@@ -36,6 +45,9 @@ fi
|
||||
|
||||
echo ">> Fetching config..."
|
||||
curl -fsSL "$BASE/configs/archinstall.json" -o /tmp/archinstall.json
|
||||
# archinstall's custom-commands run in the installed system and cannot see our env, so
|
||||
# bake the resolved BASE into them. Keeps a single source of truth for the fetch root.
|
||||
sed -i "s|@@BASE@@|$BASE|g" /tmp/archinstall.json
|
||||
|
||||
echo ">> Launching archinstall."
|
||||
echo " In the menu: set hostname, pick Disk -> btrfs + subvolumes + LUKS encryption,"
|
||||
@@ -43,6 +55,16 @@ echo " create your user. Everything else is preset."
|
||||
echo
|
||||
archinstall --config /tmp/archinstall.json <"$TTY"
|
||||
|
||||
# Hand the site config (if any) to the installed system so firstboot can provision
|
||||
# dotfiles/secrets unattended. No site.env => firstboot skips that phase entirely.
|
||||
if [ -f "$SITE" ]; then
|
||||
echo ">> Installing site config for first-boot provisioning..."
|
||||
install -Dm600 "$SITE" /mnt/etc/arch-turnkey/site.env
|
||||
else
|
||||
echo ">> No site config — first boot will skip dotfiles/secret provisioning."
|
||||
echo " (That's the generic path. See docs/SITE.md to provision your own.)"
|
||||
fi
|
||||
|
||||
# --- auto-reboot into the installed system (which auto-runs firstboot) ---
|
||||
echo
|
||||
echo ">> Base install done. Rebooting in 10s into first-boot setup..."
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
],
|
||||
|
||||
"custom-commands": [
|
||||
"curl -fsSL https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/firstboot.sh -o /usr/local/bin/firstboot && chmod +x /usr/local/bin/firstboot",
|
||||
"curl -fsSL https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/configs/firstboot-once -o /usr/local/bin/firstboot-once && chmod +x /usr/local/bin/firstboot-once",
|
||||
"curl -fsSL https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/configs/firstboot.service -o /etc/systemd/system/firstboot.service",
|
||||
"curl -fsSL @@BASE@@/firstboot.sh -o /usr/local/bin/firstboot && chmod +x /usr/local/bin/firstboot",
|
||||
"curl -fsSL @@BASE@@/configs/firstboot-once -o /usr/local/bin/firstboot-once && chmod +x /usr/local/bin/firstboot-once",
|
||||
"curl -fsSL @@BASE@@/configs/firstboot.service -o /etc/systemd/system/firstboot.service",
|
||||
"systemctl enable firstboot.service"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# arch-turnkey site config — YOUR machine's personal provisioning. Never commit a filled-in
|
||||
# copy: `site.env` is gitignored precisely so no personal infrastructure lands in this repo.
|
||||
#
|
||||
# Without a site.env, arch-turnkey installs a clean generic Arch system (LUKS + btrfs +
|
||||
# subvolumes + snapper + yay) and stops. Everything below is the optional second phase:
|
||||
# unlock a vault, register the new machine's SSH key with your forge, clone your dotfiles,
|
||||
# and run `yadm bootstrap`.
|
||||
#
|
||||
# HOW IT REACHES THE INSTALLED SYSTEM
|
||||
# bootstrap.sh copies this file to /etc/arch-turnkey/site.env (mode 0600) after
|
||||
# archinstall finishes; firstboot.sh sources it on the first boot.
|
||||
#
|
||||
# - Unattended: bake it into a custom ISO — put site.env at iso/site.env and run
|
||||
# iso/build.sh. Booting that ISO provisions the machine with no extra typing.
|
||||
# - Ad hoc: drop it at /root/site.env on the live ISO before running bootstrap.sh, or
|
||||
# point ARCH_TURNKEY_SITE at another path.
|
||||
#
|
||||
# See docs/SITE.md.
|
||||
|
||||
# --- dotfiles (required to enable phase 2; unset => phase 2 is skipped entirely) ---
|
||||
# A yadm-managed repo containing ~/.config/pkglist/ (common + class-* + per-host).
|
||||
DOTFILES_REPO="ssh://git@git.example.com:22/you/dotfiles.git"
|
||||
|
||||
# --- forge (used to auto-register this machine's new SSH key, so the clone above works) ---
|
||||
FORGE_API="https://git.example.com/api/v1"
|
||||
FORGE_SSH_HOST="git.example.com"
|
||||
FORGE_SSH_PORT="22"
|
||||
|
||||
# --- vault (Vaultwarden/Bitwarden via rbw) ---
|
||||
# Provides the forge token and the dotfiles archive passphrase from ONE master-password
|
||||
# prompt. Create an item named "arch-install" with:
|
||||
# password field = a forge token with scope write:public_key
|
||||
# custom field = archive-pass -> your yadm encrypted-archive passphrase
|
||||
# If rbw is unavailable, firstboot falls back to prompting you to paste both.
|
||||
VAULT_URL="https://vault.example.com"
|
||||
VAULT_EMAIL="you@example.com"
|
||||
|
||||
# --- optional: fetch root override ---
|
||||
# Where bootstrap.sh pulls the rest of the tree from. Defaults to the public GitHub mirror.
|
||||
# Point it at your own forge if you would rather not depend on GitHub at install time.
|
||||
#ARCH_TURNKEY_BASE="https://git.example.com/you/arch-turnkey/raw/branch/master"
|
||||
+1
-1
@@ -21,7 +21,7 @@ plug in), then:
|
||||
|
||||
```
|
||||
pacman -Sy archinstall
|
||||
curl -O https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/configs/archinstall.json
|
||||
curl -O https://raw.githubusercontent.com/sudolulo/arch-turnkey/master/configs/archinstall.json
|
||||
archinstall --config archinstall.json
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Site config — provisioning your own machines
|
||||
|
||||
arch-turnkey installs in two phases:
|
||||
|
||||
| Phase | What it does | Needs a site config? |
|
||||
|---|---|---|
|
||||
| 1 · Base | archinstall: LUKS + btrfs subvolumes + snapper + yay | no |
|
||||
| 2 · Personal | unlock vault → register SSH key with your forge → clone dotfiles → `yadm bootstrap` | **yes** |
|
||||
|
||||
Phase 1 is generic and works for anyone straight from a clone. Phase 2 is inherently
|
||||
personal — it needs *your* forge, *your* vault, *your* dotfiles — so none of it is baked
|
||||
into this repo. Instead it reads a **`site.env`** that you supply. With no `site.env`,
|
||||
firstboot prints what it's skipping and stops with a complete, clean Arch system.
|
||||
|
||||
`site.env` is gitignored. It should never be committed.
|
||||
|
||||
## Setting one up
|
||||
|
||||
Copy the template and fill it in:
|
||||
|
||||
```
|
||||
cp configs/site.env.example site.env
|
||||
$EDITOR site.env
|
||||
```
|
||||
|
||||
You need, at minimum, `DOTFILES_REPO` — a yadm repo carrying `~/.config/pkglist/`
|
||||
(`common` + `class-*` + per-host). Unset, phase 2 is skipped entirely. The forge variables
|
||||
exist so the freshly-generated SSH key on the new machine can be registered with your forge
|
||||
*before* the dotfiles clone is attempted, and the vault variables collapse the two secrets
|
||||
phase 2 needs (a forge token, the archive passphrase) behind a single master-password prompt.
|
||||
|
||||
## Getting it onto the machine
|
||||
|
||||
`bootstrap.sh` installs `site.env` to `/etc/arch-turnkey/site.env` (mode 0600) once
|
||||
archinstall finishes; `firstboot.sh` sources it on first boot. Three ways to get it there:
|
||||
|
||||
**Unattended — bake it into an ISO.** Put it at `iso/site.env` and build:
|
||||
|
||||
```
|
||||
cd iso && ./build.sh
|
||||
```
|
||||
|
||||
Booting that ISO auto-runs bootstrap, which finds the config at `/root/site.env`. This is
|
||||
the near-zero-typing path: boot, answer archinstall's disk/user prompts, enter your vault
|
||||
master password once, done.
|
||||
|
||||
**Ad hoc — drop it on the live ISO.** Before running bootstrap, write it to `/root/site.env`.
|
||||
|
||||
**Anywhere else.** Point `ARCH_TURNKEY_SITE` at it:
|
||||
|
||||
```
|
||||
ARCH_TURNKEY_SITE=/mnt/usb/site.env bash bootstrap.sh
|
||||
```
|
||||
|
||||
## Not depending on GitHub at install time
|
||||
|
||||
`bootstrap.sh` fetches the rest of the tree from the public GitHub mirror by default, so a
|
||||
plain `curl | bash` works for anyone. If you'd rather the install pull from your own forge,
|
||||
set `ARCH_TURNKEY_BASE` (in `site.env` or the environment):
|
||||
|
||||
```
|
||||
ARCH_TURNKEY_BASE="https://git.example.com/you/arch-turnkey/raw/branch/master"
|
||||
```
|
||||
|
||||
Everything downstream — the archinstall config's `custom-commands`, `firstboot.sh`, the
|
||||
systemd unit — is fetched relative to that one root, so a single variable re-points the
|
||||
whole install at a mirror you control.
|
||||
@@ -5,5 +5,5 @@
|
||||
# nginx -t && systemctl reload nginx
|
||||
|
||||
location = /arch {
|
||||
return 302 https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/bootstrap.sh;
|
||||
return 302 https://raw.githubusercontent.com/sudolulo/arch-turnkey/master/bootstrap.sh;
|
||||
}
|
||||
|
||||
+30
-7
@@ -10,12 +10,20 @@
|
||||
# Fallback: if rbw/Vaultwarden is unavailable, it drops back to manual paste.
|
||||
set -euo pipefail
|
||||
|
||||
DOTFILES="ssh://git@git.onetick.ninja:55214/flan/dotfiles.git"
|
||||
GITEA_API="https://git.onetick.ninja/api/v1"
|
||||
GITEA_SSH_HOST="git.onetick.ninja"
|
||||
GITEA_SSH_PORT=55214
|
||||
# Vaultwarden URL + account email — set via env, else prompted (no PII in the repo)
|
||||
VAULT_URL="${VAULT_URL:-https://pass.onetick.ninja}"
|
||||
# Site config — YOUR forge, dotfiles and vault. Never committed: it is written to the
|
||||
# installed system by bootstrap.sh from a site.env you supply (see docs/SITE.md), so this
|
||||
# repo carries no personal infrastructure. Absent => the whole phase-2 provisioning block
|
||||
# below is skipped and you get a clean generic Arch install.
|
||||
SITE_CONF="${SITE_CONF:-/etc/arch-turnkey/site.env}"
|
||||
# shellcheck source=/dev/null
|
||||
[ -r "$SITE_CONF" ] && . "$SITE_CONF"
|
||||
|
||||
DOTFILES="${DOTFILES_REPO:-}" # yadm repo; empty => skip provisioning
|
||||
GITEA_API="${FORGE_API:-}" # e.g. https://git.example.com/api/v1
|
||||
GITEA_SSH_HOST="${FORGE_SSH_HOST:-}"
|
||||
GITEA_SSH_PORT="${FORGE_SSH_PORT:-22}"
|
||||
# Vaultwarden URL + account email — set via site.env/env, else prompted (no PII in the repo)
|
||||
VAULT_URL="${VAULT_URL:-}"
|
||||
VAULT_EMAIL="${VAULT_EMAIL:-}"
|
||||
LOG="$HOME/firstboot.log" # user-writable (firstboot runs as the user)
|
||||
exec > >(tee -a "$LOG") 2>&1 # QoL: log everything
|
||||
@@ -23,7 +31,8 @@ exec > >(tee -a "$LOG") 2>&1 # QoL: log everything
|
||||
# populated as we go
|
||||
TOKEN="" ARCHIVE_PASS="" CLASS="" HOST=""
|
||||
|
||||
net_up() { ping -c1 -W3 "$GITEA_SSH_HOST" >/dev/null 2>&1; }
|
||||
# Reach for the configured forge when there is one; otherwise just prove we have internet.
|
||||
net_up() { ping -c1 -W3 "${GITEA_SSH_HOST:-archlinux.org}" >/dev/null 2>&1; }
|
||||
|
||||
# --- network: prompt + help connect if needed (nmtui, since NetworkManager is installed) ---
|
||||
require_network() {
|
||||
@@ -140,6 +149,20 @@ main() {
|
||||
HOST="$(hostnamectl hostname)"
|
||||
|
||||
install_yay
|
||||
|
||||
# Phase 2 is personal: unlock a vault, register this key with your forge, clone your
|
||||
# dotfiles. It only runs when a site.env named a dotfiles repo. Without one the generic
|
||||
# install is already complete, so stop here rather than prompting for infrastructure
|
||||
# the operator does not have.
|
||||
if [ -z "$DOTFILES" ]; then
|
||||
echo
|
||||
echo ">> No site config — skipping dotfiles/secret provisioning."
|
||||
echo " The base system is complete: LUKS + btrfs + snapper + yay, class=$CLASS."
|
||||
echo " To provision your own dotfiles and secrets, see docs/SITE.md."
|
||||
echo "== Done. Class=$CLASS Host=$HOST. Reboot into your session. Log: $LOG =="
|
||||
return
|
||||
fi
|
||||
|
||||
load_secrets
|
||||
register_ssh_key
|
||||
pull_dotfiles
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
out/
|
||||
*.iso
|
||||
|
||||
# Personal site config — never commit (see configs/site.env.example)
|
||||
site.env
|
||||
|
||||
@@ -10,5 +10,5 @@ if [ "$(tty)" = /dev/tty1 ]; then
|
||||
echo
|
||||
printf "Press Enter to install, or Ctrl-C for a shell... "
|
||||
read -r _ || true
|
||||
curl -fsSL https://git.onetick.ninja/flan/arch-turnkey/raw/branch/master/bootstrap.sh | bash
|
||||
curl -fsSL https://raw.githubusercontent.com/sudolulo/arch-turnkey/master/bootstrap.sh | bash
|
||||
fi
|
||||
|
||||
+12
-1
@@ -17,12 +17,23 @@ rm -rf "$WORK" "$PROFILE"
|
||||
cp -r /usr/share/archiso/configs/releng "$PROFILE"
|
||||
install -Dm755 airootfs/root/.zprofile "$PROFILE/airootfs/root/.zprofile"
|
||||
|
||||
# Bake in the personal site config if there is one (iso/site.env, gitignored). bootstrap.sh
|
||||
# picks it up at /root/site.env, so booting this ISO provisions dotfiles + secrets with no
|
||||
# extra typing. Without it the ISO is a clean generic installer — which is what anyone
|
||||
# building from a fresh clone gets. See docs/SITE.md.
|
||||
if [ -f site.env ]; then
|
||||
install -Dm600 site.env "$PROFILE/airootfs/root/site.env"
|
||||
echo ">> Baked site.env into the ISO — personal provisioning enabled."
|
||||
else
|
||||
echo ">> No iso/site.env — building a generic ISO (no personal provisioning)."
|
||||
fi
|
||||
|
||||
# Distinct branding so it's clearly the turnkey ISO, not a stock Arch one.
|
||||
LABEL="TURNKEY_$(date +%Y%m)" # date-stamped ISO9660 volume label, injected as a literal
|
||||
sed -i \
|
||||
-e 's/^iso_name=.*/iso_name="arch-turnkey"/' \
|
||||
-e "s/^iso_label=.*/iso_label=\"$LABEL\"/" \
|
||||
-e 's/^iso_publisher=.*/iso_publisher="flan <https:\/\/git.onetick.ninja\/flan\/arch-turnkey>"/' \
|
||||
-e 's/^iso_publisher=.*/iso_publisher="flan <https:\/\/github.com\/sudolulo\/arch-turnkey>"/' \
|
||||
-e 's/^iso_application=.*/iso_application="arch-turnkey installer"/' \
|
||||
"$PROFILE/profiledef.sh"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user