Dotfiles/setup/modules/FreeipaAnsible/image/ansipa-portal-auth-setup.sh

82 lines
3.6 KiB
Bash
Executable File

#!/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)"