diff --git a/src/containers/portainer/INSTALL.md b/src/containers/portainer/INSTALL.md index ed2a1b2..7965e85 100644 --- a/src/containers/portainer/INSTALL.md +++ b/src/containers/portainer/INSTALL.md @@ -69,6 +69,18 @@ ssh tappaas@ '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 `.`, e.g. + **`portainer-lab1.lab1.makerfloss.eu`** — not `portainer.lab1.…`. + ## Verify ```bash diff --git a/src/containers/portainer/update.sh b/src/containers/portainer/update.sh index 51b5789..2658bd2 100755 --- a/src/containers/portainer/update.sh +++ b/src/containers/portainer/update.sh @@ -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=". + 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)" ;;