Как мы ушли от паролей в Google Docs: разворачиваем Passbolt на своём VPS

Пошаговая инструкция по развёртыванию Passbolt — self-hosted менеджера паролей с открытым кодом — на собственном VPS. Покупка домена, Docker Compose, Nginx Proxy Manager с Let's Encrypt, бэкапы, безопасность. Альтернативы Cloudflare для пользователей из РФ.

Как мы ушли от паролей в Google Docs: разворачиваем Passbolt на своём VPS

В одной из команд, где я работал, пароли жили в Google Docs. Документ назывался что-то вроде пароли_не_смотреть, доступ к нему был у всех, кто когда-либо его открывал, история изменений — на совести Google. Когда сотрудник уходил, никто толком не знал, какие именно пароли надо ротировать: проще было поменять всё подряд или забить и надеяться.

Эту дыру надо было как-то закрывать. Дальше — как я выбирал решение, во что в итоге упёрся и как развернул Passbolt у себя для семьи (по той же схеме, что подходит и для небольшой команды).

Что я смотрел перед Passbolt

Passwork. Первое, с чем я познакомился вживую — отличный функционал, удобные группы, аудит, мобильные клиенты. Но цена для небольшой команды или семьи плохо бьётся с задачей: платить корпоративный ценник за 4–6 человек не хочется.

1Password, LastPass, Bitwarden (облако). Закрывают сценарий «храни и шарь», но всё хранится на чужих серверах. Для семьи это приемлемо, для команды с реальными доступами к проду — уже вопрос доверия и compliance.

Vaultwarden. Self-hosted реализация Bitwarden API. Лёгкий, поднимается в один контейнер, поддерживает не только пароли, но и данные карт. Хороший вариант, если знаешь, что ограничишься личным использованием или маленькой группой без жёстких ролей.

Passbolt CE. Open-source, self-hosted, с акцентом именно на командную работу: группы, права на конкретный секрет, видно — кому, когда и какой доступ был выдан. То, что нужно, когда уход сотрудника не должен превращаться в массовую ротацию паролей.

Изначально я искал решение для компании — закрыть ту самую дыру с Google Docs. Passbolt подошёл: бесплатный, self-hosted, с группами и аудитом, не упирающийся в корпоративный ценник. А когда понадобилось организовать пароли уже дома — для семьи — развернул его же по той же схеме. Дальше — про развёртывание под домашний сценарий, но архитектура один в один масштабируется на команду: меняются только пользователи и группы.

Кому подойдёт эта инструкция

  • Есть домашняя лаба или опыт с Docker
  • Нужно расшаривание с ролями и аудитом — в семье, в небольшой команде или в pet-проекте
  • Важно видеть: кому, когда, какой доступ был выдан

Если речь про одного человека — Bitwarden или Vaultwarden закроют задачу с меньшими хлопотами.


1. Покупаем домен

Регистратор — на свой вкус. Я использую reg.ru (рефералка: код 9E45-4577-1048-A828 даёт скидку).

Что важно при выборе зоны:

  • Смотреть на цену продления, а не на цену первого года. В бюджетных зонах вроде .online, .store, .fun, .icu стартовая цена часто символическая, но продление в разы выше. На некоторых зонах (например, .pro) бывает обратная ситуация — стабильно дёшево и при покупке, и при продлении. Стоит сравнить несколько зон в корзине прежде чем оплачивать.
  • Дом или работа. Для домашнего сервиса подойдёт любая дешёвая зона. Для рабочего — лучше .ru или .com: некоторые корпоративные DNS-фильтры режут «мусорные» зоны и сотрудники просто не смогут открыть менеджер паролей с офисной сети.
  • Имя домена — рандом vs говорящее. Менеджер паролей — это не сайт-визитка, его не нужно запоминать наизусть: один раз сохранил закладку и всё. Чем менее говорящее имя, тем меньше информации утекает наружу: passwords-mycompany.ru сразу сообщает атакующему, что по этому адресу живёт чувствительный сервис, а условный kx7m2.online — нет. Это небольшой дополнительный слой — не панацея (через DNS-логи и Certificate Transparency сервис всё равно найдут, если будут искать целенаправленно), но и не лишний.

Ловушка с двумя доменами. Reg.ru на оформлении предложит докинуть второй домен за символическую цену — выглядит как подарок, но продление второго обычно сильно дороже. Если второй домен реально не нужен — отказаться.

Скриншот покупки домена
Корзина reg.ru

2. VPS или домашнее железо?

Passbolt можно развернуть как на арендованном VPS, так и на домашнем сервере/мини-ПК/Raspberry Pi. Оба варианта рабочие, но у домашнего есть требование, без которого ничего не заработает: белый статический IP.

  • Белый (публичный) — потому что Passbolt должен быть доступен снаружи: с телефона по дороге домой, с рабочего ноутбука, из браузерного расширения у родственников. Если провайдер выдаёт серый IP за NAT — снаружи на этот адрес попасть нельзя.
  • Статический — потому что DNS-запись A your-domain.online → IP должна стабильно указывать на один и тот же адрес. Динамический IP технически можно обернуть в DDNS, но это лишняя точка отказа в сервисе, от которого зависит вход во все остальные.

У большинства российских провайдеров белый статический IP — платная опция, обычно в пределах нескольких сотен рублей в месяц. Если IP за NAT и менять условия с провайдером не вариант — можно пробросить трафик через туннель на внешний VPS (Wireguard/Tailscale + reverse proxy), но это уже отдельная история и заметное усложнение схемы.

Дальнейшая инструкция написана под аренду VPS — у него белый статический IP «из коробки» и не нужно думать о провайдерских условиях. Если хочется поставить на домашнее железо — все шаги, начиная с Docker, идентичны, меняется только то, куда указывает A-запись домена.

Берём самый бюджетный тариф на VDSina — для Passbolt хватит 1 ядра и 1 ГБ ОЗУ. Лично пользуюсь этим хостером уже пару лет, оправдывает свои деньги (бывают сбои, но проблемы не задерживались дольше пары дней).

Рекомендация: не жадничать на месте и взять хотя бы 50 ГБ — это избавит от необходимости частой очистки логов, а стоимость вырастет несильно.


3. Доменные записи и прятки за прокси

Изначально в этой статье использовался Cloudflare: бесплатно, удобно, дополнительно скрывает реальный IP сервера. С 9 июня 2025 года российские провайдеры начали активно резать трафик до сетей Cloudflare (Cloudflare о ситуации в РФ). На стороне самого Cloudflare решения нет — это действия local ISP. Для сервиса, которым нужно стабильно пользоваться из России, Cloudflare больше не вариант.

Дальше — два сценария, выбирать по ситуации.

Вариант A. Без CDN-прокси (рекомендую для домашнего сценария)

Если читатель и его семья/команда находятся в РФ — проще всего обойтись без CDN-прослойки. A-запись напрямую указывает на IP VPS, HTTPS закрывается через Nginx Proxy Manager + Let's Encrypt (см. шаги 6–7).

  • Плюсы: работает стабильно, не требует обходных манёвров.
  • Минусы: реальный IP сервера виден. Атакующие сканируют интернет массово и постоянно — сам факт «у вас есть VPS» секретом не является. Что важно: рандомное имя домена не светится публично, сервис закрыт логином и PGP-фразой, на сервере оставлены только нужные порты (см. шаг 9). Этого достаточно для домашнего сценария.

Что делать: в панели регистратора или DNS-провайдера создать A-запись your-domain.online → <IP вашего VPS>.

Вариант B. С CDN/прокси

Если хочется дополнительного слоя (скрыть IP, базовая DDoS-защита, кеширование), есть несколько путей — у каждого свои оговорки:

  • Российские CDN (Selectel, NGENIX, DDoS-Guard, StormWall) — работают стабильно в РФ, но тарифы рассчитаны на бизнес и стартуют от нескольких тысяч рублей в месяц. Для домашнего Passbolt это перебор по цене.
  • Cloudflare с обходом блокировок на стороне клиента (WARP, VPN, прописывание Cloudflare-IP в hosts) — технически возможно, но превращает «бесплатное удобное решение» в «надо настроить обход у каждого члена семьи и поддерживать это». Если кто-то из пользователей не в РФ — у него Cloudflare работает обычным образом.
  • Self-hosted прокси на втором VPS за рубежом — для энтузиастов. Отдельная статья.
Дальше инструкция идёт по Варианту A.

4. Устанавливаем Docker

Команды ниже взяты из официального гайда Docker для Ubuntu — для другого дистрибутива стоит свериться там же, инструкция может разойтись.

Добавляем официальный репозиторий Docker:

# GPG-ключ
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Репозиторий
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Устанавливаем сам Docker и плагин Compose (он понадобится, чтобы поднимать Passbolt и Nginx Proxy Manager):

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Проверяем, что всё встало:

sudo docker run --rm hello-world

Если в выводе появилось Hello from Docker! — Docker работает.

В этой инструкции все команды docker далее идут через sudo — так надёжнее: не нужно думать о членстве в группах и правах доступа. Альтернатива — добавить пользователя в группу docker (тогда sudo не нужен), но членство в этой группе фактически равно правам root на хосте: Docker умеет монтировать любые директории. Если охота сократить набор sudo — можно сделать это осознанно, в остальных случаях проще оставить как есть.

5. Docker Compose для Passbolt

Создаём рабочую директорию:

mkdir ~/passbolt && cd ~/passbolt

Секреты — в отдельный .env

Чтобы пароли и почтовые креды не лежали внутри docker-compose.yml, выносим их в .env. Так удобнее править и проще не закоммитить случайно в git.

Сгенерируем пароль для БД (24 байта, base64) и сразу запишем его в .env:

cat > .env <<EOF
DB_PASSWORD=$(openssl rand -base64 24)
APP_FULL_BASE_URL=https://your-domain.online
EMAIL_FROM=you@gmail.com
EMAIL_USER=you@gmail.com
EMAIL_PASSWORD=your-gmail-app-password
EOF
chmod 600 .env

Заполнить APP_FULL_BASE_URL своим доменом, EMAIL_* — почтой, которая будет отправлять письма пользователям (приглашения, восстановление). Для Gmail нужен app password, не основной пароль аккаунта — сгенерировать здесь (нужно включить 2FA).

chmod 600 ограничивает доступ к файлу только владельцем — обязательно, в файле лежат секреты.

docker-compose.yml

nano docker-compose.yml
services:
  db:
    image: mariadb:10.11
    restart: unless-stopped
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "true"
      MYSQL_DATABASE: "passbolt"
      MYSQL_USER: "passbolt"
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - database_volume:/var/lib/mysql

  passbolt:
    image: passbolt/passbolt:latest-ce
    restart: unless-stopped
    depends_on:
      - db
    environment:
      APP_FULL_BASE_URL: ${APP_FULL_BASE_URL}
      DATASOURCES_DEFAULT_HOST: "db"
      DATASOURCES_DEFAULT_USERNAME: "passbolt"
      DATASOURCES_DEFAULT_PASSWORD: ${DB_PASSWORD}
      DATASOURCES_DEFAULT_DATABASE: "passbolt"
      EMAIL_DEFAULT_FROM: ${EMAIL_FROM}
      EMAIL_TRANSPORT_DEFAULT_HOST: smtp.gmail.com
      EMAIL_TRANSPORT_DEFAULT_PORT: 587
      EMAIL_TRANSPORT_DEFAULT_USERNAME: ${EMAIL_USER}
      EMAIL_TRANSPORT_DEFAULT_PASSWORD: ${EMAIL_PASSWORD}
      EMAIL_TRANSPORT_DEFAULT_TLS: "true"
      PASSBOLT_SSL_FORCE: "false"
    volumes:
      - gpg_volume:/etc/passbolt/gpg
      - jwt_volume:/etc/passbolt/jwt
    command:
      [
        "/usr/bin/wait-for.sh",
        "-t", "0",
        "db:3306",
        "--",
        "/docker-entrypoint.sh",
      ]
    ports:
      - "127.0.0.1:40080:80"
      - "127.0.0.1:40443:443"

volumes:
  database_volume:
  gpg_volume:
  jwt_volume:

Что важно в этом конфиге:

  • 127.0.0.1:40080:80 — порт прибит к loopback, наружу не торчит. Passbolt доступен только с самого сервера; снаружи к нему будет ходить Nginx Proxy Manager в шаге 7. Это снимает целый класс атак: даже если в UFW случайно откроются порты, до Passbolt напрямую не достучаться.
  • PASSBOLT_SSL_FORCE: "false" — это не значит, что HTTPS не используется. HTTPS терминируется на NPM (с настоящим сертификатом Let's Encrypt), а от NPM до Passbolt внутри сервера идёт обычный HTTP. Так делают почти все, кто ставит reverse proxy перед приложением.
  • restart: unless-stopped — контейнеры поднимутся сами после ребута VPS.

Альтернатива: compose с официального сайта

На сайте Passbolt лежит официальный docker-compose-ce.yaml. Его можно взять как референс и адаптировать по тем же принципам (вынести секреты в .env, прибить порты к 127.0.0.1). Если идти этим путём, у официального файла есть три нюанса, на которые легко наступить:

1. Имя файла — docker-compose-ce.yaml, а не docker-compose.yml.

Стандартное sudo docker compose up -d без аргументов ищет только docker-compose.yml / docker-compose.yaml — официальный файл с суффиксом -ce оно не подхватит и выдаст «no configuration file provided». Два варианта:

# Вариант А: переименовать
mv docker-compose-ce.yaml docker-compose.yml
sudo docker compose up -d

# Вариант Б: каждый раз указывать файл явно
sudo docker compose -f docker-compose-ce.yaml up -d
sudo docker compose -f docker-compose-ce.yaml logs -f passbolt

Вариант А удобнее, если Passbolt — единственный сервис в этой папке. Вариант Б — если рядом лежат другие compose-файлы и важно не перепутать.

2. Имена контейнеров и volume'ов зависят от имени папки.

Docker Compose формирует имена так: <имя-папки>_<имя-сервиса>-<n> для контейнеров и <имя-папки>_<имя-volume> для томов. Если папка называется ~/passbolt/ — получится passbolt_passbolt-1 и passbolt_gpg_volume. Если папка ~/passbolt_docker/ (как часто оказывается после git clone) — будет passbolt_docker_passbolt-1 и passbolt_docker_gpg_volume.

На что это влияет:

  • Бэкап-скрипт из шага 9 обращается к volume'ам по именам passbolt_gpg_volume / passbolt_jwt_volume. Если префикс другой — нужно поправить имена в скрипте, иначе бэкап молча сохранит пустые архивы.
  • Команды docker compose exec passbolt ... работают одинаково в обоих случаях, потому что обращаются к имени сервиса (passbolt), а не к имени контейнера. Так что инструкция по созданию первого пользователя из шага 8 не сломается.

Проверить актуальные имена:

sudo docker compose ps           # имена контейнеров
sudo docker volume ls | grep gpg # имена volume'ов

3. Если хочется зафиксировать имена явно — добавить name: в начало compose-файла:

name: passbolt
services:
  db:
    ...

Тогда префикс не будет зависеть от папки: всё будет называться passbolt_*, как в инструкциях ниже.

Запускаем

sudo docker compose up -d
sudo docker compose logs -f passbolt

Первое поднятие занимает 30–60 секунд: MariaDB инициализирует БД, Passbolt генерирует GPG-ключи сервера и накатывает миграции. Когда в логах появится строка вида Welcome to Passbolt! — контейнер готов.

Проверить, что Passbolt отвечает локально:

curl -I http://127.0.0.1:40080

Должен вернуть HTTP/1.1 302 или 200. Это значит, что приложение работает; снаружи оно пока недоступно — этим займётся NPM в шаге 7.


6. Nginx Proxy Manager

NPM — это reverse proxy на базе Nginx с веб-интерфейсом и автоматическим выпуском Let's Encrypt-сертификатов. Он будет принимать HTTPS-запросы снаружи на 443 порт и проксировать их на Passbolt.

Где запускать NPM: вместе с Passbolt или отдельно?

Вместе с Passbolt в одном compose — путь, описанный ниже. Подходит, если:

  • это первая инсталляция и пока нет других сервисов, которые нужно проксировать;
  • Passbolt стоит на отдельном VPS «под одну задачу»;
  • хочется получить рабочий результат за один присест, без лишних развилок.

Отдельно от Passbolt — мой основной паттерн дома. NPM поднят как самостоятельный сервис и управляет трафиком всей домашней лабы: один прокси, одна точка терминации SSL, через который наружу пробрасывается только то, что должно быть наружу. Так логичнее, если:

  • на сервере уже есть или будут другие сервисы (медиа, мониторинг, ещё что-то self-hosted);
  • хочется явно отделить «инфраструктурный слой» (DNS, прокси, сертификаты) от «прикладного» (Passbolt, остальное).

Настройка NPM как самостоятельного сервиса — тема для отдельной заметки, тут на ней не останавливаюсь.

Для команды/прода — другой разговор. Если Passbolt разворачивается для рабочей команды, имеет смысл начать не с прикладного сервиса, а с обратного прокси: централизованный reverse proxy, политики безопасности, единая точка терминации SSL и логов. В корп-сценариях это часто уже есть (Traefik в Kubernetes-кластере, nginx-конфиги под управлением ansible, корпоративный WAF). Городить рядом ещё один NPM ради одного Passbolt — плохая идея: ломается единая модель управления трафиком, появляется второй слой, который никто не патчит. В таком случае ниже стоит читать как референс, чем эквивалентно настроить ваш существующий прокси (forward host, схема, websockets, заголовки).

Дальше — конфиг для сценария «всё в одном compose, для пробы и для дома».

Добавляем сервис в тот же docker-compose.yml

Чтобы Passbolt и NPM могли общаться по внутренним именам контейнеров (passbolt, db, npm), их удобнее держать в одном compose-файле. Открываем тот же ~/passbolt/docker-compose.yml и добавляем сервис рядом с db и passbolt:

  npm:
    image: 'jc21/nginx-proxy-manager:2'
    restart: unless-stopped
    ports:
      - '80:80'           # HTTP, нужен для Let's Encrypt http-01 challenge
      - '443:443'         # HTTPS
      - '127.0.0.1:81:81' # админка — только локально, через SSH-туннель
    volumes:
      - ./npm-data:/data
      - ./npm-letsencrypt:/etc/letsencrypt

Поднимаем:

sudo docker compose up -d

Что важно в этом конфиге:

  • jc21/nginx-proxy-manager:2 — пин к мажорной версии 2.x. С :latest мажорное обновление может прилететь молча и поломать конфиг; с :2 будут приходить только совместимые апдейты в рамках мажора. Обновляться на v3 (когда выйдет) — осознанно, после прочтения changelog.
  • 127.0.0.1:81:81 — админка NPM не доступна снаружи. Это критично: её дефолтные креды admin@example.com / changeme — публичная информация, и боты ломятся в открытые 81 порты постоянно.
  • ./npm-data и ./npm-letsencrypt — папки создадутся в ~/passbolt/. Это конфиг NPM и сертификаты, их нужно будет включить в бэкап (см. шаг 9).

Как зайти в админку

Раз порт 81 прибит к loopback, напрямую с устройства открыть http://<ip>:81 не получится. Делаем SSH-туннель:

ssh -L 8081:127.0.0.1:81 user@your-vps-ip

После подключения админка NPM доступна на устройстве по http://localhost:8081. Туннель работает, пока открыт SSH-сеанс — закрыли терминал, доступ из браузера пропал.

Первый вход: логин admin@example.com, пароль changeme. NPM сразу попросит сменить почту и пароль — сменить немедленно, до того, как будут настроены прокси-хосты.


7. Настраиваем Proxy Host в NPM с Let's Encrypt

На этом шаге Passbolt станет доступен снаружи по HTTPS на своём домене.

Что должно быть готово к этому шагу

  • В DNS у домена создана A-запись на IP VPS (шаг 3)
  • Passbolt запущен и отвечает локально на http://127.0.0.1:40080 (шаг 5)
  • В NPM сменён дефолтный пароль (шаг 6)
  • 80 и 443 порты на VPS открыты для входящих (на этом шаге UFW ещё не настроен, по дефолту они открыты; если уже закрыли — сначала открыть, иначе Let's Encrypt не пройдёт проверку)

Создаём Proxy Host

В админке NPM открываем вкладку Hosts → Proxy HostsAdd Proxy Host.

Вкладка Details:

Поле Значение Комментарий
Domain Names your-domain.online без https://, без слэшей
Scheme http от NPM до Passbolt внутри сети идёт HTTP, см. PASSBOLT_SSL_FORCE: false в шаге 5
Forward Hostname / IP passbolt имя контейнера из docker-сети, NPM резолвит его автоматически, потому что лежит в том же compose
Forward Port 80 внутренний порт контейнера Passbolt, не 40080
Block Common Exploits стандартный набор Nginx-правил против частых паттернов атак
Websocket Support Passbolt использует websocket-соединения

Важно про порт: в шаге 5 наружу проброшен 127.0.0.1:40080 → 80, но NPM ходит к Passbolt не через хостовый loopback, а напрямую по docker-сети к контейнеру passbolt на его внутренний порт 80. Поэтому здесь именно 80, а не 40080.

Вкладка SSL:

Поле Значение
SSL Certificate Request a new SSL Certificate
Force SSL
HTTP/2 Support
HSTS Enabled
Email Address for Let's Encrypt действующий ящик
I Agree to the Let's Encrypt Terms of Service

Жмём Save. NPM запросит сертификат у Let's Encrypt — занимает 10–60 секунд. По окончании в списке Proxy Hosts появится зелёный статус.

Проверка

С ноутбука открываем https://your-domain.online. Должна загрузиться страница установки Passbolt (если первый запуск) или экран входа. В адресной строке — замочек и валидный сертификат от Let's Encrypt.

Если что-то пошло не так

  • Let's Encrypt не выпустил сертификат. Самая частая причина — A-запись домена не указывает на этот VPS (или указывает, но DNS ещё не разошёлся: дать 5–10 минут, проверить через dig your-domain.online). Вторая — порт 80 закрыт на VPS или у провайдера.
  • Too many redirects или бесконечный редирект. В compose не выставлено PASSBOLT_SSL_FORCE: "false" — Passbolt сам пытается редиректить на HTTPS, а NPM уже его проксирует. Поправить в .env или compose и перезапустить: sudo docker compose up -d.
  • 502 Bad Gateway. Контейнер Passbolt ещё не поднялся (первое поднятие может занять до минуты — БД и GPG-ключи генерируются). Подождать, посмотреть логи: sudo docker compose logs -f passbolt.

8. Создаём первого пользователя

На свежей инсталляции у Passbolt нет публичной регистрации — это сделано специально, чтобы случайный человек, открыв URL, не стал первым админом. Первый пользователь создаётся командой внутри контейнера; команда печатает одноразовую ссылку, по которой админ доделывает регистрацию в браузере.

Создаём admin-юзера

sudo docker compose exec passbolt su -m -c \
  "/usr/share/php/passbolt/bin/cake passbolt register_user \
  -u admin@your-domain.online \
  -f Admin \
  -l User \
  -r admin" -s /bin/sh www-data

Что в команде:

  • -u — email админа (на него потом будут приходить системные письма)
  • -f / -l — first name / last name
  • -r admin — роль; альтернатива user (обычный пользователь без прав на админку)

На почту -u команда письма не отправляет — она печатает ссылку прямо в stdout. Выглядит примерно так:

https://your-domain.online/setup/install/<user-uuid>/<token>

Завершаем регистрацию в браузере

Открываем ссылку из вывода. Passbolt предложит:

  1. Установить браузерное расширение (для Chrome/Firefox). Без расширения работать не получится — оно держит локальный PGP-ключ и расшифровывает пароли на клиенте. Это и есть суть end-to-end шифрования: сервер видит только зашифрованные данные.
  2. Сгенерировать PGP-ключ. Passbolt сделает это сам прямо в браузере. Ключ привязан к этому admin-аккаунту.
  3. Задать passphrase. Это пароль, которым шифруется приватный PGP-ключ.
  4. Скачать recovery kit. Это резервная копия PGP-ключа админа в виде файла.

Про passphrase и recovery kit — ниже отдельно, это самые важные понятия во всей инсталляции.

Про passphrase

Passphrase — единственное, что защищает приватный PGP-ключ, и единственное, что нельзя восстановить. Никаких «забыли пароль?» в Passbolt нет: это не баг, а суть end-to-end шифрования.

Правила, которые работают:

  • Запоминается. Парольная фраза из нескольких случайных слов на родном языке — надёжнее короткого «сложного» пароля и лучше держится в памяти. Достаточно трёх слов, если они действительно случайные и личные: дурацкая обзывалка друга, локальная семейная шутка, нелепая фраза, которую сказал ребёнок. То, что никто, кроме узкого круга, не угадает, а владелец не забудет.
  • Используется часто. Если сервис лежит без дела, фраза забывается. В этом смысле обратное тоже верно: нет смысла в Passbolt, которым не пользуются. Если через полгода окажется, что в него никто не заглядывает — лучше снести и не страдать.
  • Не сохраняется в браузере, в заметках, в облаке. Если passphrase лежит в Google Keep — вся конструкция бессмысленна: приватный ключ шифруется именно ею.

Что если passphrase всё же потеряна

Тут есть встроенное решение, и оно правильнее, чем «конверт у бабушки».

Заводим минимум двух админов. В Passbolt admin — это не только «может приглашать пользователей», но и носитель ресурсов, которые в случае потери одного аккаунта можно перевыдать остальным. Если жена/коллега тоже admin со своими PGP-ключом и passphrase — при потере доступа у одного из вас второй пересоздаст аккаунт и перешарит общие пароли.

Это не «второй ключ от того же замка» — это два независимых полноценных аккаунта, которыми семья реально пользуется. Никаких конвертов и записей passphrase у близких не нужно: достаточно того, что роль admin распределена между несколькими людьми.

Про recovery kit

Recovery kit — файл с приватным PGP-ключом, по сути сертификат пользователя. Нужен, чтобы:

  • добавить ещё одно устройство (второй ноутбук, рабочий комп, телефон);
  • переустановить браузерное расширение или сменить браузер;
  • восстановиться после переустановки системы.

Без recovery kit + passphrase новое устройство к Passbolt не подключить. Если оба админа потеряют свои recovery kit'ы одновременно — придётся пересоздавать аккаунт через второго админа (см. выше), с потерей истории.

Хранить можно локально — на самом устройстве в защищённой папке или на внешнем носителе. Это не публичный секрет: с одним только kit без passphrase войти нельзя, поэтому требования к нему мягче. Главное — не потерять, не выложить в публичный git и помнить, в каком закутке файл лежит.

Что сделать сразу

  • Проверить SMTP. В админке: Administration → Email server → Send test email. Если письмо не пришло — проблема с EMAIL_TRANSPORT_* переменными в .env. Без работающего SMTP не получится приглашать новых пользователей: им уходит письмо с ссылкой-инвайтом, аналогичной той, которая печаталась в stdout у первого admin'а.
  • Завести второго админа. Тем же register_user или через UI после первого входа. Это страховка на случай потери passphrase, см. выше.
  • Сменить email админа на реальный, если использовали временный.

Приглашаем остальных пользователей

Дальше всё через UI: Users → New user. Каждому уходит письмо с ссылкой-инвайтом, по которой он генерирует свой PGP-ключ и задаёт passphrase — ровно тот же процесс, что и у admin'а, только без CLI-шага. Группы и расшаривание паролей — там же, в Users / Groups, без необходимости лезть в сервер.


9. Безопасность сервера и бэкапы

Сервис с паролями всей семьи — это не место для «настроим потом». Ниже базовый минимум: firewall, бэкапы, обновления и пара мелочей по SSH.

Firewall: оставляем только нужные порты

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Что закрывается этим: 81 (админка NPM, она и так на 127.0.0.1), 40080/40443 (порты Passbolt, тоже только loopback), 3306 (MariaDB, наружу не пробрасывалась вовсе). Снаружи остаются только три порта: SSH для администрирования и 80/443 для HTTPS-трафика.

Проверяем:

sudo ufw status verbose

Бэкапы

Что нужно сохранять, чтобы из бэкапа можно было поднять Passbolt с нуля на другом сервере:

  • ~/passbolt/.env — пароли БД и SMTP, без него compose не стартанёт
  • ~/passbolt/docker-compose.yml — сам конфиг (по-хорошему он и так в git, но дублировать не вредно)
  • Docker-волюм database_volume — данные MariaDB, основная ценность
  • Docker-волюм gpg_volume — серверные PGP-ключи, без них зашифрованные данные в БД не расшифровать
  • Docker-волюм jwt_volume — ключи для JWT-токенов, без них клиенты придётся перерегистрировать
  • ~/passbolt/npm-data и ~/passbolt/npm-letsencrypt — конфиг NPM и сертификаты Let's Encrypt

Простой ежедневный бэкап-скрипт. Скрипт исходит из того, что compose-проект называется passbolt (volume'ы вида passbolt_gpg_volume). Если использовали официальный docker-compose-ce.yaml без явного name: в compose, имена volume'ов могут отличаться — см. примечание в шаге 5. Проверить можно командой sudo docker volume ls.

#!/bin/bash
set -euo pipefail

BACKUP_DIR=/var/backups/passbolt
TIMESTAMP=$(date +%Y-%m-%d_%H-%M)
mkdir -p "$BACKUP_DIR"

cd ~/passbolt

# Дамп БД через работающий контейнер
docker compose exec -T db mariadb-dump \
  -u passbolt -p"$(grep DB_PASSWORD .env | cut -d= -f2)" \
  passbolt | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"

# Конфиги и npm-данные
tar -czf "$BACKUP_DIR/files_$TIMESTAMP.tar.gz" \
  .env docker-compose.yml npm-data npm-letsencrypt

# Volume'ы Passbolt (gpg + jwt)
docker run --rm \
  -v passbolt_gpg_volume:/gpg:ro \
  -v passbolt_jwt_volume:/jwt:ro \
  -v "$BACKUP_DIR":/backup \
  alpine tar -czf "/backup/volumes_$TIMESTAMP.tar.gz" /gpg /jwt

# Ротация: удалить файлы старше 30 дней
find "$BACKUP_DIR" -type f -mtime +30 -delete

Сохранить как ~/passbolt/backup.sh, дать chmod +x, в root'овый крон (скрипт пишет в /var/backups/, куда обычному юзеру записать нельзя; внутри скрипта команды docker идут без sudo, потому что cron работает из-под root):

sudo crontab -e
# каждый день в 4:00
0 4 * * * /home/youruser/passbolt/backup.sh

Проверить вручную перед тем, как доверять крону:

sudo /home/youruser/passbolt/backup.sh
ls -lah /var/backups/passbolt/

Куда складывать сами бэкапы. Локальная копия на VPS — это половина решения; если умрёт диск или хостер, исчезнут и они. Бэкапы нужно регулярно отгружать наружу — в любое отдельное хранилище: второй VPS, домашний NAS через rsync, S3-совместимое хранилище (Selectel, Yandex Cloud, Backblaze), внешний диск через scp. Что критически важно: бэкапы шифровать перед отправкой наружу (gpg -c или age — самый простой путь). В архивах лежат .env с паролем БД и server-side PGP-ключи; в облаке без шифрования это не бэкап, а вторая копия проблемы.

Проверка восстановления. Раз в полгода — поднять бэкап на отдельной VM или локально в Docker и убедиться, что Passbolt запускается, БД читается, пользователи входят. Бэкап, который ни разу не восстанавливали — это не бэкап, а надежда.

Обновления

Passbolt и MariaDB обновляются через образы. У Passbolt тег latest-ce — это плохо для прода: мажорное обновление может прилететь без предупреждения и сломать миграции. Лучше пинить к конкретной версии и обновляться осознанно:

image: passbolt/passbolt:5.2.0-1-ce  # пример, актуальную версию смотреть на passbolt.com/docs

Чек-лист апдейта:

  1. Сделать бэкап (см. выше).
  2. Прочитать release notes — особенно секцию breaking changes.
  3. sudo docker compose pull && sudo docker compose up -d
  4. Проверить, что вход работает и письма уходят.

Систему обновлять отдельно — unattended-upgrades для security-патчей и ручной apt upgrade раз в пару недель. На single-purpose VPS это малозатратно.

Минимум по SSH

Если ещё не сделано — три быстрые вещи:

# 1. Запретить логин по паролю (только ключи)
sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart ssh

# 2. Поставить fail2ban (банит IP после нескольких неудачных попыток)
sudo apt install fail2ban

# 3. (опционально) Сменить SSH-порт на нестандартный
# — снижает шум от ботов, но не защита; сначала открыть новый порт в ufw,
# потом менять в sshd_config, иначе можно лишиться доступа

Перед PasswordAuthentication no обязательно убедиться, что SSH-ключ работает: открыть второй терминал и зайти по ключу, не закрывая первый. Если второй сеанс не пускает — править на месте, не доводить до залоченного сервера.


Как пользоваться Passbolt — коротко

Подробного разбора UI в этой статье не будет — Passbolt активно развивается, и любые скриншоты протухают за пару релизов. Вместо этого — карта основных сценариев с отсылками к официальной документации.

Браузерное расширение

Расширение для Chrome, Firefox, Edge — основной способ работы с сервисом. Без него Passbolt в веб-интерфейсе не открывается: расширение держит локальный PGP-ключ и расшифровывает пароли на клиенте, сервер этих данных не видит. Ставится один раз на каждом устройстве, импортирует recovery kit + passphrase — после этого браузер сам подставляет пароли в формах входа.

Документация по браузерному расширению →

Мобильные приложения

iOS и Android есть, но это просмотр и копирование паролей, не автозаполнение системного уровня (в отличие от 1Password/Bitwarden). Для семьи в большинстве сценариев этого хватает: открыл, скопировал, вставил. На рабочем телефоне с десятками логинов это может ощущаться медленно.

Passbolt Mobile →

Группы и расшаривание

Главная фича, ради которой всё это и затевалось. Базовая модель:

  • Группы — например, «Семья», «Работа», «Подписки». Пользователь в группе автоматически получает доступ ко всем паролям группы.
  • Пароли с индивидуальным шарингом — конкретный пароль выдаётся конкретным людям с правами read / update / owner.
  • Аудит — на каждом пароле видно, кому и когда был выдан доступ, кто и когда менял. На странице пользователя — все пароли, к которым у него есть доступ. Уволили сотрудника / расстался с подрядчиком — видно ровно то, что нужно ротировать.

Sharing & permissions →

Импорт паролей из других мест

Passbolt умеет импортировать KDBX (KeePass), CSV (универсальный экспорт), форматы 1Password, LastPass, Bitwarden. Это правильный момент наконец-то перенести тот самый Google Docs с паролями и закрыть исходную дыру.

Import / export →

CLI и API

Для автоматизации (CI/CD, выдача паролей сервисам, ротация секретов) есть официальный CLI и REST API. В контексте этой статьи это уже за рамками, но факт того, что они есть — один из аргументов в пользу Passbolt против Vaultwarden, если планируется когда-нибудь дорасти до использования в pipeline'ах.

Готово

На этом цикл закрывается:

  • Пароли больше не лежат в Google Docs.
  • Доступ выдаётся явно, отзывается явно, видно кому что выдано.
  • Семья / команда пользуется одним инструментом на всех устройствах.
  • Сервер — свой, сертификат — настоящий, бэкапы — есть.

Subscribe to zhe700

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe