#!/bin/sh # bootstrap-alpine.sh — One-shot setup for Haskell build machine on Alpine Linux # # Usage (from workstation): # scp bootstrap-alpine.sh root@:/tmp/ # ssh root@ sh /tmp/bootstrap-alpine.sh # # What it does: # 1. Installs system dependencies (apk) # 2. Creates 'build' user with sudo # 3. Installs GHCup + GHC + Cabal (single version — 8GB disk constraint) # 4. Installs build-agent + OpenRC service # 5. Configures SSH for key-based access # # Disk budget (8GB total): # Alpine base: ~200 MB # Build deps: ~300 MB # GHCup + GHC: ~1800 MB # Cabal + pkgdb: ~300 MB # Headroom: ~5400 MB (for project builds) set -eu GHC_VERSION="${GHC_VERSION:-9.8.4}" CABAL_VERSION="${CABAL_VERSION:-3.12.1.0}" echo "=== Haskell Build Machine Bootstrap (Alpine) ===" echo "GHC: ${GHC_VERSION} | Cabal: ${CABAL_VERSION}" echo "" # ---- 1. System packages ---- echo "[1/5] Installing system packages..." apk update apk add \ build-base curl git \ gmp-dev libffi-dev zlib-dev ncurses-dev \ pkgconf openssh autossh jq rsync \ sudo shadow python3 \ musl-dev gcc g++ make \ linux-headers \ xz tar # ---- 2. Build user ---- echo "[2/5] Creating build user..." if ! id build >/dev/null 2>&1; then adduser -D -s /bin/sh -h /home/build build echo "build ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/build chmod 440 /etc/sudoers.d/build fi # Create build workspace mkdir -p /build chown build:build /build # SSH directory mkdir -p /home/build/.ssh chmod 700 /home/build/.ssh chown build:build /home/build/.ssh # ---- 3. Haskell toolchain ---- echo "[3/5] Installing Haskell toolchain (this takes a while)..." # GHCup needs these env vars for non-interactive install export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 export BOOTSTRAP_HASKELL_GHC_VERSION="$GHC_VERSION" export BOOTSTRAP_HASKELL_CABAL_VERSION="$CABAL_VERSION" export BOOTSTRAP_HASKELL_INSTALL_STACK=0 export BOOTSTRAP_HASKELL_INSTALL_HLS=0 # Install GHCup as build user su - build -c " export BOOTSTRAP_HASKELL_NONINTERACTIVE=1 export BOOTSTRAP_HASKELL_GHC_VERSION='$GHC_VERSION' export BOOTSTRAP_HASKELL_CABAL_VERSION='$CABAL_VERSION' export BOOTSTRAP_HASKELL_INSTALL_STACK=0 export BOOTSTRAP_HASKELL_INSTALL_HLS=0 curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh " # Add ghcup to build user's profile cat >> /home/build/.profile << 'PROFILE' [ -f "$HOME/.ghcup/env" ] && . "$HOME/.ghcup/env" PROFILE # Verify installation su - build -c '. ~/.ghcup/env && ghc --version && cabal --version' # Pre-warm cabal package index echo "[3/5] Warming cabal package index..." su - build -c '. ~/.ghcup/env && cabal update' # ---- 4. Build agent ---- echo "[4/5] Installing build-agent..." # The agent script will be copied separately via SCP # Here we just set up the OpenRC service skeleton cat > /etc/init.d/build-agent << 'INITD' #!/sbin/openrc-run name="build-agent" description="Haskell Build Agent — State Hub registration + SSH reverse tunnel" command="/usr/local/bin/build-agent" command_user="build" command_background=true pidfile="/run/${RC_SVCNAME}.pid" output_log="/var/log/build-agent.log" error_log="/var/log/build-agent.log" depend() { need net after firewall } INITD chmod 755 /etc/init.d/build-agent # Create placeholder env file if [ ! -f /etc/build-agent.env ]; then cat > /etc/build-agent.env << 'ENV' # Custodian State Hub — access via forward tunnel STATE_HUB_URL=http://127.0.0.1:18000 STATE_HUB_DOMAIN=railiance SSH_RELAY_HOST= SSH_RELAY_USER=worsch SSH_KEY_PATH=/home/build/.ssh/id_build REMOTE_PORT=12222 ENV chmod 600 /etc/build-agent.env fi # Enable on boot rc-update add build-agent default # ---- 5. SSH hardening ---- echo "[5/5] Configuring SSH..." # Enable and start sshd rc-update add sshd default rc-service sshd start 2>/dev/null || true # Harden (will take effect after key injection) sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config echo "" echo "=== Bootstrap complete ===" echo "" echo "Disk usage:" df -h / | tail -1 echo "" echo "GHC location: /home/build/.ghcup/" su - build -c '. ~/.ghcup/env && ghc --version' echo "" echo "Next steps:" echo " 1. Copy build-agent.py: scp build-agent.py root@:/usr/local/bin/build-agent" echo " 2. Copy SSH keys: scp id_build root@:/home/build/.ssh/" echo " 3. Edit env: ssh root@ vi /etc/build-agent.env" echo " 4. Start agent: ssh root@ rc-service build-agent start" echo " 5. Disable root login: ssh root@ 'sed -i s/PermitRootLogin.*/PermitRootLogin no/ /etc/ssh/sshd_config && rc-service sshd restart'"