106 lines
4.8 KiB
Bash
Executable File
106 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# infra/authentik/bootstrap/bootstrap.sh
|
|
#
|
|
# Idempotent post-deploy bootstrap. Waits for the Authentik server to become
|
|
# ready, then ensures the project blueprints (./blueprints/*.yaml) are loaded
|
|
# and applied. The blueprint files themselves are also mounted into the
|
|
# server/worker containers via docker-compose.yml — the worker auto-applies
|
|
# them at startup. This script is a belt-and-braces re-trigger that:
|
|
# 1. polls /-/health/ready/ until it returns 200,
|
|
# 2. triggers /api/v3/managed/blueprints/<id>/apply/ for every blueprint
|
|
# so they re-apply immediately rather than waiting for the next worker
|
|
# tick,
|
|
# 3. prints the operator-actionable next steps (rotate bootstrap token,
|
|
# enable MFA on the admin account, etc.).
|
|
#
|
|
# Run this from the operator's machine OR inside a Coolify post-deploy hook:
|
|
#
|
|
# export AUTHENTIK_BASE_URL=https://auth.boiler.saas-ctrl.com
|
|
# export AUTHENTIK_BOOTSTRAP_TOKEN=$(cat ~/.cli-tokens/authentik-bootstrap)
|
|
# bash infra/authentik/bootstrap/bootstrap.sh
|
|
|
|
set -Eeuo pipefail
|
|
|
|
: "${AUTHENTIK_BASE_URL:?AUTHENTIK_BASE_URL must be set (e.g. https://auth.boiler.saas-ctrl.com)}"
|
|
: "${AUTHENTIK_BOOTSTRAP_TOKEN:?AUTHENTIK_BOOTSTRAP_TOKEN must be set (see .env.example)}"
|
|
|
|
WAIT_MAX_SECONDS="${WAIT_MAX_SECONDS:-300}"
|
|
WAIT_INTERVAL_SECONDS="${WAIT_INTERVAL_SECONDS:-5}"
|
|
|
|
log() { printf "\033[1;34m[authentik-bootstrap]\033[0m %s\n" "$*"; }
|
|
err() { printf "\033[1;31m[authentik-bootstrap:ERROR]\033[0m %s\n" "$*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Step 1: wait for /-/health/ready/
|
|
# ----------------------------------------------------------------------------
|
|
log "waiting for ${AUTHENTIK_BASE_URL}/-/health/ready/ (timeout ${WAIT_MAX_SECONDS}s)"
|
|
deadline=$(( $(date +%s) + WAIT_MAX_SECONDS ))
|
|
while true; do
|
|
if curl -fsS -o /dev/null -w "%{http_code}" "${AUTHENTIK_BASE_URL}/-/health/ready/" | grep -q "^200$"; then
|
|
log "server ready"
|
|
break
|
|
fi
|
|
if [[ $(date +%s) -ge ${deadline} ]]; then
|
|
die "timed out waiting for /-/health/ready/"
|
|
fi
|
|
sleep "${WAIT_INTERVAL_SECONDS}"
|
|
done
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Step 2: list blueprints and trigger re-apply
|
|
# ----------------------------------------------------------------------------
|
|
auth_header="Authorization: Bearer ${AUTHENTIK_BOOTSTRAP_TOKEN}"
|
|
|
|
log "listing managed blueprints"
|
|
blueprints_json="$(curl -fsS -H "${auth_header}" "${AUTHENTIK_BASE_URL}/api/v3/managed/blueprints/?page_size=200")" || die "blueprint listing failed (check AUTHENTIK_BOOTSTRAP_TOKEN)"
|
|
|
|
# Extract pk for any blueprint whose `path` starts with /blueprints/toolctrl/.
|
|
ids="$(printf "%s" "${blueprints_json}" \
|
|
| python3 -c 'import sys, json; data=json.load(sys.stdin); [print(b["pk"]) for b in data.get("results", []) if b.get("path","").startswith("/blueprints/toolctrl/")]')"
|
|
|
|
if [[ -z "${ids}" ]]; then
|
|
log "no /blueprints/toolctrl/* blueprints found yet — the worker has not picked them up"
|
|
log "this is expected on the very first boot; the worker auto-discovers them within ~30s"
|
|
log "re-run this script in 1 minute to apply explicitly"
|
|
exit 0
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Step 3: apply each blueprint
|
|
# ----------------------------------------------------------------------------
|
|
for id in ${ids}; do
|
|
log "applying blueprint ${id}"
|
|
curl -fsS -X POST -H "${auth_header}" "${AUTHENTIK_BASE_URL}/api/v3/managed/blueprints/${id}/apply/" \
|
|
-o /dev/null \
|
|
|| err "apply failed for blueprint ${id} (will be retried by the worker)"
|
|
done
|
|
|
|
log "bootstrap complete"
|
|
|
|
cat <<'EONOTE'
|
|
|
|
----------------------------------------------------------------------
|
|
NEXT STEPS (operator)
|
|
----------------------------------------------------------------------
|
|
1. Log into https://auth.boiler.saas-ctrl.com using AUTHENTIK_BOOTSTRAP_EMAIL
|
|
+ AUTHENTIK_BOOTSTRAP_PASSWORD. The first login forces a password change.
|
|
|
|
2. In the Authentik UI, enroll TOTP for your account
|
|
(User Settings -> Authenticators -> Set up TOTP). Required because
|
|
the toolctrl-require-mfa-totp-for-admin policy denies access to the
|
|
admin OIDC application without a verified TOTP device.
|
|
|
|
3. Rotate AUTHENTIK_BOOTSTRAP_TOKEN — it grants full admin-API access
|
|
and is no longer needed after this bootstrap. Generate a fresh
|
|
personal admin token instead, and store it in Coolify-Vault.
|
|
|
|
4. Add real human admins by creating users in the Authentik UI and
|
|
adding them to the `toolctrl-admin` group. Force TOTP enrollment
|
|
on each.
|
|
|
|
5. Verify OIDC discovery from the admin-panel side once #82 is wired:
|
|
curl -fsS https://auth.boiler.saas-ctrl.com/application/o/toolctrl-admin-panel/.well-known/openid-configuration | jq .issuer
|
|
----------------------------------------------------------------------
|
|
EONOTE
|