portainer: reopen Portainer's admin-init window when it has closed

Portainer stops accepting POST /api/users/admin/init a few minutes after start
and answers 303 with Redirect-Reason: AdminInitTimeout. Any transient failure
during that window therefore made the module permanently un-bootstrappable.
update.sh now restarts the container to reset the timer, waits for the API and
retries once, re-reading the setup token because a fresh one is minted at every
start.

Also splits the OIDC precondition: the single condition reported the secrets env
as missing whenever the admin session was absent, which is what sent the first
debugging pass down the wrong path.
This commit is contained in:
Lars Rossen 2026-08-24 20:59:55 +02:00
parent 1c48eb4634
commit ffc9e12526

View file

@ -142,18 +142,40 @@ portainer_jwt() {
-d '{\"Username\":\"admin\",\"Password\":\"${ADMIN_PW}\"}'" 2>/dev/null | jq -r '.jwt // empty'
}
wait_for_api() {
local code
for _ in $(seq 1 24); do
code="$(vm "curl -fsk -o /dev/null -w '%{http_code}' https://localhost:9443/api/status 2>/dev/null" || echo 000)"
[[ "${code}" == "200" ]] && return 0
sleep 5
done
return 1
}
# Echoes the HTTP status of an admin-init attempt. The setup token is minted
# afresh at every container start, so re-read it each time.
init_admin() {
local tok hdr=""
tok="$(vm "sudo podman logs ${CONTAINER} 2>&1 | grep setup_token | grep -oE '[0-9a-f]{64}' | tail -1" | tr -d '\r')"
[[ -n "${tok}" ]] && hdr="-H 'X-Setup-Token: ${tok}'"
vm "curl -sk -o /dev/null -w '%{http_code}' -X POST https://localhost:9443/api/users/admin/init \
-H 'Content-Type: application/json' ${hdr} \
-d '{\"Username\":\"admin\",\"Password\":\"${ADMIN_PW}\"}'" 2>/dev/null || echo 000
}
JWT="$(portainer_jwt)"
if [[ -z "${JWT}" ]]; then
info " Bootstrapping the break-glass admin account..."
# Portainer >= 2.39 prints a one-time setup token at startup and refuses
# admin creation without it (HTTP 403). The log line wraps the value in ANSI
# colour codes, so match the 64-hex token rather than "setup_token=<value>".
SETUP_TOKEN="$(vm "sudo podman logs ${CONTAINER} 2>&1 | grep setup_token | grep -oE '[0-9a-f]{64}' | tail -1" | tr -d '\r')"
TOKEN_HDR=""
[[ -n "${SETUP_TOKEN}" ]] && TOKEN_HDR="-H 'X-Setup-Token: ${SETUP_TOKEN}'"
init_code="$(vm "curl -sk -o /dev/null -w '%{http_code}' -X POST https://localhost:9443/api/users/admin/init \
-H 'Content-Type: application/json' ${TOKEN_HDR} \
-d '{\"Username\":\"admin\",\"Password\":\"${ADMIN_PW}\"}'" || echo 000)"
init_code="$(init_admin)"
if [[ "${init_code}" == "303" ]]; then
# Portainer closes the admin-init window a few minutes after start
# (Redirect-Reason: AdminInitTimeout). Without this the module can never
# be bootstrapped again after any transient failure — restarting resets
# the timer and mints a new setup token.
info " admin-init window had closed — restarting ${CONTAINER} to reopen it"
vm "sudo podman restart ${CONTAINER} >/dev/null" || warn " restart failed"
wait_for_api && init_code="$(init_admin)"
fi
case "${init_code}" in
200|204) info " admin created (password in ${ADMIN_SECRET} on the VM)" ;;
409) warn " an admin exists but ${ADMIN_SECRET} does not match it — reset it by hand" ;;
@ -167,7 +189,11 @@ fi
# ── 5. Point Portainer at TAPPaaS identity (OIDC) ────────────────────
# identity:identity wrote these three values; if they are missing the module is
# still usable with the local admin, so warn rather than fail.
if [[ -n "${JWT:-}" ]] && vm "sudo test -s ${SECRETS_ENV}" 2>/dev/null; then
if [[ -z "${JWT:-}" ]]; then
warn " no admin session — skipping OIDC configuration (see the admin init warning above)"
elif ! vm "sudo test -s ${SECRETS_ENV}" 2>/dev/null; then
warn " ${SECRETS_ENV} missing — is identity:identity in dependsOn?"
else
info " Configuring OIDC login against Authentik..."
CLIENT_ID="$(vm "sudo sh -c '. ${SECRETS_ENV}; printf %s \"\$OIDC_CLIENT_ID\"'" | tr -d '\r')"
CLIENT_SECRET="$(vm "sudo sh -c '. ${SECRETS_ENV}; printf %s \"\$OIDC_CLIENT_SECRET\"'" | tr -d '\r')"
@ -213,8 +239,6 @@ if [[ -n "${JWT:-}" ]] && vm "sudo test -s ${SECRETS_ENV}" 2>/dev/null; then
else
warn " ${SECRETS_ENV} did not carry all three OIDC values — skipping OIDC configuration"
fi
else
warn " ${SECRETS_ENV} missing — is identity:identity in dependsOn? Local admin login still works."
fi
echo ""