Как установить панель и личный кабинет Zentro
This content is not available in your language yet.
Инструкция для того, кто ставит фронт Zentro у заказчика или на своём сервере: образы, настройка, запуск, проверка, обновление. Identity ставится своим комплектом (Установка сервера) — здесь только панель, личный кабинет и их прокси (BFF).
Единый вход для приложений организации — Единый вход.
Что ставится
Заголовок раздела «Что ставится»Три образа, четыре контейнера (и пятый — edge из образа zentro-edge, если TLS ставится на этом
же сервере, шаг 5):
| Контейнер | Образ | Что делает |
|---|---|---|
portal-web |
zentro-portal-web |
Панель администратора: статика + nginx |
portal-bff |
zentro-bff |
Прокси панели к Identity: хранит сессию, токены в браузер не попадают |
account-web |
zentro-account-web |
Личный кабинет: вход, согласие, профиль, безопасность |
account-bff |
zentro-bff |
Прокси кабинета, во внешнем режиме: админский API через него закрыт |
Каждое приложение — свой адрес; его прокси — по пути /bff того же адреса:
https://admin.example.com/ панель (только внутренняя сеть)https://admin.example.com/bff/… прокси панелиhttps://account.example.com/ личный кабинет (снаружи)https://account.example.com/bff/… прокси кабинетаОбразы одинаковы для всех заказчиков: адресов внутри нет, всё задаётся настройками.
Что нужно заранее
Заголовок раздела «Что нужно заранее»- Сервер с Docker 24+ и Docker Compose v2. Сколько ресурсов — Требования к серверу.
- Identity установлен и доступен с этого сервера.
- Адреса для панели и кабинета: свой TLS-прокси перед ними (nginx, балансировщик) или TLS из
комплекта — со своими сертификатами или Let’s Encrypt (шаг 5); либо
http://localhostдля локальной установки. - Комплект Zentro Identity, скачанный и проверенный по инструкции Установка сервера:
панель и кабинет лежат в его папке
portal/, той же версии, что и сервер.
Шаг 1. Загрузить образы
Заголовок раздела «Шаг 1. Загрузить образы»Папка portal/ из распакованного и проверенного комплекта (на сервер фронта, если он отдельный, — скопируйте её):
cd zentro-identity-0.7.2/portaldocker load < images/zentro-portal-0.7.2-linux-amd64.tar.gz # arm64-сервер — …-linux-arm64cd deployВ папке portal/:
| Путь | Что |
|---|---|
images/zentro-portal-<версия>-linux-<arch>.tar.gz |
все образы фронта одним архивом: панель, кабинет, BFF, TLS-прокси |
deploy/docker-compose.yml, deploy/.env.example |
запуск и справочник переменных |
deploy/docker-compose.tls.yml, deploy/tls/ |
TLS на этом сервере (шаг 5) |
README.md, server-requirements.md |
инструкция по установке и ресурсы сервера |
THIRD_PARTY_NOTICES.txt |
лицензии сторонних компонентов панели и кабинета |
VERSION, SHA256SUMS |
версия, контрольные суммы (их тоже покрывает подпись всего комплекта) |
Все команды дальше — из папки deploy.
Шаг 2. Настроить
Заголовок раздела «Шаг 2. Настроить»cp .env.example .envchmod 600 .env| Переменная | Что вписать |
|---|---|
VERSION |
Версия образов — уже вписана версия комплекта |
IDENTITY_BASE_URL |
Адрес Identity с сервера фронта: https://id.example.com или внутренний. Identity на этом же сервере — http://host.docker.internal:8080 |
PORTAL_SESSION_KEY |
Ключ шифрования сессий панели: openssl rand -base64 32 |
ACCOUNT_SESSION_KEY |
Ключ сессий кабинета — другой: openssl rand -base64 32 |
PORTAL_BIND, PORTAL_PORT |
На каком адресе и порту слушает панель. По умолчанию 127.0.0.1:8090 — только локально |
ACCOUNT_BIND, ACCOUNT_PORT |
То же для кабинета, по умолчанию 127.0.0.1:8091 |
CSP_IMG_HOSTS |
Если логотип или картинки бренда лежат на внешних хостах — эти хосты через пробел (те же, что в настройке Identity ui.asset_allowed_hosts). Пусто — только картинки, загруженные в панели |
HSTS |
За своим TLS-прокси: max-age=31536000. С TLS из комплекта — не нужен (ставит edge). Без TLS (http://localhost) — пусто |
PORTAL_PUBLIC_URL, ACCOUNT_PUBLIC_URL |
Адреса панели и кабинета с /bff, как их видит браузер: https://admin.example.com/bff, https://account.example.com/bff. Нужны корпоративному входу с переходом (OIDC, Keycloak): провайдер вернёт человека в то приложение, где он начал. Нет таких провайдеров — можно не задавать |
REAL_IP_FROM |
Только если перед фронтом стоит свой прокси (TLS-балансировщик): его адреса или сети через пробел — шаг 5. Пусто — пользователи ходят к контейнерам напрямую |
RATE_LIMIT_PER_MIN, AUTH_RATE_LIMIT_SCALE |
Лимиты запросов прокси. Оставьте по умолчанию, если нет причин |
Ключи сессий — секреты. Файл
.env— только владельцу. Смена ключа завершает все сессии: пользователям придётся войти заново.
Шаг 3. Запустить
Заголовок раздела «Шаг 3. Запустить»docker compose -f docker-compose.yml --env-file .env up -ddocker compose -f docker-compose.yml --env-file .env psС TLS из комплекта — команды из шага 5. Все контейнеры — running. Не задан обязательный параметр — compose откажется запускаться и
назовёт его.
Контейнеры работают без root, с файловой системой только для чтения. Данных не хранят: всё
состояние — в Identity, бэкапить здесь нечего, кроме .env.
Шаг 4. Настроить Identity под фронт
Заголовок раздела «Шаг 4. Настроить Identity под фронт»В env Identity (делает тот, кто его разворачивает; после правки — перезапуск Identity):
| Переменная | Значение |
|---|---|
WEBAUTHN_RP_ORIGIN |
оба адреса фронта через запятую: https://account.example.com,https://admin.example.com |
OIDC_AUTHORIZE_UI_URL |
https://account.example.com/oidc/consent |
PASSWORD_RESET_URL, EMAIL_VERIFY_URL, EMAIL_CHANGE_URL |
адреса кабинета: /reset-password, /verify-email, /confirm-email-change |
TRUSTED_PROXY_CIDRS |
адрес, с которого Identity видит прокси фронта (сеть Docker этого сервера или адрес сервера) |
Для локальной установки (http://localhost) — те же переменные с http://localhost:8091 (кабинет)
и http://localhost:8090 (панель).
Если подключаются внешние провайдеры входа с переходом на их страницу (OIDC, Keycloak) — в
провайдере и в Zentro адреса возврата обоих приложений:
https://account.example.com/bff/auth/idp/<имя>/callback и
https://admin.example.com/bff/auth/idp/<имя>/callback (подробно — Единый вход, шаг 4).
Шаг 5. TLS и прокси
Заголовок раздела «Шаг 5. TLS и прокси»Контейнеры слушают http. Для адресов с доменом нужен TLS — два варианта.
Вариант А. TLS из комплекта
Заголовок раздела «Вариант А. TLS из комплекта»Когда своего TLS-прокси нет — например, всё на одном VPS. Порты 80 и 443 сервера занимает
контейнер edge (nginx): он отдаёт панель, кабинет и, если нужно, Identity по имени домена.
- DNS доменов — на этот сервер; порт 80 открыт снаружи (Let’s Encrypt проверяет домен по нему).
- В
.env— блок «TLS на этом сервере»:PORTAL_HOST,ACCOUNT_HOST, при необходимостиIDENTITY_HOST(Identity на этом же сервере, свой nginx у него тогда не нужен), почтаLETSENCRYPT_EMAIL.REAL_IP_FROMиHSTSзадавать не нужно. - Сертификаты — для каждого домена свой выбор:
- свои (корпоративный центр, закрытый контур):
tls/certs/<домен>/fullchain.pemиprivkey.pem, ключ читает только uid 101 —tls/certs/README.md; - Let’s Encrypt — ничего не класть: сертификат получит и будет продлевать сервис
certbot.
- свои (корпоративный центр, закрытый контур):
- Запуск:
# только свои сертификаты:docker compose -f docker-compose.yml -f docker-compose.tls.yml --env-file .env up -d# Let's Encrypt (хотя бы для одного домена):docker compose -f docker-compose.yml -f docker-compose.tls.yml --env-file .env --profile letsencrypt up -dПока сертификата нет, edge отдаёт временный самоподписанный (браузер предупредит) — так он
может ответить Let’s Encrypt. Настоящий появляется за минуту-две, подхватывается без перезапуска;
продление — само, за 30 дней до конца. Что с сертификатами — в логе:
docker compose -f docker-compose.yml -f docker-compose.tls.yml --env-file .env logs edge certbot# edge: admin.example.com → portal-web:8080, сертификат: letsencrypt (own / selfsigned)Сначала можно проверить на тестовом центре Let’s Encrypt:
LETSENCRYPT_STAGING=true— лимиты выдачи не тратятся, но браузер такому сертификату не верит. Потомfalse, удалить тестовые сертификаты (docker volume rm <проект>_letsencrypt <проект>_edge-le) и запустить заново.
Панель снаружи. По умолчанию панель открыта всем по своему домену — защищают вход и второй
фактор. Закрыть её по адресам — ADMIN_ALLOW_FROM в .env (примеры там же: подсеть VPN, офисные
адреса, только частные сети); остальным — 403. Кабинет и Identity этим не ограничиваются.
Identity через edge. IDENTITY_UPSTREAM — как edge видит Identity (по умолчанию
host.docker.internal:8080 — на этом же сервере). Identity должен доверять адресу, с которого к
нему приходит edge (TRUSTED_PROXY_CIDRS, по умолчанию у Identity — шлюз его подсети, этого
хватает), и знать свой внешний адрес (PUBLIC_BASE_URL=https://id.example.com).
Вариант Б. Свой TLS-прокси
Заголовок раздела «Вариант Б. Свой TLS-прокси»Перед контейнерами ставится TLS-прокси заказчика:
admin.example.com→PORTAL_BIND:PORTAL_PORT,account.example.com→ACCOUNT_BIND:ACCOUNT_PORT;PORTAL_BIND/ACCOUNT_BIND— адрес, на который ходит прокси;- прокси сохраняет заголовок
Hostи дописывает адрес пользователя вX-Forwarded-For(nginx:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;); REAL_IP_FROMв.env— адреса прокси, как их видит контейнер (10.0.0.5/32или сеть10.20.0.0/16, несколько — через пробел);HSTSв.env— задать;- панель наружу не публиковать: только внутренняя сеть или VPN.
Зачем
REAL_IP_FROM. Без него адрес пользователя для фронта — адрес прокси: все пользователи делят один лимит попыток входа, а в сессиях и аудите Identity стоит прокси. С ним адрес берётся изX-Forwarded-For, но только от перечисленных адресов: прямой запрос с подставленным заголовком ничего не подменит.0.0.0.0/0не принимается. Неверное значение — контейнер не стартует и пишет, что не так (docker compose … logs portal-web).
Как проверить, что адрес доходит: войти через прокси и открыть кабинет → Сессии — у текущей сессии ваш адрес, а не адрес прокси.
Шаг 6. Проверить
Заголовок раздела «Шаг 6. Проверить»A=https://account.example.com # или http://localhost:8091P=https://admin.example.com # или http://localhost:8090
curl -s $A/bff/healthz # okcurl -s $P/bff/healthz # okcurl -sI $A/ | grep -i content-security # заголовок естьcurl -s -o /dev/null -w '%{http_code}\n' $A/bff/api/identity/admin/users # 403: кабинет не пускает в админкуВ браузере:
- Кабинет открывается, вход работает, «Забыли пароль?» присылает письмо со ссылкой на кабинет.
- Панель открывается (из внутренней сети), вход администратором, второй фактор, разделы с данными.
- Ключ (passkey) заводится и входит — и в кабинете, и в панели.
- Панель не открывается снаружи.
Обновление
Заголовок раздела «Обновление»Панель и кабинет обновляются из папки portal/ нового комплекта Zentro Identity (скачать и проверить подпись —
как при установке; сервер Identity обновляет upgrade.sh, Установка сервера).
docker load < zentro-identity-<новая>/portal/images/zentro-portal-<новая>-linux-amd64.tar.gz# VERSION=<новая> в .env; сравнить portal/deploy/.env.example нового комплекта со своим .env — новые переменныеdocker compose -f docker-compose.yml --env-file .env up -d # с TLS — те же -f и --profile, что при запускеКонтейнеры пересоздаются за секунды, сессии пользователей сохраняются (ключи те же). Откат —
вернуть прежнюю VERSION и повторить up -d. Порядок относительно Identity — по примечаниям к
версии: обычно сначала Identity, затем фронт.
Остановить и удалить
Заголовок раздела «Остановить и удалить»docker compose -f docker-compose.yml --env-file .env down # с TLS — добавить -f docker-compose.tls.ymlДанных нет — после down удалять больше нечего, кроме сертификатов Let’s Encrypt в томах
(down -v удалит и их — при следующем запуске выпустятся заново). Образы — docker image rm.
Частые ошибки
Заголовок раздела «Частые ошибки»| Что видно | Причина | Что сделать |
|---|---|---|
compose не запускается: «задайте …» |
Не заполнен обязательный параметр | .env, шаг 2 |
Браузер предупреждает о сертификате, в логе edge — selfsigned |
Let’s Encrypt ещё не выдал или не смог: DNS не на этот сервер, порт 80 закрыт, не запущен профиль letsencrypt |
logs certbot — там причина; проверить DNS и порт 80, шаг 5 |
edge не стартует, в логе privkey.pem не читается |
Свой ключ недоступен пользователю прокси | chown 101 + chmod 600 на ключ, tls/certs/README.md |
Панель отвечает 403, кабинет работает |
Ваш адрес не в ADMIN_ALLOW_FROM |
дописать сеть в .env, up -d |
| Страница открывается, при входе — «Сервис входа временно недоступен» | Прокси не достучался до Identity | IDENTITY_BASE_URL: открывается ли он с сервера фронта (из контейнера) |
| Ключ (passkey) не работает | Адреса фронта нет в WEBAUTHN_RP_ORIGIN Identity |
Шаг 4 |
| Вход в приложение через SSO обрывается | OIDC_AUTHORIZE_UI_URL не указывает на кабинет |
Шаг 4 |
| Логотип бренда не показывается | Картинка на внешнем хосте, которого нет в CSP_IMG_HOSTS |
Добавить хост, перезапустить *-web |
После смены .env все вышли |
Сменились ключи сессий | Так и должно быть; ключи менять только намеренно |
| Все упираются в «слишком много попыток»; в Сессиях у всех один адрес | Фронт или Identity видят адрес прокси вместо пользователей | Фронт — REAL_IP_FROM (шаг 5); Identity — TRUSTED_PROXY_CIDRS (шаг 4) |
Контейнер *-web сразу останавливается, в логе REAL_IP_FROM: … |
Не адрес/сеть или 0.0.0.0/0 |
Адреса прокси в виде 10.0.0.5/32, через пробел |
Сайт перестал открываться по http после включения HSTS |
HSTS заставляет браузер ходить только по https | HSTS — только при настоящем TLS |