Перейти к содержанию

Vaultwarden: self-hosted password manager

Личный менеджер паролей на VPS vm-mini, Bitwarden-совместимый.

Дата снимка: 05.08.2026 Хост: VPS vm-mini (Hostkey, 46.17.99.183) Домен: https://vault.alatyr-service.ru Fallback по IP: https://46.17.99.183:8443/ (когда DNS не работает или SNI блокируется)


Быстрая справка

Параметр Значение
Развёртывание Docker Compose, restart: unless-stopped
Image vaultwarden/server:latest
Container name vaultwarden
Внутренний порт 80 (в контейнере)
Публикуется на 127.0.0.1:8222 (только localhost, наружу через nginx)
Данные /opt/vaultwarden/data/ (bind mount → /data)
БД SQLite (db.sqlite3)
Регистрация SIGNUPS_ALLOWED=false (только по invite)
Приглашения INVITATIONS_ALLOWED=true
Push (mobile) PUSH_ENABLED=false
SMTP Не настроен
Log /opt/vaultwarden/data/vaultwarden.log
fail2ban jails vaultwarden, vaultwarden-admin (active)
Бэкапы Ежедневно 03:15, GPG-зашифрованные, 30-дневный ретеншн

Расположение файлов на VPS

/opt/vaultwarden/
├── .env                     # секреты и конфиг (chmod 600)
├── .env.backup.1784919342   # старый бэкап .env (перед изменениями)
├── docker-compose.yml       # определение сервиса
├── data/                    # bind-mounted в контейнер как /data
│   ├── db.sqlite3           # основная БД (заблокирована пока Vaultwarden работает)
│   ├── db.sqlite3-shm       # SQLite shared memory
│   ├── db.sqlite3-wal       # SQLite WAL
│   ├── attachments/         # прикреплённые файлы
│   ├── sends/               # Bitwarden Send (эфемерные ссылки)
│   ├── config.json          # runtime-конфиг (менеджмент через /admin)
│   ├── rsa_key.pem          # приватный ключ для шифрования (КРИТИЧНО!)
│   ├── rsa_key.pub.pem      # публичный ключ
│   └── vaultwarden.log      # лог warn+ (для fail2ban)
└── backups/                 # локальные бэкапы (chmod 700)
    ├── backup.sh            # скрипт бэкапа (chmod 700)
    ├── backup.log           # лог выполнения
    └── vaultwarden-backup-YYYY-MM-DD_HHMMSS.tar.gz.gpg  # шифрованные архивы

docker-compose.yml

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    env_file: .env
    volumes:
      - ./data:/data
    ports:
      # Слушаем ТОЛЬКО на 127.0.0.1 — снаружи Vaultwarden недоступен,
      # весь трафик пойдёт через nginx с TLS
      - "127.0.0.1:8222:80"
    healthcheck:
      test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:80/alive || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 30s

Ключевые решения:

  • 127.0.0.1:8222:80 — порт слушает только на loopback, снаружи Vaultwarden не виден
  • ✅ Весь трафик снаружи идёт через nginx: vault.alatyr-service.ru:443 → 127.0.0.1:8222
  • restart: unless-stopped — автозапуск после reboot
  • image: vaultwarden/server:latest — фиксация версии в TODO

TODO: - [ ] Зафиксировать точную версию image (vaultwarden/server:1.30.5 вместо :latest) — чтобы breaking-change не сломал в момент обновления


.env (только структура, без секретов)

Файл: /opt/vaultwarden/.env (29 строк, chmod 600).

Активные переменные:

Переменная Значение Комментарий
DOMAIN https://vault.alatyr-service.ru Основной URL, обязательно с https://
ADMIN_TOKEN $argon2id$v=19$m=65540,t=3,p=<токен> Argon2-хэш для /admin панели, оригинал offline
SIGNUPS_ALLOWED false Регистрация закрыта
SIGNUPS_VERIFY false Верификация email не требуется
INVITATIONS_ALLOWED true Приглашения разрешены
LOG_LEVEL warn Логировать warn и выше
EXTENDED_LOGGING true Расширенные логи с IP
LOG_FILE /data/vaultwarden.log Файл, который читает fail2ban
PUSH_ENABLED false Push отключён (нет mobile)
TZ Europe/Moscow Таймзона
IP_HEADER X-Real-IP Реальный IP от nginx

Не настроено (TODO):

  • SMTP — нет SMTP_HOST, SMTP_FROM, SMTP_USERNAME, SMTP_PASSWORD → нельзя отправлять email (invites, password reset, 2FA email)

Рекомендация: подключить SMTP через Yandex 360 (уже настроен для alatyr-service.ru):

SMTP_HOST=smtp.yandex.ru
SMTP_FROM=vault@alatyr-service.ru
SMTP_FROM_NAME=Alatyr Vault
SMTP_SECURITY=starttls
SMTP_PORT=587
SMTP_USERNAME=vault@alatyr-service.ru
SMTP_PASSWORD=<application password из Yandex>
SMTP_AUTH_MECHANISM=Plain

TODO: - [ ] Создать почтовый ящик vault@alatyr-service.ru в Yandex 360 - [ ] Сгенерировать application password - [ ] Добавить SMTP-блок в .env, docker compose down && up -d - [ ] Проверить отправку через /admin → Send test email


nginx перед Vaultwarden

Файл: /etc/nginx/sites-enabled/vault.alatyr-service.ru

Server blocks:

  1. HTTP → HTTPS (listen 80): 301 → https://vault.alatyr-service.ru
  2. HTTPS основной (listen 443 ssl + listen 8443 ssl):
  3. proxy_pass http://127.0.0.1:8222
  4. WebSocket ready (proxy_http_version 1.1, Upgrade headers)
  5. X-Real-IP, X-Forwarded-For, X-Forwarded-Proto → Vaultwarden видит реальный IP

Особенности:

  • listen 8443 ssl — резервный доступ по IP https://46.17.99.183:8443/ в обход DNS/SNI (подробнее см. nginx-vps-configs.md)
  • Отдельного access_log нет — всё летит в общий /var/log/nginx/access.log (TODO)

Сертификат:

  • CN: vault.alatyr-service.ru
  • Issuer: Let's Encrypt
  • Расположение: /etc/letsencrypt/live/vault.alatyr-service.ru/{fullchain,privkey}.pem
  • Автообновление через certbot systemd timer

🔴 Проблема: DPI провайдера Inetcom блокирует vault.alatyr-service.ru

✅ 06.08.2026 — блокировка временно снята

Повторные тесты с домашнего Wi-Fi Инетком (без VPN):

PS C:\Users\user> curl.exe -sI --max-time 10 https://vault.alatyr-service.ru
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Thu, 06 Aug 2026 09:53:14 GMT
...

С рабочего ПК через Инетком без AmneziaWG — теперь отвечает нормально. Сайт открывается, TLSv1.3 handshake проходит.

Гипотезы: - Провайдер обновил правила DPI — снял ключевое слово "vault" или конкретный домена - 05.08 было временное срабатывание DPI (какая-то A/B-кампания?) - Изменился DPI-вендор у провайдера

Статус: наблюдаем. AmneziaWG остаётся на случай возврата. Фул-диагностика (задача 4 в TODO) — пропускаем до возврата проблемы.


✅ 07.08.2026 — Healthcheck исправлен (wget → curl)

При фиксации версии выяснилось что healthcheck давно был unhealthy — в образах vaultwarden/server нет wget, есть только curl. Ошибка маскировалась на :latest просто тем что никто не смотрел docker ps внимательно.

Исправлено в /opt/vaultwarden/docker-compose.yml:

# было:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:80/alive || exit 1"]
# стало:
test: ["CMD-SHELL", "curl -fsS http://localhost:80/alive || exit 1"]

Проверка:

sudo docker ps --filter name=vaultwarden --format 'table {{.Names}}\t{{.Status}}'
# vaultwarden   Up About a minute (healthy)


✅ 07.08.2026 — Зафиксирована версия образа

Задача 23 закрыта. В /opt/vaultwarden/docker-compose.yml (VPS 46.17.99.183):

# было:
image: vaultwarden/server:latest
# стало:
image: vaultwarden/server:1.36.0

Резервная копия сохранена в docker-compose.yml.bak. Перекат без downtime — sha256 тот же, что был у :latest.

Как обновлять в будущем: 1. Проверить changelog: https://github.com/dani-garcia/vaultwarden/releases 2. Отредактировать image: в docker-compose.yml на новый тег 3. Обязательно: бэкап ./data перед обновлением 4. docker compose pull && docker compose up -d 5. Проверить docker ps и curl -sI https://vault.alatyr-service.ru


✅ 07.08.2026 — HSTS добавлен в nginx

Задача 21a закрыта. В /etc/nginx/sites-available/vault.alatyr-service.ru (на VPS 46.17.99.183) в HTTPS server-блок добавлена строка:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Расположение: сразу после ssl_dhparam в первом блоке (443/8443).

Проверка:

curl -sI https://vault.alatyr-service.ru | grep -i strict-transport
# Strict-Transport-Security: max-age=31536000; includeSubDomains

Полный набор security headers на vault.alatyr-service.ru: | Заголовок | Источник | |---|---| | Strict-Transport-Security | nginx (07.08.2026) | | X-Frame-Options: SAMEORIGIN | Vaultwarden | | Content-Security-Policy (полный) | Vaultwarden | | Permissions-Policy (27 фичей) | Vaultwarden | | X-Content-Type-Options: nosniff | Vaultwarden | | Referrer-Policy: same-origin | Vaultwarden | | X-Robots-Tag: noindex, nofollow | Vaultwarden | | Cross-Origin-Resource-Policy: same-origin | Vaultwarden |


Симптомы (05.08.2026)

  • С домашней сети (провайдер Inetcom / Инетком, Железнодорожный) при попытке открыть https://vault.alatyr-service.ruERR_CONNECTION_RESET
  • С мобильного интернета (МТС/Билайн/Мегафон) и в VPN (AmneziaWG) — работает нормально
  • curl -v https://vault.alatyr-service.ru с домашнего роутера обрывается на TLS ClientHello с SNI = vault.alatyr-service.ru
  • Тот же IP (176.99.153.164) по другому домену (rag.alatyr-service.ru) — открывается

Диагноз

DPI провайдера Inetcom блокирует TLS-соединения по SNI с частью "vault" в имени. Вероятно:

  • Срабатывает keyword-блок на слова vault, wallet, crypto в SNI
  • Или IP 176.99.153.164 в blacklist'е (но тогда бы rag. тоже не работал) — маловероятно
  • Или конкретно домен vault.alatyr-service.ru попал в какой-то список (RKN / чёрный список Inetcom)

Обходы (по приоритету)

1. AmneziaWG VPN на домашнем клиенте — рабочее решение:

Клиент Amnezia (Windows/Android) → 8443/udp (обфускация) → VPS 46.17.99.183 → nginx :443 → Vaultwarden

DPI видит только UDP-мусор на порт 8443 (обфускация Amnezia), SNI в TLS ему не виден.

2. Fallback по IP без SNI — аварийный вариант:

  • URL: https://46.17.99.183:8443/
  • Браузер скажет "не доверяю сертификату" (CN не совпадает с IP) → продолжить вручную
  • Bitwarden CLI тоже может работать через bw config server https://46.17.99.183:8443 --disable-cert-check
  • Работает потому что SNI в TLS ClientHello будет в IP-виде (или отсутствовать), DPI не найдёт vault в имени

3. Bitwarden mobile app через мобильную сеть — не через Wi-Fi дома:

  • В настройках Bitwarden: Self-hosted → https://vault.alatyr-service.ru
  • На Wi-Fi дома — не работает, на мобильной (МТС и т.д.) — работает

Что не стоит делать

  • Менять домен на vw.alatyr-service.ru или pw.alatyr-service.ru — может временно помочь, но Inetcom вероятно подтянет правило; кроме того сломает все закладки в клиентах Bitwarden
  • ECH (Encrypted Client Hello) — не все nginx билды поддерживают, требует координации с CDN
  • Жалоба провайдеру — Inetcom скорее всего выполняет требования РКН/центрального DPI, снять не сможет

Повторная диагностика когда проблема вернётся

С машины в домашней сети (без VPN):

# 1. DNS вообще работает?
dig vault.alatyr-service.ru +short
# Ожидаем: 176.99.153.164

# 2. TCP доходит?
curl -v --connect-to vault.alatyr-service.ru:443:176.99.153.164:443 https://vault.alatyr-service.ru 2>&1 | head -30
# Если "Connection reset by peer" на TLS handshake → DPI режет SNI

# 3. Тот же IP без SNI?
curl -vk https://176.99.153.164:8443/alive
# Если 200 OK → DPI точно по SNI блокирует домен

# 4. Другие поддомены alatyr-service.ru тоже блокируются?
curl -sI https://rag.alatyr-service.ru | head -3
curl -sI https://alatyr-service.ru | head -3

TODO: - [ ] Проверить какой именно паттерн DPI ловит (curl с разными SNI: test-vault., vlt., v4ult. — что пройдёт?) - [ ] Задокументировать в amneziawg-tunnels.md что VPN теперь обязательный для домашнего доступа к Vaultwarden


Бэкапы

Расписание

Cron root:

15 3 * * * /opt/vaultwarden/backups/backup.sh

Ежедневно в 03:15 локального времени сервера (Europe/Moscow).

Что бэкапится

Скрипт /opt/vaultwarden/backups/backup.sh:

  1. VACUUM INTO — консистентный дамп SQLite (не битый при живой БД)
  2. Копирует в staging-каталог (/tmp/vw_staging_<date>):
  3. db.sqlite3 (свежий дамп)
  4. attachments/ (все прикреплённые файлы)
  5. sends/ (Bitwarden Send)
  6. config.json
  7. rsa_key.pem, rsa_key.pub.pem ← критично, без них БД нельзя расшифровать
  8. .env (в файл env.backup) ← включая ADMIN_TOKEN
  9. docker-compose.yml
  10. tar -czf — сжимает в архив
  11. GPG AES-256 symmetric encryption с паролем из /root/.vw_backup_pass
  12. Удаляет незашифрованный архив и staging
  13. Ретеншн 30 дней — старые .gpg удаляются автоматом
  14. Всё логируется в backup.log

Текущее состояние (05.08.2026)

  • ✅ 13 бэкапов подряд (25.07 — 05.08), ни одного пропуска
  • ✅ Размер стабилен ~23 KB (база растёт линейно)
  • ✅ Все зашифрованы GPG AES-256
  • ✅ Retention 30 дней работает

🔴 Критичные пробелы disaster recovery

Пункт Состояние Риск
GPG-пароль offline (не только на VPS) Записан на бумаге / в сейфе Защищено
Копия бэкапов вне VPS Только локально на VPS Пропадёт VPS → пропадут бэкапы
Тест восстановления Не тестировалось Скрипт может работать, но restore может не сработать

Правило: непроверенный бэкап = отсутствующий бэкап.

План восстановления (drill в Block 4)

Шаг 1 — тест на локальной машине или в отдельной директории на VPS:

# Расшифровать
gpg --decrypt --output vaultwarden-restore.tar.gz \
    /opt/vaultwarden/backups/vaultwarden-backup-2026-08-04_031501.tar.gz.gpg
# (пароль ввести вручную из бумажной копии)

# Распаковать
mkdir /tmp/vw-restore
tar -xzf vaultwarden-restore.tar.gz -C /tmp/vw-restore

# Проверить что БД корректная
sqlite3 /tmp/vw-restore/db.sqlite3 "SELECT COUNT(*) FROM users;"
sqlite3 /tmp/vw-restore/db.sqlite3 "SELECT COUNT(*) FROM ciphers;"

Шаг 2 — если БД корректная, восстановление в новую директорию:

docker compose -f test-compose.yml down
cp -r /tmp/vw-restore/* /opt/vw-test/data/
docker compose -f test-compose.yml up -d
# зайти на test.vault.alatyr-service.ru, проверить логин + записи

TODO: - [ ] Настроить off-site бэкап (варианты): - rsync/rclone на ragserver (/opt/knowledge/vault-backups/) - S3-совместимое хранилище (Yandex Object Storage) - Резервный VPS - [ ] Раз в квартал делать test restore — задокументировать в KB (или cron с автоматической проверкой чек-суммы) - [ ] Установить pass_check.sh — скрипт, который раз в неделю пытается расшифровать последний бэкап и валидирует БД


fail2ban

Активные jails (см. также nginx-vps-configs.md § Состояние безопасности):

Jail Filter Что ловит
vaultwarden Username or password is incorrect. Try again. IP: <ADDR>. Failed login на веб-vault
vaultwarden-admin (тот же лог, специальный regex) Failed попытки на /admin

Оба на 05.08.2026 = 0 events — никто не пытается ломать (см. также подробный разбор в nginx-vps-configs.md).

Логи Vaultwarden при этом пишутся — грепом видим только WARN про dotfile-сканеров (BadStart('.')), которые Vaultwarden сам блочит через PathBuf guard. Реальных попыток брутфорса пароля не было.


Ежедневная работа

Проверить статус

sudo docker compose -f /opt/vaultwarden/docker-compose.yml ps
sudo docker logs vaultwarden --tail 20

Хелсчек

curl -sf http://127.0.0.1:8222/alive && echo "OK"

Логи

sudo tail -f /opt/vaultwarden/data/vaultwarden.log
sudo tail -f /opt/vaultwarden/backups/backup.log

Ручной бэкап

sudo /opt/vaultwarden/backups/backup.sh

Перезапуск

cd /opt/vaultwarden
sudo docker compose down && sudo docker compose up -d

Обновление образа (осторожно с :latest)

cd /opt/vaultwarden
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune -f

Доступ к /admin панели

  • URL: https://vault.alatyr-service.ru/admin
  • Токен: оригинал ADMIN_TOKEN хранится offline (в .env только Argon2-хэш)
  • Что можно: инвайты, blacklist, тестовое письмо, посмотреть/удалить пользователей, изменить runtime-конфиг

TODO (сводка)

🔴 Критично (Block 4 hardening)

  • Off-site бэкап (rsync на ragserver / S3 Yandex / резервный VPS)
  • Test restore — хотя бы один раз выполнить полное восстановление в отдельной директории
  • Скрипт weekly-check: раз в неделю расшифровка последнего бэкапа + sqlite3 pragma integrity_check
  • Проверить паттерн DPI Inetcom — точно по SNI vault или есть другие триггеры?

🟡 Улучшения

  • Настроить SMTP через Yandex 360 для invitations, password reset, 2FA email
  • Зафиксировать точную версию image вместо :latest
  • Добавить access_log для vault-сайта в nginx (сейчас смешано с общим логом)
  • Пересмотреть LOG_LEVEL=warn → возможно info для лучшей аудитности

🟢 Далёкий horizon

  • Мигрировать с SQLite на PostgreSQL (если пользователей > 10)
  • Two-factor authentication для админки
  • Sync с мобильными клиентами (тут нужен push — Vaultwarden поддерживает Bitwarden's push, но требует настройки)

История изменений

Дата Что
2026-08-05 Первая версия. Задокументированы docker-compose, .env, nginx, бэкапы (ежедневный GPG-скрипт), fail2ban. Найдены пробелы: off-site бэкапов нет, test restore не делали, SMTP не настроен.
2026-08-05 Добавлен раздел про DPI провайдера Inetcom: vault.alatyr-service.ru блокируется по SNI с домашнего Wi-Fi (ERR_CONNECTION_RESET). Обход — AmneziaWG VPN или fallback по IP https://46.17.99.183:8443/. Мобильные операторы не блокируют.