wrolpi/debian-live-config/config/hooks/normal/9999-wrolpi.hook.chroot
lrnselfreliance f644a80c9a
Debian installs create the user the installer asks for (#834)
The ISO's chroot hook created wrolpi as a login user (UID 1000, password
"wrolpi", full sudo, LightDM autologin).  The Debian installer creates the
user it asks for only when no UID 1000-59999 user exists, so it silently
skipped that user: the only login on an installed system was wrolpi/wrolpi,
with SSH and sudo.

wrolpi is now a system account with no password or login sudo, as on every
other WROLPi deployment.  The installer creates the user it asks for (with
sudo, no autologin).  The live session logs in as live-config's "user".
Desktop shortcuts moved to /etc/skel so every user gets them.

check-iso.sh now fails the build when the image has a UID 1000-59999 user.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:23:40 -06:00

219 lines
9.4 KiB
Bash

#!/usr/bin/bash
# Chroot hook for the combined WROLPi Live + Installer ISO.
#
# Composes the full WROLPi stack into the live system during live-build's
# chroot phase. The resulting squashfs is what runs in live mode AND what
# the Calamares installer (phase 2) will copy onto the target disk, so any
# customisation here applies to both deployments.
set -e
set -x
# Generate and set default locale to avoid perl warnings.
# Users can change this later via: sudo dpkg-reconfigure locales
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
echo 'LANG=en_US.UTF-8' > /etc/default/locale
export LANG=en_US.UTF-8
cat >>/etc/profile <<'EOF'
alias ll='ls -lh'
alias la='ll -a'
EOF
# The wrolpi service account owns /media/wrolpi and runs the wrolpi-*
# services, as on every other WROLPi deployment. It has no password and no
# login sudo; 90-wrolpi grants it specific commands only. It must be a
# system account (UID < 1000): the Debian installer creates the user it asks
# for only when no UID 1000-59999 user exists (see check-iso.sh). People log
# in as that installer user; the live session's "user" comes from live-config
# (live-config.username in build.sh), which also sets up its autologin.
useradd --system -m -d /home/wrolpi -s /bin/bash -U wrolpi
# Rebrand from generic Debian so users don't think they have the wrong USB.
echo "wrolpi" > /etc/hostname
sed -i 's/127\.0\.1\.1.*/127.0.1.1\twrolpi/' /etc/hosts 2>/dev/null || \
echo "127.0.1.1 wrolpi" >> /etc/hosts
cat > /etc/issue <<'EOF'
WROLPi \n (\l)
EOF
cat > /etc/issue.net <<'EOF'
WROLPi %h
EOF
git --version
WROLPI_BRANCH="release"
[ -f /opt/wrolpi-branch ] && WROLPI_BRANCH=$(cat /opt/wrolpi-branch)
git clone -b "${WROLPI_BRANCH}" https://github.com/lrnselfreliance/wrolpi.git /opt/wrolpi
rm -f /opt/wrolpi-branch
# Install webapp.
cd /opt/wrolpi/app
npm --version
node --version
npm install
# `npm install` rewrites the tracked app/package-lock.json. build_app.sh (below)
# stamps build/.build-stamp from that working tree, but on-device repair.sh runs
# `git reset --hard` before it stamps -- reverting the lock -- so a stamp baked
# against the dirty lock can never match on first boot and the box rebuilds the app.
# Reset to HEAD here, exactly as repair.sh does, so the baked stamp represents
# committed source. Only the lock is reverted; build/, node_modules, and the venvs
# are untracked and survive. HOME is set explicitly so `git config --global`
# cannot fail (and abort the build under `set -e`) when the chroot leaves HOME
# unset; safe.directory avoids git's "dubious ownership" refusal.
export HOME=/root
git config --global --add safe.directory /opt/wrolpi
git -C /opt/wrolpi reset --hard HEAD
# Build the React production bundle. Without this, /opt/wrolpi/app/build/
# does not exist and wrolpi-app.service (`serve build -l 3000 ...`) returns
# 404 for every request. Baking the build into the squashfs means the
# live image works offline immediately and the installed deployment skips
# a multi-minute first-boot step. build_app.sh also writes build/.build-stamp
# so the on-device repair.sh sees this build as current and never rebuilds it.
/opt/wrolpi/scripts/build_app.sh
# Install Node console commands.
npm i -g serve@12.0.1 single-file-cli@2.0.73 readability-extractor@0.0.6
# Install Deno runtime. Hash pinned to detect tampering of the upstream release artifact.
DENO_VERSION="v2.8.3"
DENO_SHA256="30455b845ffa6082209c3590269c910ad3b7efdf28c9879afd4006c47ae54197"
curl -fsSL "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip" -o /tmp/deno.zip
echo "${DENO_SHA256} /tmp/deno.zip" | sha256sum -c -
unzip /tmp/deno.zip -d /usr/local/bin/
chmod +x /usr/local/bin/deno
rm /tmp/deno.zip
deno --version
# Install Python requirements. Try multiple times because pypi may stop responding.
python3 --version
python3 -m venv /opt/wrolpi/venv
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt ||
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt ||
/opt/wrolpi/venv/bin/pip3 install -r /opt/wrolpi/requirements.txt
# Install Controller with separate venv.
echo "Pre-installing WROLPi Controller..."
python3 -m venv /opt/wrolpi/controller/venv
/opt/wrolpi/controller/venv/bin/pip install --upgrade pip
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt ||
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt ||
/opt/wrolpi/controller/venv/bin/pip install -r /opt/wrolpi/controller/requirements.txt
echo "Controller pre-installed"
# Configure Caddy. Caddy itself comes from package-lists/ (repository in
# config/archives/), never apt-get here: the Debian installer purges packages
# that a hook installs.
mkdir -p /etc/caddy
cp /opt/wrolpi/etc/raspberrypios/Caddyfile /etc/caddy/Caddyfile
mkdir -p /var/www
cp /opt/wrolpi/etc/raspberrypios/50x.html /var/www/50x.html
# WROLPi needs a few privileged commands.
cp /opt/wrolpi/etc/raspberrypios/90-wrolpi /etc/sudoers.d/90-wrolpi
chmod 0440 /etc/sudoers.d/90-wrolpi
# Verify this new file is valid.
visudo -c -f /etc/sudoers.d/90-wrolpi
mkdir /media/wrolpi
# Verify blob files were copied in by includes.chroot (downloaded by build.sh).
[[ -f /opt/wrolpi-blobs/map-fonts.tar.gz && -s /opt/wrolpi-blobs/map-fonts.tar.gz ]] || \
(echo "ERROR: map-fonts.tar.gz missing from /opt/wrolpi-blobs!" && exit 1)
[[ -f /opt/wrolpi-blobs/map-overview.pmtiles && -s /opt/wrolpi-blobs/map-overview.pmtiles ]] || \
(echo "ERROR: map-overview.pmtiles missing from /opt/wrolpi-blobs!" && exit 1)
# Ship committed blobs (e.g. the default Zim served by Kiwix when the media dir
# has no working zims) into /opt/wrolpi-blobs.
cp /opt/wrolpi/wrolpi/blobs/* /opt/wrolpi-blobs/
# Bake in the map toolchain (pmtiles CLI + tippecanoe) so first boot does not
# need internet. Shared with pi-gen and on-device upgrades via a single script
# so versions/pins live in one place. It is safe in the chroot: unlike
# install_protomaps.sh it contains no migration logic that needs a running
# systemd.
/opt/wrolpi/wrolpi/scripts/install_map_tools.sh
# Install WROLPi Help.
/opt/wrolpi/scripts/install_help_service.sh
cp /opt/wrolpi/etc/raspberrypios/wrolpi-*.service /etc/systemd/system/
cp /opt/wrolpi/etc/raspberrypios/wrolpi-*.timer /etc/systemd/system/
cp /opt/wrolpi/etc/raspberrypios/wrolpi.target /etc/systemd/system/
# Copy MOTD scripts, delete original.
cp /opt/wrolpi/etc/raspberrypios/motd/20-wrolpi.motd /etc/update-motd.d/20-wrolpi
cp /opt/wrolpi/etc/raspberrypios/motd/30-wrolpi-first.motd /etc/update-motd.d/30-wrolpi
chmod +x /etc/update-motd.d/*
truncate -s 0 /etc/motd
# NetworkManager for the hotspot.
systemctl enable NetworkManager
# Enable iperf3 for speed testing.
systemctl enable iperf3
# Enable first startup script to generate certificates and start Caddy.
systemctl enable wrolpi-first-startup.service
# Enable Controller service to start on boot.
systemctl enable wrolpi-controller.service
# Apply controller.yaml drives.mounts at boot (replaces /etc/fstab for
# secondary mounts so the mount table travels with the drive).
systemctl enable wrolpi-mounts.service
# Enable weekly certificate renewal check.
systemctl enable wrolpi-cert-renew.timer
# Bootstrap service: on a Live USB it creates the persistence partition on
# first boot; on every boot (Live or Installed) it ensures the config
# directory exists and the wrolpi-* runtime services are enabled. The
# SQLite database is created by the API itself on first start (it lives in
# /media/wrolpi/config/wrolpi.db). Installed deployments find no live
# medium and short-circuit the partition step.
chmod +x /usr/local/sbin/wrolpi-bootstrap.sh
systemctl enable wrolpi-bootstrap.service
# Plymouth — replace Debian-branded splash with a minimal theme. live-build's
# initramfs regeneration at the end of the chroot phase will pick up this
# change so the new theme is used at boot.
plymouth-set-default-theme text || true
# LightDM greeter — add a WROLPi banner so the login screen identifies the
# system instead of looking like a stock Debian.
mkdir -p /etc/lightdm/lightdm-gtk-greeter.conf.d
cat > /etc/lightdm/lightdm-gtk-greeter.conf.d/50-wrolpi.conf <<'EOF'
[greeter]
banner-message-enable=true
banner-message-text=WROLPi
EOF
# Desktop shortcuts to WROLPi's services, in /etc/skel so every login user
# gets them: the live session's "user" and the user the installer creates.
# Use the canonical .desktop files from the repo (etc/raspberrypios/) so
# updates to those files propagate to the live ISO without touching the
# chroot hook. wrolpi-trust-desktop-icons.desktop autostart marks each as
# trusted at session start so XFCE will launch them on double-click.
mkdir -p /etc/skel/Desktop
cp /opt/wrolpi/etc/raspberrypios/wrolpi.desktop /etc/skel/Desktop/
cp /opt/wrolpi/etc/raspberrypios/controller.desktop /etc/skel/Desktop/
cp /opt/wrolpi/etc/raspberrypios/wrolpi-help.desktop /etc/skel/Desktop/
cp /opt/wrolpi/etc/raspberrypios/wrolpi-help-files.desktop /etc/skel/Desktop/
chmod +x /etc/skel/Desktop/*.desktop
# Protect blob files from accidental modification or deletion.
if [ -d /opt/wrolpi-blobs ] && ls /opt/wrolpi-blobs/* >/dev/null 2>&1; then
chown -R root:root /opt/wrolpi-blobs
chmod 444 /opt/wrolpi-blobs/*
chattr +i /opt/wrolpi-blobs/*
fi
set +x
echo
echo "======================================================================"
echo "9999-wrolpi.hook.chroot completed"
echo "======================================================================"
echo