MAATRIX / Блог / Passbolt на сервере: частые ошибки и решения

Passbolt на сервере: частые ошибки и решения

MAATRIX

Passbolt ломается не так, как обычное веб-приложение. Контейнер стартует, порт открыт, а панель пишет что-то про GPG-ключ, которого «нет в кольце». Или письма с приглашениями уходят в никуда, хотя тестовое доходит. Или после переезда на новый сервер вся команда разом видит «Sorry, the server key has changed». Разберём по слоям: GPG, база, почта, прокси и вход — с командами и честным разбором, где это норма, а где реальная поломка.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Триаж: с какого слоя начинать разбор

Passbolt — это CakePHP-приложение с обязательным end-to-end шифрованием через GPG поверх обычного стека: прокси → PHP-API → GPG-кольцо сервера → MySQL/MariaDB, и отдельным контуром — очередь писем → cron → SMTP. Почти вся диагностика сводится к тому, чтобы понять, какой из этих узлов виноват, не читая логи наугад.

Что видит пользовательВероятный слойПервая проверка
Контейнер не поднимается или сразу падаетGPG-ключ сервера не сконфигурированhealthcheck (ниже)
ERR_TOO_MANY_REDIRECTS в браузереHTTPS теряется между прокси и приложениемзаголовок X-Forwarded-Proto
Установка застряла на «Verifying server key»несовпадение fingerprintсверка отпечатка
Тестовое письмо доходит, приглашения — неточередь писем / cron внутри контейнерастатус cron-задачи
«Sorry, the server key has changed» у всей командыключ сервера реально сменился (миграция, потерянный volume)см. раздел про вход
SQLSTATE[HY000] [2002] Connection refusedБД ещё не готова или не тот хостdocker compose logs db

Passbolt поставляется со сводной командой, которая проверяет разом GPG, TLS, БД и конфиг — с неё и стоит начинать, а не с чтения общего лога:

docker compose exec passbolt su -m -c "bin/cake passbolt healthcheck" -s /bin/sh www-data

Вывод размечен по категориям ([GPG], [SSL], [Database], [Environment]), каждая строка — PASS/WARN/FAIL. Дальше разбираем именно эти категории.

GPG-ключ сервера: почему установка не идёт дальше

Секреты Passbolt шифруются на клиенте под GPG-ключ сервера, и без корректно сконфигурированного ключа приложение осознанно отказывается работать. Четыре типовых текста ошибки, каждый указывает на свой узел:

  • «The server OpenPGP key is not set» — переменная PASSBOLT_GPG_SERVER_KEY_FINGERPRINT не задана или контейнер её не увидел (проверьте docker exec passbolt printenv | grep GPG).
  • «The server key fingerprint doesn't match the one defined in /etc/passbolt/passbolt.php» — отпечаток в конфиге/переменной окружения не совпадает с реально импортированным ключом. Пересчитайте фактический отпечаток и сравните буквально посимвольно:
gpg --homedir /home/www-data/.gnupg --list-secret-keys --with-colons | grep fpr
  • «The server public key ... is not in the keyring» — файлы ключа не смонтированы или контейнер пересоздали без сохранённого GPG-тома. Образ ждёт пару serverkey.asc / serverkey_private.asc в /etc/passbolt/gpg/, либо генерирует ключ сам скриптом bin/generate_gpg_server_key.sh, если ключа нет вовсе.
  • «The server key does not have a valid email id» — email в UID ключа не совпадает с PASSBOLT_KEY_EMAIL.

Здесь та же логика, что и с любым GPG-ключом на сервере: если каталог .gnupg не вынесен в отдельный именованный том, пересоздание контейнера без сохранённого volume теряет ключ безвозвратно. Восстановить зашифрованные под старый ключ пароли после этого технически нельзя — это следствие честного E2E-шифрования, а не баг. Отсюда правило: сразу после установки снимите резервную копию каталога с приватным ключом отдельно от бэкапа базы — похожая ловушка с ключом подписи разобрана в статье про Vaultwarden на сервере.

Первого администратора заводят одной командой:

docker compose exec passbolt su -m -c "bin/cake passbolt register_user -u admin@example.com -f Имя -l Фамилия -r admin" -s /bin/sh www-data

Команда вернёт одноразовую ссылку для завершения регистрации в браузере — именно на этом шаге расширение Passbolt впервые проверяет отпечаток сервера.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

База данных: только MySQL/MariaDB, и почему падение при первом старте — не всегда поломка

В отличие от многих приложений из этого блога, Passbolt CE работает только с MySQL/MariaDB, не с PostgreSQL — даже если под остальные сервисы у вас уже стоит Postgres, под Passbolt нужен отдельный инстанс MySQL/MariaDB. Разница подходов — в статье PostgreSQL или MySQL.

Подключение к базе задаётся набором переменных:

environment:
  DATASOURCES_DEFAULT_HOST: db
  DATASOURCES_DEFAULT_PORT: "3306"
  DATASOURCES_DEFAULT_USERNAME: passbolt
  DATASOURCES_DEFAULT_PASSWORD: ${DB_PASSWORD}
  DATASOURCES_DEFAULT_DATABASE: passbolt

Опечатка в хосте или пароле даёт в логе PHP SQLSTATE[HY000] [2002] Connection refused или Access denied for user. Но чаще на первом же docker compose up виноват порядок запуска: passbolt стартует раньше, чем MySQL успевает принять соединения — обычный depends_on: [db] без condition: service_healthy этого не гарантирует. Итог — рестарт-луп на первом поднятии стека; второй docker compose up -d проходит гладко, база уже прогрета. Добавьте health-check на сервис БД и condition: service_healthy в depends_on — тогда рестарт-луп на первом запуске не повторится.

При обновлении версии образа Passbolt сам предупредит о непрокатанных миграциях:

docker compose exec passbolt su -m -c "bin/cake migrations status" -s /bin/sh www-data
docker compose exec passbolt su -m -c "bin/cake passbolt migrate" -s /bin/sh www-data

Перед этим — обязательный дамп: mysqldump в отдельный файл с датой в имени, миграции CakePHP, как и у большинства фреймворков, откатываются не всегда чисто.

Письма не уходят: тестовое работает, приглашения — нет

SMTP настраивается стандартным для CakePHP набором переменных:

environment:
  EMAIL_TRANSPORT_DEFAULT_HOST: smtp.example.com
  EMAIL_TRANSPORT_DEFAULT_PORT: "587"
  EMAIL_TRANSPORT_DEFAULT_USERNAME: passbolt@example.com
  EMAIL_TRANSPORT_DEFAULT_PASSWORD: ${SMTP_PASSWORD}
  EMAIL_TRANSPORT_DEFAULT_TLS: "true"
  EMAIL_DEFAULT_FROM: passbolt@example.com

Проверка одной командой:

docker compose exec passbolt su -m -c "bin/cake passbolt send_test_email you@example.com" -s /bin/sh www-data

Здесь и начинается путаница: send_test_email отправляет напрямую, минуя очередь. А приглашения и уведомления кладутся в таблицу email_queue и рассылаются отдельным cron-заданием внутри контейнера (в официальном образе это файл вида /etc/cron.d/passbolt-ce-server, раз в минуту). Если cron-демон в контейнере не запущен — очередь просто копится. Проверка:

docker compose exec passbolt sh -c 'service cron status || pgrep cron'
docker compose exec passbolt su -m -c "bin/cake passbolt email_digest send" -s /bin/sh www-data

Вторая команда прогоняет очередь: письмо не сформировалось (проблема в данных) или лежало готовым и не отправилось (проблема в доставке, смотрите тот же SMTP). Старый мусор чистит bin/cake passbolt purge_email_queue — это не лечит причину, только убирает хвосты.

За прокси: редирект-луп и почему HTTPS обязателен, а не опционален

Passbolt в проде принудительно требует HTTPS: PASSBOLT_SSL_FORCE=true редиректит любой HTTP-запрос на HTTPS. Проблема начинается, когда TLS терминируется на Nginx перед контейнером, а сам контейнер получает обычный HTTP-запрос по внутренней сети и не понимает, что снаружи уже HTTPS — он честно редиректит, а на входе в контейнер запрос снова HTTP, и так по кругу: ERR_TOO_MANY_REDIRECTS.

Лечится одним заголовком на прокси:

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host  $host;
proxy_set_header Host              $host;

Второе условие — APP_FULL_BASE_URL должен буквально совпадать с адресом в браузере: схема https://, домен, без завершающего слэша. Расхождение даёт не редирект-луп, а битые ссылки и ассеты, подгружаемые со старого адреса. Общий разбор проксирования, включая 502 и таймауты, — в статье Nginx как реверс-прокси: частые ошибки; если сертификат не обновляется или Let's Encrypt не проходит валидацию — в статье про Let's Encrypt на сервере.

Вход и расширение: «server key has changed» и ошибки проверки подлинности

Браузерное расширение Passbolt запоминает отпечаток GPG-ключа сервера при первом подключении по модели trust-on-first-use — как SSH-ключ хоста. Если отпечаток не совпал, расширение блокирует вход экраном «Sorry, the server key has changed».

Это ожидаемо в двух сценариях: вы намеренно ротировали ключ (отпечаток меняется у всех сразу — это норма) или перенесли инсталляцию на новый сервер, сохранив базу, но не GPG-ключ — тогда сервер сгенерировал новый ключ сам, и клиенты видят «смену» одновременно. Если переезд не задумывался как ротация, проверьте, перенесён ли каталог с приватным ключом вместе с базой — тогда отпечаток совпадёт и предупреждение не появится. Если ключ поменялся осознанно — администратор публикует новый отпечаток (его покажет healthcheck), каждый пользователь сверяет его при входе и подтверждает продолжение.

Отдельная, более редкая ошибка — «Could not verify the server key, the authentication failed». В отличие от «key has changed», она означает не смену ключа, а рассинхронизацию конфигурации: PASSBOLT_GPG_SERVER_KEY_FINGERPRINT не совпадает с ключом, реально лежащим в кольце контейнера — та же диагностика, что и в разделе про GPG выше, просто проявляется при входе, а не при старте. И банальность, которая отнимает больше времени, чем должна: на виртуалке с урезанным энтропийным пулом первая генерация GPG-ключа может зависать надолго — помогает заранее поставленный haveged или rng-tools.

Какой сервер брать под Passbolt в MAATRIX

Официальный минимум для Passbolt CE — 1 vCPU и 1 ГБ RAM, это порог «запускается», а не «работает предсказуемо под нагрузкой»: PHP-FPM, MySQL/MariaDB, прокси и cron очереди писем на 1 ГБ живут тесно, и одновременный вход пяти-семи человек с проверкой GPG-подписей на клиенте ощутимо просаживает отклик.

Рабочий минимум: 2 vCPU, 2–4 ГБ RAM, 40 ГБ NVMe. Это официально рекомендуемые цифры для продакшена, без запаса — под них ложится MySQL с буферным пулом, PHP-воркеры и место под дамп базы перед обновлением. На 1 ГБ без свопа обновление с миграциями рискованно: упрётся процесс в память посреди работы — откатывать придётся руками через дамп.

Приложения из каталога apps.maatrix.io разворачиваются автоматически при заказе сервера: том с GPG-ключом смонтирован постоянно с первого запуска, MySQL поднимается раньше приложения — часть ошибок из разделов про GPG и базу на автоустановке просто не возникает. Но она не отменяет вашу часть: домен и сертификат, внешний SMTP, регулярная копия каталога с ключом и базы на разные носители, и осознанное решение о ротации ключа при переезде на другой сервер.

Локация — по тому, где физически сидит команда и чьи данные в сейфе. Для российской команды разумна российская локация — меньше задержка на вход и меньше вопросов про пересечение границ с чувствительными данными. Для команды с международными клиентами и требованиями GDPR — Великобритания. Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT, без разницы для локации.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли поставить Passbolt на PostgreSQL, если остальной стек уже на нём?

Нет, официально CE работает только с MySQL/MariaDB — под Passbolt придётся держать отдельный инстанс, даже если для остального у вас Postgres.

Потерял volume с GPG-ключом сервера — можно восстановить пароли?

Если приватный ключ действительно утерян (не в бэкапе, не в старом контейнере через docker ps -a и docker cp), то нет: секреты зашифрованы под него асимметрично, это математически недоступные данные, а не «забытый пароль». Отсюда правило — копия каталога с ключом отдельно от бэкапа базы, снятая сразу после установки.

После переноса на новый сервер вся команда видит «Sorry, the server key has changed» — это баг?

Нет, это штатная защита trust-on-first-use. Если перенос не планировался как ротация, проверьте, перенесён ли каталог с приватным ключом вместе с базой — тогда предупреждение не появится. Если ключ поменялся осознанно — раздайте команде новый отпечаток.

send_test_email доходит, а приглашения коллегам — нет. В чём разница?

Тестовое письмо уходит напрямую, минуя очередь. Приглашения попадают в таблицу очереди и рассылаются cron-заданием внутри контейнера раз в минуту. Проверьте, что cron-процесс жив, и прогоните очередь вручную: bin/cake passbolt email_digest send.

Как быстрее всего понять, какой слой сломан, не читая весь лог?

Команда bin/cake passbolt healthcheck проверяет GPG, TLS, базу и конфигурацию разом и помечает каждый пункт PASS/WARN/FAIL — дальше читаете не весь лог, а конкретную проваленную категорию.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →