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:
parent
bca395d7f9
commit
77f361120a
2 changed files with 24 additions and 2 deletions
|
|
@ -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.
|
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
|
## Verify
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -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"
|
vm "exit 0" 2>/dev/null || die "SSH to tappaas@${IP} not available"
|
||||||
|
|
||||||
# ── Friendly URL for the operator ────────────────────────────────────
|
# ── 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}}")"
|
PROXY_DOMAIN="$(get_config_value 'proxyDomain' "${VMNAME}${DOMAIN:+.${DOMAIN}}")"
|
||||||
UI_DIRECT_URL="https://${IP}:9443"
|
UI_DIRECT_URL="https://${IP}:9443"
|
||||||
if [[ -n "${PROXY_DOMAIN}" ]] && getent hosts "${PROXY_DOMAIN}" >/dev/null 2>&1; then
|
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 "sudo install -d -m 0700 \$(dirname ${ADMIN_SECRET})"
|
||||||
vm "openssl rand -base64 24 | sudo tee ${ADMIN_SECRET} >/dev/null && sudo chmod 0600 ${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')"
|
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 \
|
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)"
|
-d '{\"Username\":\"admin\",\"Password\":\"${ADMIN_PW}\"}'" || echo 000)"
|
||||||
case "${init_code}" in
|
case "${init_code}" in
|
||||||
200|204) info " admin created (password in ${ADMIN_SECRET} on the VM)" ;;
|
200|204) info " admin created (password in ${ADMIN_SECRET} on the VM)" ;;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue