#!/bin/bash # ansipa-portal-auth-setup.sh — generate the HTTP Basic Auth credential file # that gates the ansipa nginx gateway (portal + every proxied web UI). # # Runs on every container start via ansipa-portal-auth.service so the # htpasswd file is always in place after container recreation (rootfs is # ephemeral; only /data survives). The file itself is written to the shared # 'portal-auth' volume so the nginx container (which has no access to /data) # can read it — same pattern as the cmk-creds volume shared with checkmk. # # This is a DELIBERATE extra sign-in layer in front of the whole gateway — # FreeIPA, CheckMK and Keycloak all still enforce their own logins behind it. # Two prompts beats one attacker who only had to beat one lock. # # Password sources (first match wins): # 1. ANSIPA_PORTAL_PASSWORD env var (first boot / explicit override) # 2. /data/portal-auth.env (persisted from first boot) # 3. auto-generated, logged once (so nothing is silently left with a # predictable/blank credential) # # To add more accounts later, hand-edit the persisted file directly: # printf 'alice:%s\n' "$(openssl passwd -apr1 'their password')" \ # >> /data/portal-auth/htpasswd (then re-copy to the shared volume, or # just re-run this script with ANSIPA_PORTAL_USER=alice ANSIPA_PORTAL_PASSWORD=... # — it appends/updates rather than truncating). set -euo pipefail LOG_TAG="ansipa-portal-auth-setup" ENV_FILE="/data/portal-auth.env" PERSIST_HTPASSWD="/data/portal-auth/htpasswd" SHARED_HTPASSWD="/portal-auth/htpasswd" # mounted from the portal-auth docker volume log() { echo "[$LOG_TAG] $*"; } warn() { echo "[$LOG_TAG][WARN] $*" >&2; } mkdir -p "$(dirname "$PERSIST_HTPASSWD")" /portal-auth PORTAL_USER="${ANSIPA_PORTAL_USER:-}" PORTAL_PASS="${ANSIPA_PORTAL_PASSWORD:-}" if [[ -f "$ENV_FILE" ]]; then # shellcheck source=/dev/null source "$ENV_FILE" PORTAL_USER="${ANSIPA_PORTAL_USER:-$PORTAL_USER}" PORTAL_PASS="${ANSIPA_PORTAL_PASSWORD:-$PORTAL_PASS}" fi PORTAL_USER="${PORTAL_USER:-ansipa}" if [[ -z "$PORTAL_PASS" ]]; then PORTAL_PASS=$(tr -dc 'A-Za-z0-9' < /dev/urandom 2>/dev/null | head -c 32 \ || openssl rand -base64 24 | tr -d '/+=\n') warn "ANSIPA_PORTAL_PASSWORD not set — generated one for user '$PORTAL_USER':" warn " $PORTAL_PASS" warn "Set ANSIPA_PORTAL_PASSWORD in .env to pin it; otherwise it is reused as-is on every restart." fi # ── Persist for subsequent restarts ────────────────────────────────────────── { printf 'ANSIPA_PORTAL_USER=%q\n' "$PORTAL_USER" printf 'ANSIPA_PORTAL_PASSWORD=%q\n' "$PORTAL_PASS" } > "$ENV_FILE" chmod 600 "$ENV_FILE" # ── Write the htpasswd entry (apr1 — natively understood by nginx's # auth_basic_module, no dependency on the base image's libc crypt()) ───────── HASH=$(openssl passwd -apr1 "$PORTAL_PASS") touch "$PERSIST_HTPASSWD" # Replace this user's line if present, else append. grep -vE "^${PORTAL_USER}:" "$PERSIST_HTPASSWD" > "${PERSIST_HTPASSWD}.tmp" 2>/dev/null || true printf '%s:%s\n' "$PORTAL_USER" "$HASH" >> "${PERSIST_HTPASSWD}.tmp" mv "${PERSIST_HTPASSWD}.tmp" "$PERSIST_HTPASSWD" chmod 644 "$PERSIST_HTPASSWD" # ── Publish to the volume nginx reads from ─────────────────────────────────── cp "$PERSIST_HTPASSWD" "$SHARED_HTPASSWD" chmod 644 "$SHARED_HTPASSWD" log "Gateway credential ready for user '$PORTAL_USER' ($SHARED_HTPASSWD)"