Files
admin/.wiki/concepts/portainer-2.21-admin-password-regression.md

86 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Portainer 2.21 `--admin-password` regression + min 12-char policy
type: concept
tags: [portainer, password, bcrypt, regression, gotcha]
sources: [../sources/vds-kzntsv-bootstrap-2026-05-20.md]
updated: 2026-05-20
---
# Portainer 2.20+ admin-password regression
## Симптом 1 — API init policy
Portainer 2.20.0+ форсит **минимум 12 chars** на admin password при создании через API endpoint `POST /api/users/admin/init`. Короче — `{"message":"Password does not meet the requirements"}`.
## Симптом 2 — CLI flag `--admin-password` broken
CLI flag `--admin-password "<bcrypt-hash>"` (документированный путь init без API) в 2.21.5 ведёт себя странно:
- Лог сервера показывает «created admin user with the given password.» — то есть admin **записывается**.
- Login с паролем → `Invalid credentials`. Bcrypt verify fails.
Пробовали и `$2y$` (htpasswd default), и `$2a$` (Go bcrypt), и YAML list form чтобы избежать compose env-interp escape (`$``$$`), и docker run direct без compose. Bcrypt hash в `docker inspect` Cmd корректный. Но login всё равно fails.
Не покопали глубоко (возможно — flag тупо игнорируется и admin создаётся с auto-generated password, либо хеш re-hashится сервером).
## Решение
Bypass CLI flag. Использовать API init с **длинным паролем (12+ chars)**:
```bash
# Run portainer без --admin-password (fresh data dir)
docker run -d --name portainer --network proxy \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $PWD/data:/data \
portainer/portainer-ce:2.21.5
# API admin/init available 5 minutes after start
curl -sk -X POST https://portainer.vds.kzntsv.site/api/users/admin/init \
-H 'Content-Type: application/json' \
-d '{"Username":"vitya","Password":"Pryakhin9-VDS-2026"}'
# Login → JWT
JWT=$(curl -sk -X POST https://portainer.vds.kzntsv.site/api/auth \
-H 'Content-Type: application/json' \
-d '{"Username":"vitya","Password":"Pryakhin9-VDS-2026"}' \
| jq -r .jwt)
# Generate API key
APIKEY=$(curl -sk -X POST https://portainer.vds.kzntsv.site/api/users/1/tokens \
-H "Authorization: Bearer $JWT" \
-H 'Content-Type: application/json' \
-d '{"description":"automation","password":"Pryakhin9-VDS-2026"}' \
| jq -r .rawAPIKey)
```
После init — local docker endpoint надо создать отдельным POST'ом:
```bash
curl -sk -X POST https://portainer.vds.kzntsv.site/api/endpoints \
-H "X-API-Key: $APIKEY" \
-F "Name=local" \
-F "EndpointCreationType=1" # LocalDockerEnvironment
```
## Trade-off
- ✗ Имя пользователя пароль user'а — приходится менять на «12+ chars» (`Pryakhin9-VDS-2026` вместо `Pryakhin9`).
- ✔ Single source of truth — admin live в DB только через API, всегда последовательное состояние.
- ✔ API token доступен сразу для дальнейшей автоматизации stacks через REST.
## Compose эскейп bcrypt — separate gotcha
При попытках использовать `--admin-password` в compose столкнулись с classic `$` escape trap:
- В YAML string form `command: "... --admin-password '$$2y$$05$$abc'"` — compose unwraps `$$``$`, передаёт правильный hash.
- В YAML list form `command: ["...", "--admin-password", "$$2y$$05$$abc"]` — compose unwraps аналогично, hash виден в `docker inspect` корректный.
- Кавычки `'...'` вокруг hash в string form **становятся literal частью value** (shell tokenize'ит, не shell-context'ит), bcrypt получает паразитные `'` → broken hash.
Решение для подобных escape-проблем — `docker run` direct (нет compose env-interp) или `--admin-password-file` (passes plain text from file, no escape) — но last не bypass'ит policy, ждёт plain text 12+ chars.
## Где применено
Portainer на [`vds-kzntsv`](../entities/vds-kzntsv.md), запущен через `docker run` (не compose) чтобы зафиксировать конкретный набор labels. Кред live в `~/projects/.common/secrets/vds-kzntsv.env`.
## Ссылки
- Portainer changelog 2.20.0 — введён password policy + auth refactor (https://github.com/portainer/portainer/releases/tag/2.20.0).
- Сессия где упоролись: [`vds-kzntsv-bootstrap-2026-05-20`](../sources/vds-kzntsv-bootstrap-2026-05-20.md) Phase 1 cont.