#!/usr/bin/env bash # Built standing on the shoulders of billions of dwarves # Created by John A. Hoeven with the ethical assistance of Claude AI # Licence: The Unlicense — https://unlicense.org # --------------------------------------------------------------------------- # configure-llama-firewall.sh # Version: v0.0.1 | Status: DEVELOPMENT # Role: Orchestrator # --------------------------------------------------------------------------- # Purpose: Open the reverse-proxy HTTP port for llama-server's web UI, # restricted to the same two LAN ranges already used for SSH # (see the kickstart's own firewalld zone file). This was # previously only run as one-off firewall-cmd commands typed # directly during the Phase 5 deployment session — never # tracked anywhere. This script is that missing artifact. # Target: BigBoy — AlmaLinux 10.2, firewalld already enabled from the # kickstart (SSH rules already in place via a direct zone-file # write during install — see CLAUDE.md for why firewall-cmd # itself doesn't work inside a kickstart %post chroot; that # limitation does not apply here, since this runs against a # live, fully-booted system with firewalld actually running). # Entry: ./configure-llama-firewall.sh # Depends: firewalld (already enabled) # Note: Idempotent — firewall-cmd's --add-rich-rule is itself # idempotent (adding an already-present rule is a no-op, not # an error), so this is safe to re-run. # --------------------------------------------------------------------------- # No 'set -e' — every operation checked and logged explicitly (CE OS standard §1.8). # ── Configuration ──────────────────────────────────────────────────────── # # These ranges must match firewall_allowed_ranges in # ansible/group_vars/bigboy.yml — if that file is ever updated, update # this script too, or better, formalize this into an actual Ansible role # that reads the variable directly rather than duplicating the values # here (see ansible/README.md's "Known Deferred" section — Phase 4/5 # aren't yet real roles, this script is the same category of gap). ALLOWED_RANGES=( "192.168.1.0/24" # home LAN — current "192.168.0.0/24" # workbench bench LAN — used during initial deploy ) HTTP_ZONE="public" LOG_DIR="${HOME}/.local/logs/firewall-config" LOG_FILE="${LOG_DIR}/config-$(date '+%Y-%m-%d_%H%M%S').log" mkdir -p "${LOG_DIR}" _log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "${1}" | tee -a "${LOG_FILE}" } # ── Pre-flight ─────────────────────────────────────────────────────────── if [[ "${EUID}" -eq 0 ]]; then _log "ERROR: do not run this as root directly — it invokes sudo only" _log "for the specific steps that need it." exit 1 fi if ! systemctl is-active --quiet firewalld; then _log "ERROR: firewalld is not active. This script assumes it's already" _log "enabled (per the kickstart). Check 'systemctl status firewalld'" _log "before proceeding — something else is wrong if it's not running." exit 1 fi # ── Confirmation ───────────────────────────────────────────────────────── _log "About to open HTTP (port 80, llama-server's reverse proxy) for:" for range in "${ALLOWED_RANGES[@]}"; do _log " - ${range}" done printf 'Proceed? [y/N] ' read -r response case "${response}" in [yY]|[yY][eE][sS]) ;; *) _log "Aborted by user."; exit 0 ;; esac # ── Apply rules ────────────────────────────────────────────────────────── for range in "${ALLOWED_RANGES[@]}"; do _log "Adding rich rule for ${range}" if ! sudo firewall-cmd --permanent --zone="${HTTP_ZONE}" \ --add-rich-rule="rule family=\"ipv4\" source address=\"${range}\" service name=\"http\" accept" \ >>"${LOG_FILE}" 2>&1; then _log "ERROR: failed to add rich rule for ${range} — see ${LOG_FILE}" exit 1 fi done _log "Reloading firewalld to apply changes" if ! sudo firewall-cmd --reload >>"${LOG_FILE}" 2>&1; then _log "ERROR: firewall-cmd --reload failed — see ${LOG_FILE}" exit 1 fi _log "" _log "Done. Confirm with:" _log " sudo firewall-cmd --zone=${HTTP_ZONE} --list-rich-rules" _log "" _log "Full log: ${LOG_FILE}"