furtka/iso/README.md
Daniel Maksymilian Syrnicki 0e05bef667 fix(console): always show the IP fallback on the welcome banner
The /etc/issue banner on both the live ISO and the installed system was a
oneshot after network-online.target that simply dropped the http://<ip>
line when DHCP hadn't delivered yet — so a slow NIC (first hardware bench,
2026-08-24) left the screen offering only proksi.local. On the ISO,
releng's root autologin on tty1 also made agetty --reload a no-op.

- furtka-issue.timer (ISO) / furtka-welcome.timer (target) re-run the
  banner script every 5 s; services drop RemainAfterExit + network wait
- fallback line is always present: every global IPv4, or a 'no IP yet —
  check cable / DHCP' hint; only rewrites + reloads on change
- build.sh removes releng's getty@tty1 autologin drop-in; banner tells
  users a shell is root + Enter away

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MScAinbyMdeNc7H2BZdnnG
2026-08-25 15:18:39 +02:00

67 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Live ISO build
Builds a bootable Arch-based live ISO that auto-starts the Flask webinstaller from `../webinstaller/` on boot. User plugs in a USB, boots, and the installer wizard comes up on `http://proksi.local:5000` (or the raw IP shown on the console).
Runnable locally (below) or through Forgejo Actions — `.forgejo/workflows/build-iso.yml` builds on every push to `main` and on manual `workflow_dispatch`. The ISO lands as an artifact named `furtka-iso`, retained for 14 days. Feature branches don't trigger the ISO build; see `memory/project_ci_branching` for why.
## Run a build locally
Needs a host with Docker. Disk space required: ~15 GB scratch during the build, ~1.5 GB for the final ISO.
```bash
./iso/build.sh
```
Output ISO ends up in `iso/out/furtka-<date>-x86_64.iso`. Around 310 min on a 4-core VM. First run is slower because it pulls `archlinux:latest` and all packages from upstream.
The script re-execs itself inside a privileged `archlinux:latest` container. That's so `mkarchiso` has root + loop-mount access without polluting the host — Ubuntu hosts don't ship archiso natively anyway.
## What gets baked in
The build starts from Arch's stock `releng` profile (the same one used to build the official Arch ISO), then overlays our customizations from `overlay/`:
| Overlay file | Effect |
|----------------------------------------------|----------------------------------------------------------------------------------|
| `overlay/packages.extra` | Appended to the package list. Adds `python`, `python-flask`, `avahi`, `nss-mdns` |
| `overlay/profiledef.sh` | Appended to `profiledef.sh`. Renames the ISO to `furtka-*` with a dated version |
| `overlay/airootfs/opt/furtka/` | Directory where `webinstaller/` is copied at build time |
| `overlay/airootfs/etc/hostname` | Live-ISO hostname (`proksi`) so mDNS advertises the installer as `proksi.local` |
| `overlay/airootfs/etc/issue` | Welcome banner on the TTY pointing users at `http://proksi.local:5000` |
| `overlay/airootfs/usr/local/bin/furtka-update-issue` | Rewrites `/etc/issue` at runtime so the banner always carries a fallback line: the DHCP-assigned IP(s), or a "no IP yet — check cable / DHCP" hint until one arrives. Only touches the file (and `agetty --reload`s) when the text changed |
| `overlay/airootfs/etc/systemd/system/` | `furtka-webinstaller.service` (Flask on :5000) + `furtka-issue.service` (banner-updater, first run at boot) in `multi-user.target.wants/`, and `furtka-issue.timer` (re-runs the updater every 5 s) in `timers.target.wants/` |
The systemd service runs `flask --app app run --host 0.0.0.0 --port 5000` under `/opt/furtka`. The `0.0.0.0` binding is important — the Flask default is localhost-only, which wouldn't be reachable from another machine on the LAN.
mDNS is wired: `avahi-daemon` + `nss-mdns` come from `packages.extra`, the live ISO's hostname is `proksi`, and as soon as `systemd-networkd-wait-online` fires the installer is reachable at `http://proksi.local:5000`. The raw IP still shows on the console for fallback — some Windows clients need the Bonjour service for `.local` to resolve at all.
`build.sh` also deletes releng's `getty@tty1.service.d/autologin.conf`. With root auto-logged-in, tty1 belongs to a shell and `agetty --reload` can't redraw the banner, so the IP line was a snapshot of whatever DHCP had done by the time getty started. Without autologin tty1 sits at the login prompt showing the live banner; for a shell type `root` + Enter (releng's root has no password) — the banner says so.
## Test flow
1. Build: `./iso/build.sh`
2. Copy the ISO to your Proxmox host's ISO storage (typically `/var/lib/vz/template/iso/`). Browser uploads of 1.5 GB truncate silently — prefer `scp` over the Proxmox WebUI.
3. Create a VM with:
- 2 vCPU, 4 GB RAM, 20 GB disk (empty)
- **BIOS: OVMF (UEFI)**, add EFI Disk on `local-lvm`. SeaBIOS fails to load `ldlinux.c32` from our ISO; only the UEFI path works reliably.
- **Secure Boot disabled**. Our GRUB isn't signed, so Secure Boot rejects it with `Access Denied`. Either boot into OVMF setup (Esc during boot) → Device Manager → Secure Boot Configuration → Attempt Secure Boot [ ] → F10 → reboot. Or remove the EFI Disk and re-add it with "Pre-Enroll keys" unchecked.
- CD-ROM attached with the Furtka ISO
- Boot order: CD before disk
- Network: same bridge as your LAN, DHCP
4. Start the VM. Wait ~30 s for boot.
5. Find its IP in Proxmox's VM summary (or your router's DHCP table)
6. Open `http://<vm-ip>:5000` — the existing 3-screen wizard should be there
## What you see after install + reboot
Once `archinstall` finishes and you click **Reboot now**, the VM comes up into the installed system. No more port `:5000` — the wizard ISO is gone. Instead:
- **Console**: agetty shows `Furtka is ready. Open http://<hostname>.local …` with the IP fallback underneath.
- **Browser** at `http://<hostname>.local` (default `http://furtka.local` — the form's default hostname is `furtka`; only the live-installer ISO uses `proksi`): Caddy-served landing page with three live status tiles (uptime, Docker version, free disk) refreshed every 30 s by `furtka-status.timer`. HTTPS is opt-in (26.15-alpha) — flip the toggle in `/settings` to switch on Caddy's `tls internal` on `:443`, then trust `rootCA.crt` from `/settings` to clear browser warnings.
- **SSH**: `ssh <user>@<hostname>.local` works; `docker ps` works without `sudo` because the user is in the `docker` group.
This is a demo shell — no Authentik, no app store yet. The landing page lives at `/srv/furtka/www/`, served by Caddy on `:80` per `/etc/caddy/Caddyfile`. All of this is written into the target by `webinstaller/app.py`'s `_post_install_commands` via archinstall's `custom_commands`.
## Known rough edges
- **Disk space**: the first time you build on a fresh host, the squashfs/xorriso steps need ~15 GB free. If the host's LVM-root is smaller, `xorriso` silently dies at the very end with "Image size exceeds free space on media".
- **Live-installer wizard is still HTTP-only**. `http://proksi.local:5000` during install has no TLS; once the box reboots, Caddy can serve `tls internal` on `:443` if the user opts in via `/settings` (26.15-alpha), but bringing TLS to the wizard itself is a later milestone.