portainer: fix admin bootstrap and the published-domain derivation

First live install on lab1 surfaced two bugs.

Portainer 2.39.6 refuses POST /api/users/admin/init with 403 unless the
X-Setup-Token header carries the token it prints once at startup; update.sh now
reads it out of podman logs, matching the 64-hex value because the log line puts
ANSI colour codes between the key and the token.

The domain fallback asked get_variant_config for the DEFAULT environment, so a
module installed into lab1 advertised portainer-lab1.makerfloss.eu while Caddy
actually publishes portainer-lab1.lab1.makerfloss.eu — which would also have put
a mismatched RedirectURI into Portainer's OAuth settings. It now derives from the
module's own environment, as identity/install-service.sh does.

Confirmed on the live system: identity:identity works — the OIDC application is
created and /etc/secrets/portainer.env is written before install.sh runs.
This commit is contained in:
Lars Rossen 2026-08-24 20:55:26 +02:00
parent bca395d7f9
commit 77f361120a
2 changed files with 24 additions and 2 deletions

View file

@ -69,6 +69,18 @@ ssh tappaas@<vm-ip> 'sudo cat /etc/secrets/portainer-admin'
Username `admin`. Keep this — it is the only way back in when identity is unavailable.
## Notes from the first live install (2026-08-24, lab1)
- **Portainer ≥ 2.39 requires a setup token.** `POST /api/users/admin/init` returns `403`
unless the `X-Setup-Token` header carries the token Portainer prints once at startup.
`update.sh` now reads it from `podman logs`. The log line wraps the value in ANSI colour
codes, so it matches the 64-hex token rather than the `setup_token=` prefix.
- **`proxyDomain` is not persisted into the installed config.** `copy-update-json.sh`
documents that `install-module.sh` computes it from the environment, but it lands as
`null`, so every consumer has to derive it — and must do so from the module's *own*
environment. The published name is `<effective-module-name>.<environment-domain>`, e.g.
**`portainer-lab1.lab1.makerfloss.eu`** — not `portainer.lab1.…`.
## Verify
```bash

View file

@ -65,7 +65,11 @@ for _ in $(seq 1 40); do vm "exit 0" 2>/dev/null && break; sleep 3; done
vm "exit 0" 2>/dev/null || die "SSH to tappaas@${IP} not available"
# ── Friendly URL for the operator ────────────────────────────────────
DOMAIN="$(get_variant_config "" 2>/dev/null | jq -r '.domain // empty' || true)"
# proxyDomain is NOT persisted into the installed config by install-module.sh, so
# it is normally empty here and we must derive it — from the module's OWN
# environment, not the default one (identity/install-service.sh does the same).
ENVIRONMENT="$(get_config_value 'environment' '')"
DOMAIN="$(get_variant_config "${ENVIRONMENT}" 2>/dev/null | jq -r '.domain // empty' || true)"
PROXY_DOMAIN="$(get_config_value 'proxyDomain' "${VMNAME}${DOMAIN:+.${DOMAIN}}")"
UI_DIRECT_URL="https://${IP}:9443"
if [[ -n "${PROXY_DOMAIN}" ]] && getent hosts "${PROXY_DOMAIN}" >/dev/null 2>&1; then
@ -129,8 +133,14 @@ if ! vm "sudo test -s ${ADMIN_SECRET}" 2>/dev/null; then
vm "sudo install -d -m 0700 \$(dirname ${ADMIN_SECRET})"
vm "openssl rand -base64 24 | sudo tee ${ADMIN_SECRET} >/dev/null && sudo chmod 0600 ${ADMIN_SECRET}"
ADMIN_PW="$(vm "sudo cat ${ADMIN_SECRET}" | tr -d '\r')"
# 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' \
-H 'Content-Type: application/json' ${TOKEN_HDR} \
-d '{\"Username\":\"admin\",\"Password\":\"${ADMIN_PW}\"}'" || echo 000)"
case "${init_code}" in
200|204) info " admin created (password in ${ADMIN_SECRET} on the VM)" ;;